229 lines
5.7 KiB
Go
229 lines
5.7 KiB
Go
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"
|
|
commonConstants "ems.agt/src/framework/constants/common"
|
|
tokenUtils "ems.agt/src/framework/utils/token"
|
|
)
|
|
|
|
// 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) {
|
|
// 获取请求头标识信息
|
|
tokenStr := ctx.Authorization(r)
|
|
// 获取请求头标识信息-旧头
|
|
accessToken := r.Header.Get("AccessToken")
|
|
if tokenStr == "" && accessToken != "" {
|
|
// 验证令牌 == 这里直接查数据库session
|
|
if !dborm.XormExistValidToken(accessToken, 0) {
|
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization valid error"))
|
|
return
|
|
}
|
|
se, err := dborm.XormUpdateSessionShakeTime(accessToken)
|
|
if err != nil {
|
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization shake error"))
|
|
return
|
|
}
|
|
|
|
// 获取缓存的用户信息
|
|
data, ok := cache.GetLocalTTL(se.AccountId)
|
|
if data == nil || !ok {
|
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization 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("Unauthorized access %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(commonConstants.CTX_LOGIN_USER), loginUser)
|
|
// 继续处理请求
|
|
next.ServeHTTP(w, r.WithContext(rContext))
|
|
return
|
|
}
|
|
|
|
// 获取请求头标识信息
|
|
if tokenStr == "" {
|
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization token error"))
|
|
return
|
|
}
|
|
|
|
// 验证令牌
|
|
claims, err := tokenUtils.Verify(tokenStr)
|
|
if err != nil {
|
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization valid error"))
|
|
return
|
|
}
|
|
|
|
// 获取缓存的用户信息
|
|
loginUser := tokenUtils.LoginUser(claims)
|
|
if loginUser.UserID == "" {
|
|
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization shake error"))
|
|
return
|
|
}
|
|
|
|
// 检查刷新有效期后存入上下文
|
|
tokenUtils.RefreshIn(&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("Unauthorized access %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(commonConstants.CTX_LOGIN_USER), 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
|
|
}
|