From 3f33f7a825225719b78bfd606e96684e268b452a Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Tue, 29 Aug 2023 16:11:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AD=E9=97=B4=E7=94=A8=E6=88=B7=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E6=8E=88=E6=9D=83=E8=AE=A4=E8=AF=81=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/midware/authorize.go | 187 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 lib/midware/authorize.go diff --git a/lib/midware/authorize.go b/lib/midware/authorize.go new file mode 100644 index 00000000..4000d57f --- /dev/null +++ b/lib/midware/authorize.go @@ -0,0 +1,187 @@ +package midware + +import ( + "context" + "fmt" + "net/http" + + "ems.agt/lib/core/cache" + "ems.agt/lib/core/utils/ctx" + "ems.agt/lib/core/vo" + "ems.agt/lib/core/vo/result" + "ems.agt/lib/dborm" +) + +// Authorize 用户身份授权认证校验 +// +// 只需含有其中角色 "hasRoles": {"xxx"}, +// +// 只需含有其中权限 "hasPerms": {"xxx"}, +// +// 同时匹配其中角色 "matchRoles": {"xxx"}, +// +// 同时匹配其中权限 "matchPerms": {"xxx"}, +func Authorize(options map[string][]string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.RequestURI == "/api/rest/securityManagement/v1/oauth/token" { + // 继续处理请求 + next.ServeHTTP(w, r) + return + } + // 获取请求头标识信息 + accessToken := r.Header.Get("AccessToken") + if accessToken == "" { + ctx.JSON(w, 401, result.CodeMsg(401, "token error 无效身份授权")) + return + } + + // 验证令牌 == 这里直接查数据库session + if !dborm.XormExistValidToken(accessToken, 0) { + ctx.JSON(w, 401, result.CodeMsg(401, "valid error 无效身份授权")) + return + } + se, err := dborm.XormUpdateSessionShakeTime(accessToken) + if err != nil { + ctx.JSON(w, 401, result.CodeMsg(401, "shake error 无效身份授权")) + return + } + + // 获取缓存的用户信息 + data, ok := cache.GetLocalTTL(se.AccountId) + if data == nil || !ok { + ctx.JSON(w, 401, result.CodeMsg(401, "info error 无效身份授权")) + return + } + loginUser := data.(vo.LoginUser) + + // 登录用户角色权限校验 + if options != nil { + var roles []string + for _, item := range loginUser.User.Roles { + roles = append(roles, item.RoleKey) + } + perms := loginUser.Permissions + verifyOk := verifyRolePermission(roles, perms, options) + if !verifyOk { + msg := fmt.Sprintf("无权访问 %s %s", r.Method, r.RequestURI) + ctx.JSON(w, 403, result.CodeMsg(403, msg)) + return + } + } + + // 在请求的 Context 中存储数据 + rContext := r.Context() + rContext = context.WithValue(rContext, ctx.ContextKey("LoginUser"), loginUser) + // 继续处理请求 + next.ServeHTTP(w, r.WithContext(rContext)) + }) + } +} + +// verifyRolePermission 校验角色权限是否满足 +// +// roles 角色字符数组 +// +// perms 权限字符数组 +// +// options 参数 +func verifyRolePermission(roles, perms []string, options map[string][]string) bool { + // 直接放行 管理员角色或任意权限 + if contains(roles, "admin") || contains(perms, "*:*:*") { + return true + } + opts := make([]bool, 4) + + // 只需含有其中角色 + hasRole := false + if arr, ok := options["hasRoles"]; ok && len(arr) > 0 { + hasRole = some(roles, arr) + opts[0] = true + } + + // 只需含有其中权限 + hasPerms := false + if arr, ok := options["hasPerms"]; ok && len(arr) > 0 { + hasPerms = some(perms, arr) + opts[1] = true + } + + // 同时匹配其中角色 + matchRoles := false + if arr, ok := options["matchRoles"]; ok && len(arr) > 0 { + matchRoles = every(roles, arr) + opts[2] = true + } + + // 同时匹配其中权限 + matchPerms := false + if arr, ok := options["matchPerms"]; ok && len(arr) > 0 { + matchPerms = every(perms, arr) + opts[3] = true + } + + // 同时判断 含有其中 + if opts[0] && opts[1] { + return hasRole && hasPerms + } + // 同时判断 匹配其中 + if opts[2] && opts[3] { + return matchRoles && matchPerms + } + // 同时判断 含有其中且匹配其中 + if opts[0] && opts[3] { + return hasRole && matchPerms + } + if opts[1] && opts[2] { + return hasPerms && matchRoles + } + + return hasRole || hasPerms || matchRoles || matchPerms +} + +// contains 检查字符串数组中是否包含指定的字符串 +func contains(arr []string, target string) bool { + for _, str := range arr { + if str == target { + return true + } + } + return false +} + +// some 检查字符串数组中含有其中一项 +func some(origin []string, target []string) bool { + has := false + for _, t := range target { + for _, o := range origin { + if t == o { + has = true + break + } + } + if has { + break + } + } + return has +} + +// every 检查字符串数组中同时包含所有项 +func every(origin []string, target []string) bool { + match := true + for _, t := range target { + found := false + for _, o := range origin { + if t == o { + found = true + break + } + } + if !found { + match = false + break + } + } + return match +}