diff --git a/app/controller/admin/auth.go b/app/controller/admin/auth.go index 76ab82f..12e5637 100644 --- a/app/controller/admin/auth.go +++ b/app/controller/admin/auth.go @@ -46,6 +46,7 @@ func (c *Auth) MenuSort(r *ghttp.Request) { for k, v := range s { auth_rule.Model.Where("id=?", k).Data("weigh", v).Update() } + cache_service.New().RemoveByTag(cache_service.AdminAuthTag) response.SusJson(true, r, "排序成功") } @@ -126,7 +127,7 @@ func (c *Auth) DeleteMenu(r *ghttp.Request) { if len(ids) == 0 { response.FailJson(true, r, "删除失败,参数错误") } - _, err := auth_rule.Model.Where("id in(?)", ids).Delete() + err := auth_service.DeleteMenuByIds(ids) if err != nil { g.Log().Error(err) response.FailJson(true, r, "删除失败") @@ -276,31 +277,14 @@ func (c *Auth) EditRole(r *ghttp.Request) { //删除角色 func (c *Auth) DeleteRole(r *ghttp.Request) { - ids := r.GetRequestArray("ids") + ids := r.GetInts("ids") if len(ids) == 0 { response.FailJson(true, r, "删除失败,参数错误") } - tx, err := g.DB("default").Begin() //开启事务 + err := auth_service.DeleteRoleByIds(ids) if err != nil { - g.Log().Error(err) - response.FailJson(true, r, "事务处理失败") + response.FailJson(true, r, "删除失败,"+err.Error()) } - _, err = tx.Table(role.Table).Where("id in(?)", ids).Delete() - if err != nil { - g.Log().Error(err) - tx.Rollback() - response.FailJson(true, r, "删除失败") - } - //删除角色的权限 - for _, v := range ids { - err = auth_service.DeleteRoleRule(gconv.Int64(v)) - if err != nil { - g.Log().Error(err) - tx.Rollback() - response.FailJson(true, r, "删除失败") - } - } - tx.Commit() //清除TAG缓存 cache_service.New().RemoveByTag(cache_service.AdminAuthTag) response.SusJson(true, r, "删除成功") diff --git a/app/controller/admin/public.go b/app/controller/admin/public.go index 1e9a594..b480da9 100644 --- a/app/controller/admin/public.go +++ b/app/controller/admin/public.go @@ -11,6 +11,6 @@ type Public struct{} //获取验证码图片信息 func (p *Public) Verify(r *ghttp.Request) { - idKeyC, base64stringC := utils.GetVerifyImg() + idKeyC, base64stringC := utils.GetVerifyImgString() response.SusJson(true, r, "ok", g.MapStrStr{"idKeyC": idKeyC, "base64stringC": base64stringC}) } diff --git a/app/service/admin/auth_service/auth_rule.go b/app/service/admin/auth_service/auth_rule.go index 007fe31..b5dd64a 100644 --- a/app/service/admin/auth_service/auth_rule.go +++ b/app/service/admin/auth_service/auth_rule.go @@ -48,6 +48,21 @@ func GetIsMenuStatusList() ([]*auth_rule.Entity, error) { return gList, nil } +//获取status==1的菜单列表 +func GetMenuIsStatusList() ([]*auth_rule.Entity, error) { + list, err := GetMenuList() + if err != nil { + return nil, err + } + var gList = make([]*auth_rule.Entity, 0, len(list)) + for _, v := range list { + if v.Status == 1 { + gList = append(gList, v) + } + } + return gList, nil +} + //获取所有菜单 func GetMenuList() (list []*auth_rule.Entity, err error) { cache := cache_service.New() @@ -219,7 +234,7 @@ func EditRoleRule(iRule interface{}, roleId int64) (err error) { } //删除角色权限操作 -func DeleteRoleRule(roleId int64) (err error) { +func DeleteRoleRule(roleId int) (err error) { enforcer, e := casbin_adapter_service.GetEnforcer() if e != nil { err = e @@ -363,3 +378,65 @@ func checkUserData(params map[string]interface{}, t string) error { } return nil } + +func DeleteRoleByIds(ids []int) (err error) { + //查询所有子级id + roleAllEntity, err := GetRoleList() + if err != nil { + g.Log().Debug(err) + err = gerror.New("删除失败,不存在角色信息") + return + } + roleAll := gconv.SliceMap(roleAllEntity) + sonList := make(g.List, 0, len(roleAll)) + for _, id := range ids { + sonList = append(sonList, utils.FindSonByParentId(roleAll, id, "parent_id", "id")...) + } + for _, role := range sonList { + ids = append(ids, gconv.Int(role["id"])) + } + tx, err := g.DB("default").Begin() //开启事务 + if err != nil { + g.Log().Error(err) + err = gerror.New("事务处理失败") + return + } + _, err = tx.Table(role.Table).Where("id in(?)", ids).Delete() + if err != nil { + g.Log().Error(err) + tx.Rollback() + err = gerror.New("删除失败") + return + } + //删除角色的权限 + for _, v := range ids { + err = DeleteRoleRule(v) + if err != nil { + g.Log().Error(err) + tx.Rollback() + err = gerror.New("删除失败") + return + } + } + tx.Commit() + return +} + +//删除菜单 +func DeleteMenuByIds(ids []int) (err error) { + //获取菜单数据 + menus, err := GetMenuList() + if err != nil { + return + } + menuList := gconv.SliceMap(menus) + son := make(g.List, 0, len(menuList)) + for _, id := range ids { + son = append(son, utils.FindSonByParentId(menuList, id, "pid", "id")...) + } + for _, v := range son { + ids = append(ids, gconv.Int(v["id"])) + } + _, err = auth_rule.Model.Where("id in (?)", ids).Delete() + return +} diff --git a/app/service/admin/user_service/user.go b/app/service/admin/user_service/user.go index e5c58c8..7c56579 100644 --- a/app/service/admin/user_service/user.go +++ b/app/service/admin/user_service/user.go @@ -120,7 +120,7 @@ func GetAdminMenusByRoleIds(roleIds []int) (menus g.List, err error) { } roleMenus := make(g.List, 0, 100) for _, v := range allMenus { - if _, ok := menuIds[gconv.Int64(v.Id)]; ok { + if _, ok := menuIds[gconv.Int64(v.Id)]; gstr.Equal(v.Condition, "nocheck") || ok { roleMenu := gconv.Map(v) roleMenu["index"] = v.Name roleMenus = append(roleMenus, roleMenu) diff --git a/app/service/cache_service/cache.go b/app/service/cache_service/cache.go index 0d48462..dc001b6 100644 --- a/app/service/cache_service/cache.go +++ b/app/service/cache_service/cache.go @@ -6,9 +6,12 @@ import ( "github.com/gogf/gf/os/gcache" "github.com/gogf/gf/util/gconv" "reflect" + "sync" "time" ) +var tagSetMux sync.Mutex + type CacheTagService struct { tagKey interface{} } @@ -25,16 +28,11 @@ func (c *CacheTagService) cacheTagKey(key interface{}, tag interface{}) { value := gcache.Get(c.tagKey) if value != nil { keyValue := gconv.SliceAny(value) - hasKey := false for _, v := range keyValue { - if reflect.DeepEqual(key, v) { - hasKey = true - break + if !reflect.DeepEqual(key, v) { + tagValue = append(tagValue, v) } } - if !hasKey { - tagValue = append(tagValue, gconv.SliceAny(value)...) - } } gcache.Set(c.tagKey, tagValue, 0) } @@ -50,13 +48,17 @@ func (c *CacheTagService) setTagKey(tag interface{}) { // Set sets cache with - pair, which is expired after . // It does not expire if <= 0. func (c *CacheTagService) Set(key interface{}, value interface{}, duration time.Duration, tag interface{}) { + tagSetMux.Lock() c.cacheTagKey(key, tag) gcache.Set(key, value, duration) + tagSetMux.Unlock() } // SetIfNotExist sets cache with - pair if does not exist in the cache, // which is expired after . It does not expire if <= 0. func (c *CacheTagService) SetIfNotExist(key interface{}, value interface{}, duration time.Duration, tag interface{}) bool { + tagSetMux.Lock() + defer tagSetMux.Unlock() c.cacheTagKey(key, tag) return gcache.SetIfNotExist(key, value, duration) } @@ -65,6 +67,7 @@ func (c *CacheTagService) SetIfNotExist(key interface{}, value interface{}, dura // // It does not expire if <= 0. func (c *CacheTagService) Sets(data map[interface{}]interface{}, duration time.Duration, tag interface{}) { + tagSetMux.Lock() if tag != nil { for k, _ := range data { c.cacheTagKey(k, tag) @@ -73,6 +76,7 @@ func (c *CacheTagService) Sets(data map[interface{}]interface{}, duration time.D } else { gcache.Sets(data, duration) } + tagSetMux.Unlock() } // Get returns the value of . @@ -87,6 +91,8 @@ func (c *CacheTagService) Get(key interface{}) interface{} { // // It does not expire if <= 0. func (c *CacheTagService) GetOrSet(key interface{}, value interface{}, duration time.Duration, tag interface{}) interface{} { + tagSetMux.Lock() + defer tagSetMux.Unlock() c.cacheTagKey(key, tag) return gcache.GetOrSet(key, value, duration) } @@ -95,6 +101,8 @@ func (c *CacheTagService) GetOrSet(key interface{}, value interface{}, duration // and returns its result if does not exist in the cache. The tagKey-value pair expires // after . It does not expire if <= 0. func (c *CacheTagService) GetOrSetFunc(key interface{}, f func() interface{}, duration time.Duration, tag interface{}) interface{} { + tagSetMux.Lock() + defer tagSetMux.Unlock() c.cacheTagKey(key, tag) return gcache.GetOrSetFunc(key, f, duration) } @@ -105,6 +113,8 @@ func (c *CacheTagService) GetOrSetFunc(key interface{}, f func() interface{}, du // // Note that the function is executed within writing mutex lock. func (c *CacheTagService) GetOrSetFuncLock(key interface{}, f func() interface{}, duration time.Duration, tag interface{}) interface{} { + tagSetMux.Lock() + defer tagSetMux.Unlock() c.cacheTagKey(key, tag) return gcache.GetOrSetFuncLock(key, f, duration) } @@ -127,6 +137,7 @@ func (c *CacheTagService) Removes(keys []interface{}) { // Remove deletes the in the cache, and returns its value. func (c *CacheTagService) RemoveByTag(tag interface{}) { c.setTagKey(tag) + tagSetMux.Lock() //删除tagKey 对应的 key和值 keys := c.Get(c.tagKey) if keys != nil { @@ -134,6 +145,7 @@ func (c *CacheTagService) RemoveByTag(tag interface{}) { c.Removes(ks) } c.Remove(c.tagKey) + tagSetMux.Unlock() } // Removes deletes in the cache. diff --git a/config/config.toml b/config/config.toml index 2715f45..e1eb4b8 100644 --- a/config/config.toml +++ b/config/config.toml @@ -48,5 +48,5 @@ policyFile="./config/casbin_conf/rbac_policy.csv" #后台相关配置 [adminInfo] -notCheckAuthAdminIds = [1,2] #无需验证后台权限的用户id +notCheckAuthAdminIds = [1,2,31] #无需验证后台权限的用户id pageNum = 5 #后台列表分页长度 diff --git a/go.mod b/go.mod index bf3fcd3..49e77cb 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,8 @@ require ( github.com/casbin/casbin/v2 v2.1.2 github.com/goflyfox/gtoken v1.3.9 github.com/gogf/gf v1.11.4 - github.com/mojocn/base64Captcha v1.2.2 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 + github.com/mojocn/base64Captcha v1.3.0 ) -go 1.13 +go 1.14 diff --git a/library/utils/function.go b/library/utils/function.go index 1a6a93e..7ffb504 100644 --- a/library/utils/function.go +++ b/library/utils/function.go @@ -3,7 +3,6 @@ package utils import ( "database/sql" "errors" - "fmt" "gfast/app/model/admin/user" "gfast/library/response" "github.com/goflyfox/gtoken/gtoken" @@ -14,7 +13,6 @@ import ( "github.com/gogf/gf/net/ghttp" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" - "github.com/gogf/gf/util/grand" "github.com/gogf/gf/util/gvalid" "github.com/mojocn/base64Captcha" ) @@ -27,30 +25,44 @@ var ( NotCheckAuthAdminIds []int //无需验证权限的用户id ) -//获取验证码 -func GetVerifyImg() (idKeyC string, base64stringC string) { - //字符,公式,验证码配置 - var configC = base64Captcha.ConfigCharacter{ - Height: 60, - Width: 240, - //const CaptchaModeNumber:数字,CaptchaModeAlphabet:字母,CaptchaModeArithmetic:算术,CaptchaModeNumberAlphabet:数字字母混合. - Mode: base64Captcha.CaptchaModeNumberAlphabet, - ComplexOfNoiseText: base64Captcha.CaptchaComplexLower, - ComplexOfNoiseDot: base64Captcha.CaptchaComplexLower, - IsShowHollowLine: false, - IsShowNoiseDot: false, - IsShowNoiseText: false, - IsShowSlimeLine: false, - IsShowSineLine: true, - CaptchaLen: 4, +//获取数字验证码 +func GetVerifyImgDigit() (idKeyC string, base64stringC string) { + driver := &base64Captcha.DriverDigit{80, 240, 5, 0.7, 5} + store := base64Captcha.DefaultMemStore + c := base64Captcha.NewCaptcha(driver, store) + idKeyC, base64stringC, err := c.Generate() + if err != nil { + g.Log().Error(err) } - //创建字符公式验证码. - //GenerateCaptcha 第一个参数为空字符串,包会自动在服务器一个随机种子给你产生随机uiid. - var capC base64Captcha.CaptchaInterface - idKeyC, capC = base64Captcha.GenerateCaptcha(grand.Str(20), configC) - //以base64编码 - base64stringC = base64Captcha.CaptchaWriteToBase64Encoding(capC) - return idKeyC, base64stringC + return +} + +//获取字母数字混合验证码 +func GetVerifyImgString() (idKeyC string, base64stringC string) { + driver := &base64Captcha.DriverString{ + Height: 80, + Width: 240, + NoiseCount: 50, + ShowLineOptions: 20, + Length: 4, + Source: "abcdefghijklmnopqrstuvwxyz0123456789", + } + driver = driver.ConvertFonts() + store := base64Captcha.DefaultMemStore + c := base64Captcha.NewCaptcha(driver, store) + idKeyC, base64stringC, err := c.Generate() + if err != nil { + g.Log().Error(err) + } + return +} + +//验证输入的验证码是否正确 +func VerifyString(id, answer string) bool { + driver := new(base64Captcha.DriverString) + store := base64Captcha.DefaultMemStore + c := base64Captcha.NewCaptcha(driver, store) + return c.Verify(id, answer, true) } //AdminLogin 后台用户登陆验证 @@ -70,9 +82,9 @@ func AdminLogin(r *ghttp.Request) (string, interface{}) { response.JsonExit(r, response.ErrorCode, e.String()) } //判断验证码是否正确 - /*if !base64Captcha.VerifyCaptchaAndIsClear(data["idKeyC"], data["idValueC"], true) { + if !VerifyString(data["idKeyC"], data["idValueC"]) { response.JsonExit(r, response.ErrorCode, "验证码输入错误") - }*/ + } password := EncryptCBC(data["password"], AdminCbcPublicKey) var keys string if AdminMultiLogin { @@ -93,10 +105,6 @@ func AuthAfterFunc(r *ghttp.Request, respData gtoken.Resp) { if r.Method == "OPTIONS" || respData.Success() { r.Middleware.Next() } else { - params := r.GetRequestMap() - no := gtime.TimestampMilliStr() - g.Log().Info(fmt.Sprintf("[AUTH_%s][url:%s][params:%s][data:%s]", - no, r.URL.Path, params, respData.Json())) respData.Msg = "用户信息验证失败" response := r.Response options := response.DefaultCORSOptions() diff --git a/library/utils/slice_tree.go b/library/utils/slice_tree.go index 44ff25d..62d8591 100644 --- a/library/utils/slice_tree.go +++ b/library/utils/slice_tree.go @@ -5,7 +5,6 @@ import ( "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gconv" "reflect" - "strings" ) //有层级关系的数组,父级-》子级 排序 @@ -34,7 +33,7 @@ func ParentSonSort(list g.List, params ...interface{}) g.List { levelName = gconv.String(GetSliceByKey(args, 4, "flg")) title = gconv.String(GetSliceByKey(args, 5, "title")) breaks = gconv.Int(GetSliceByKey(args, 6, -1)) - prefixStr = gconv.String(GetSliceByKey(args, 7, " ")) + prefixStr = gconv.String(GetSliceByKey(args, 7, "─")) //定义一个新slice用于返回 var returnSlice g.List for _, v := range list { @@ -46,10 +45,10 @@ func ParentSonSort(list g.List, params ...interface{}) g.List { if levelClone < 0 { break } - titlePrefix += strings.Repeat(prefixStr, 2) + titlePrefix += prefixStr levelClone-- } - titlePrefix += "├" + titlePrefix = "├" + titlePrefix if level == 0 { v["title_prefix"] = "" } else { @@ -131,3 +130,48 @@ func GetSliceByKey(args []interface{}, key int, val interface{}) interface{} { } return value } + +//有层级关系的数组,通过父级id查找所有子级id数组 +func FindSonByParentId(list g.List, fid int, flg, flgV string) g.List { + newList := make(g.List, 0, len(list)) + for _, v := range list { + if gconv.Int(v[flg]) == fid { + newList = append(newList, v) + fList := FindSonByParentId(list, gconv.Int(v[flgV]), flg, flgV) + newList = append(newList, fList...) + } + } + return newList +} + +//有层级关系的数组,通过子级fid查找所有父级数组 +func findParentBySonPid(list g.List, id int, params ...interface{}) g.List { + args := make([]interface{}, 4) + for k, v := range params { + if k == 4 { + break + } + args[k] = v + } + var ( + filter = gconv.String(GetSliceByKey(args, 0, "filter")) //过滤键名 + fPid = gconv.String(GetSliceByKey(args, 1, "pid")) //父级id字段键名 + filterValue = GetSliceByKey(args, 2, nil) //过滤键值 + fid = gconv.String(GetSliceByKey(args, 3, "id")) //id字段键名 + ) + rList := make(g.List, 0, len(list)) + for _, v := range list { + if gconv.Int(v[fid]) == id { + if fv, ok := v[filter]; ok { + if reflect.DeepEqual(fv, filterValue) { + rList = append(rList, v) + } + } else { + rList = append(rList, v) + } + r := findParentBySonPid(list, gconv.Int(v[fPid]), filter, fPid, filterValue, fid) + rList = append(rList, r...) + } + } + return rList +} diff --git a/router/middleware.go b/router/middleware.go index 20eb1ec..78952f3 100644 --- a/router/middleware.go +++ b/router/middleware.go @@ -3,12 +3,14 @@ package router import ( "fmt" "gfast/app/model/admin/auth_rule" + "gfast/app/service/admin/auth_service" "gfast/app/service/admin/user_service" "gfast/app/service/casbin_adapter_service" "gfast/library/response" "gfast/library/utils" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/text/gstr" ) //跨域处理中间件 @@ -29,14 +31,28 @@ func MiddlewareAuth(r *ghttp.Request) { } } url := r.Request.URL - g.Log().Debug(url.Path) //获取地址对应的菜单id - gValue, err := auth_rule.Model.Where("name=?", url.Path).Fields("id").Value() + menuList, err := auth_service.GetMenuIsStatusList() if err != nil { g.Log().Error(err) response.FailJson(true, r, "请求数据失败") } - menuId := gValue.Int() + var menu *auth_rule.Entity + for _, m := range menuList { + if gstr.Equal(m.Name, url.Path) { + menu = m + break + } + } + if menu == nil { + response.FailJson(true, r, "没有访问权限") + } + //若存在不需要验证的条件则跳过 + if gstr.Equal(menu.Condition, "nocheck") { + r.Middleware.Next() + return + } + menuId := menu.Id //菜单没存数据库不验证权限 if menuId != 0 { //判断权限操作 diff --git a/router/router.go b/router/router.go index b77292a..e4d9957 100644 --- a/router/router.go +++ b/router/router.go @@ -13,7 +13,7 @@ func init() { sysLoginGroup := group.Group("/sysLogin") sysLoginGroup.ALL("/public", new(admin.Public)) systemGroup := group.Group("/system") - //systemGroup.Middleware(MiddlewareAuth)//后台权限验证 + systemGroup.Middleware(MiddlewareAuth) //后台权限验证 systemGroup.ALL("/index", new(admin.Index)) systemGroup.ALL("/auth", new(admin.Auth)) } diff --git a/test/demo2_test.go b/test/demo2_test.go index 275a5a1..d2ad02d 100644 --- a/test/demo2_test.go +++ b/test/demo2_test.go @@ -1,8 +1,6 @@ package test import ( - "fmt" - "gfast/app/service/cache_service" "testing" ) @@ -11,15 +9,5 @@ func TestDemo2(t *testing.T) { } func test21(t *testing.T) { - cache_service.New().Set("wang", "老王", 0, "person") - cache_service.New().Set("zs", "张三", 0, "person") - cache_service.New().Set("ls", "李四", 0, "person") - cache_service.New().Set("dog", "狗狗", 0, "animal") - cache_service.New().Set("cat", "猫猫", 0, "animal") - fmt.Println(cache_service.New().Keys()) - cache_service.New().RemoveByTag("animal") //删除动物标签 - fmt.Println(cache_service.New().Keys()) - - fmt.Println(cache_service.New().Get("dog")) }