fix: cache admin permissions in redis
This commit is contained in:
@@ -10,13 +10,18 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"hotgo/internal/consts"
|
"hotgo/internal/consts"
|
||||||
"hotgo/internal/dao"
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/cache"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/casbin/casbin/v2"
|
"github.com/casbin/casbin/v2"
|
||||||
"github.com/casbin/casbin/v2/model"
|
"github.com/casbin/casbin/v2/model"
|
||||||
|
casbinutil "github.com/casbin/casbin/v2/util"
|
||||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||||
"github.com/gogf/gf/v2/container/gvar"
|
"github.com/gogf/gf/v2/container/gvar"
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
"github.com/gogf/gf/v2/os/gfile"
|
"github.com/gogf/gf/v2/os/gfile"
|
||||||
"github.com/gogf/gf/v2/os/gres"
|
"github.com/gogf/gf/v2/os/gres"
|
||||||
@@ -33,6 +38,14 @@ const (
|
|||||||
|
|
||||||
var Enforcer *casbin.Enforcer
|
var Enforcer *casbin.Enforcer
|
||||||
|
|
||||||
|
const permissionCacheKey = "admin:role:permissions"
|
||||||
|
|
||||||
|
type PermissionPolicy struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Perm string `json:"perm"`
|
||||||
|
ActRegexp string `json:"actRegexp"`
|
||||||
|
}
|
||||||
|
|
||||||
// InitEnforcer 初始化
|
// InitEnforcer 初始化
|
||||||
func InitEnforcer(ctx context.Context) {
|
func InitEnforcer(ctx context.Context) {
|
||||||
var (
|
var (
|
||||||
@@ -69,6 +82,9 @@ func InitEnforcer(ctx context.Context) {
|
|||||||
g.Log().Panicf(ctx, "casbin NewEnforcer err:%v", err)
|
g.Log().Panicf(ctx, "casbin NewEnforcer err:%v", err)
|
||||||
}
|
}
|
||||||
loadPermissions(ctx)
|
loadPermissions(ctx)
|
||||||
|
if err = RefreshPermissionCache(ctx); err != nil {
|
||||||
|
g.Log().Panicf(ctx, "casbin RefreshPermissionCache err:%v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDbLink 获取数据库链接
|
// GetDbLink 获取数据库链接
|
||||||
@@ -91,15 +107,13 @@ func getDbLink(ctx context.Context) *gvar.Var {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPermissions(ctx context.Context) {
|
func queryPermissionPolicies(ctx context.Context) (policies []*PermissionPolicy, err error) {
|
||||||
type Policy struct {
|
type RolePermission struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Permissions string `json:"permissions"`
|
Permissions string `json:"permissions"`
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
rules [][]string
|
records []*RolePermission
|
||||||
polices []*Policy
|
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
//别名拼接 r.key m.permissions
|
//别名拼接 r.key m.permissions
|
||||||
q := func(alias string, column string) string {
|
q := func(alias string, column string) string {
|
||||||
@@ -113,23 +127,43 @@ func loadPermissions(ctx context.Context) {
|
|||||||
Where(q("m", dao.AdminMenu.Columns().Status), consts.StatusEnabled).
|
Where(q("m", dao.AdminMenu.Columns().Status), consts.StatusEnabled).
|
||||||
WhereNot(q("m", dao.AdminMenu.Columns().Permissions), "").
|
WhereNot(q("m", dao.AdminMenu.Columns().Permissions), "").
|
||||||
WhereNot(q("r", dao.AdminRole.Columns().Key), consts.SuperRoleKey).
|
WhereNot(q("r", dao.AdminRole.Columns().Key), consts.SuperRoleKey).
|
||||||
Scan(&polices)
|
Scan(&records)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range records {
|
||||||
|
if strings.Contains(record.Permissions, ",") {
|
||||||
|
lst := strings.Split(record.Permissions, ",")
|
||||||
|
for _, permission := range lst {
|
||||||
|
permission = strings.TrimSpace(permission)
|
||||||
|
if permission == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
policies = append(policies, &PermissionPolicy{Key: record.Key, Perm: permission, ActRegexp: ActionAll})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
permission := strings.TrimSpace(record.Permissions)
|
||||||
|
if permission == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
policies = append(policies, &PermissionPolicy{Key: record.Key, Perm: permission, ActRegexp: ActionAll})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPermissions(ctx context.Context) {
|
||||||
|
policies, err := queryPermissionPolicies(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Fatalf(ctx, "loadPermissions Scan err:%v", err)
|
g.Log().Fatalf(ctx, "loadPermissions Scan err:%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, policy := range polices {
|
rules := make([][]string, 0, len(policies))
|
||||||
if strings.Contains(policy.Permissions, ",") {
|
for _, policy := range policies {
|
||||||
lst := strings.Split(policy.Permissions, ",")
|
rules = append(rules, []string{policy.Key, policy.Perm, policy.ActRegexp})
|
||||||
for _, permissions := range lst {
|
|
||||||
rules = append(rules, []string{policy.Key, permissions, ActionAll})
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
rules = append(rules, []string{policy.Key, policy.Permissions, ActionAll})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = Enforcer.AddPolicies(rules); err != nil {
|
if _, err = Enforcer.AddPolicies(rules); err != nil {
|
||||||
g.Log().Fatalf(ctx, "loadPermissions AddPolicies err:%v", err)
|
g.Log().Fatalf(ctx, "loadPermissions AddPolicies err:%v", err)
|
||||||
return
|
return
|
||||||
@@ -160,5 +194,65 @@ func Refresh(ctx context.Context) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
loadPermissions(ctx)
|
loadPermissions(ctx)
|
||||||
|
return RefreshPermissionCache(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RefreshPermissionCache(ctx context.Context) (err error) {
|
||||||
|
policies, err := queryPermissionPolicies(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rolePolicies := make(map[string][]*PermissionPolicy)
|
||||||
|
for _, policy := range policies {
|
||||||
|
rolePolicies[policy.Key] = append(rolePolicies[policy.Key], policy)
|
||||||
|
}
|
||||||
|
data, err := gjson.Encode(rolePolicies)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cache.Instance().Set(ctx, permissionCacheKey, string(data), 30*24*time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cachedPermissionPolicies(ctx context.Context) (rolePolicies map[string][]*PermissionPolicy, err error) {
|
||||||
|
value, err := cache.Instance().Get(ctx, permissionCacheKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if value.IsNil() {
|
||||||
|
if err = RefreshPermissionCache(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
value, err = cache.Instance().Get(ctx, permissionCacheKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rolePolicies = make(map[string][]*PermissionPolicy)
|
||||||
|
if value.IsNil() {
|
||||||
|
return rolePolicies, nil
|
||||||
|
}
|
||||||
|
if err = gjson.DecodeTo([]byte(value.String()), &rolePolicies); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Verify(ctx context.Context, roleKey, path, method string) (bool, error) {
|
||||||
|
rolePolicies, err := cachedPermissionPolicies(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
for _, policy := range rolePolicies[roleKey] {
|
||||||
|
if !casbinutil.KeyMatch(path, policy.Perm) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ok, err := regexp.MatchString(policy.ActRegexp, method)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,9 +53,11 @@ func (s *sAdminMenu) Delete(ctx context.Context, in *adminin.MenuDeleteInp) (err
|
|||||||
if !exist.IsEmpty() {
|
if !exist.IsEmpty() {
|
||||||
return gerror.New("请先删除该菜单下的所有菜单!")
|
return gerror.New("请先删除该菜单下的所有菜单!")
|
||||||
}
|
}
|
||||||
_, err = dao.AdminMenu.Ctx(ctx).Where(dao.AdminMenu.Columns().Id, in.Id).Delete()
|
if _, err = dao.AdminMenu.Ctx(ctx).Where(dao.AdminMenu.Columns().Id, in.Id).Delete(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
return casbin.Refresh(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// VerifyUnique 验证菜单唯一属性
|
// VerifyUnique 验证菜单唯一属性
|
||||||
func (s *sAdminMenu) VerifyUnique(ctx context.Context, in *adminin.VerifyUniqueInp) (err error) {
|
func (s *sAdminMenu) VerifyUnique(ctx context.Context, in *adminin.VerifyUniqueInp) (err error) {
|
||||||
@@ -104,7 +106,7 @@ func (s *sAdminMenu) Edit(ctx context.Context, in *adminin.MenuEditInp) (err err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
if err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||||
in.Pid, in.Level, in.Tree, err = hgorm.AutoUpdateTree(ctx, &dao.AdminMenu, in.Id, in.Pid)
|
in.Pid, in.Level, in.Tree, err = hgorm.AutoUpdateTree(ctx, &dao.AdminMenu, in.Id, in.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -121,8 +123,11 @@ func (s *sAdminMenu) Edit(ctx context.Context, in *adminin.MenuEditInp) (err err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
return casbin.Refresh(ctx)
|
return casbin.Refresh(ctx)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List 获取菜单列表
|
// List 获取菜单列表
|
||||||
|
|||||||
@@ -82,9 +82,9 @@ func (s *sAdminRole) Verify(ctx context.Context, path, method string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, err := casbin.Enforcer.Enforce(user.RoleKey, path, method)
|
ok, err := casbin.Verify(ctx, user.RoleKey, path, method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Infof(ctx, "admin Verify Enforce err:%+v", err)
|
g.Log().Infof(ctx, "admin Verify cache err:%+v", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return ok
|
return ok
|
||||||
@@ -221,15 +221,18 @@ func (s *sAdminRole) Edit(ctx context.Context, in *adminin.RoleEditInp) (err err
|
|||||||
// 如果当前角色有子级,更新子级tree关系树
|
// 如果当前角色有子级,更新子级tree关系树
|
||||||
return updateRoleChildrenTree(ctx, in.Id, in.Level, in.Tree)
|
return updateRoleChildrenTree(ctx, in.Id, in.Level, in.Tree)
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
return casbin.Refresh(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// 新增
|
// 新增
|
||||||
if _, err = dao.AdminRole.Ctx(ctx).Fields(adminin.RoleInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil {
|
if _, err = dao.AdminRole.Ctx(ctx).Fields(adminin.RoleInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil {
|
||||||
err = gerror.Wrap(err, consts.ErrorORM)
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return casbin.Refresh(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateRoleChildrenTree(ctx context.Context, _id int64, _level int, _tree string) (err error) {
|
func updateRoleChildrenTree(ctx context.Context, _id int64, _level int, _tree string) (err error) {
|
||||||
@@ -288,9 +291,10 @@ func (s *sAdminRole) Delete(ctx context.Context, in *adminin.RoleDeleteInp) (err
|
|||||||
|
|
||||||
if _, err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Delete(); err != nil {
|
if _, err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Delete(); err != nil {
|
||||||
err = gerror.Wrap(err, consts.ErrorORM)
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
return casbin.Refresh(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *sAdminRole) DataScopeSelect() (res form.Selects) {
|
func (s *sAdminRole) DataScopeSelect() (res form.Selects) {
|
||||||
for k, v := range consts.RoleDataNameMap {
|
for k, v := range consts.RoleDataNameMap {
|
||||||
|
|||||||
Reference in New Issue
Block a user