You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
5.7 KiB

package authorization
import (
"github.com/dchest/captcha"
"github.com/gin-gonic/gin"
"goskeleton/app/global/consts"
"goskeleton/app/global/variable"
userstoken "goskeleton/app/service/users/token"
"goskeleton/app/utils/response"
"strings"
)
type HeaderParams struct {
Authorization string `header:"Authorization" binding:"required,min=20"`
}
// CheckTokenAuth 检查token完整性、有效性中间件
func CheckTokenAuth() gin.HandlerFunc {
return func(context *gin.Context) {
headerParams := HeaderParams{}
// 推荐使用 ShouldBindHeader 方式获取头参数
if err := context.ShouldBindHeader(&headerParams); err != nil {
response.TokenErrorParam(context, consts.JwtTokenMustValid+err.Error())
return
}
token := strings.Split(headerParams.Authorization, " ")
if len(token) == 2 && len(token[1]) >= 20 {
tokenIsEffective := userstoken.CreateUserFactory().IsEffective(token[1])
if tokenIsEffective {
if customToken, err := userstoken.CreateUserFactory().ParseToken(token[1]); err == nil {
key := variable.ConfigYml.GetString("Token.BindContextKeyName")
// token验证通过同时绑定在请求上下文
context.Set(key, customToken)
}
context.Next()
} else {
response.ErrorTokenAuthFail(context)
}
} else {
response.ErrorTokenBaseInfo(context)
}
}
}
// CheckTokenAuthWithRefresh 检查token完整性、有效性并且自动刷新中间件
func CheckTokenAuthWithRefresh() gin.HandlerFunc {
return func(context *gin.Context) {
headerParams := HeaderParams{}
// 推荐使用 ShouldBindHeader 方式获取头参数
if err := context.ShouldBindHeader(&headerParams); err != nil {
response.TokenErrorParam(context, consts.JwtTokenMustValid+err.Error())
return
}
token := strings.Split(headerParams.Authorization, " ")
if len(token) == 2 && len(token[1]) >= 20 {
tokenIsEffective := userstoken.CreateUserFactory().IsEffective(token[1])
// 判断token是否有效
if tokenIsEffective {
if customToken, err := userstoken.CreateUserFactory().ParseToken(token[1]); err == nil {
key := variable.ConfigYml.GetString("Token.BindContextKeyName")
// token验证通过同时绑定在请求上下文
context.Set(key, customToken)
// 在自动刷新token的中间件中将请求的认证键、值原路返回与后续刷新逻辑格式保持一致
context.Header("Refresh-Token", "")
context.Header("Access-Control-Expose-Headers", "Refresh-Token")
}
context.Next()
} else {
// 判断token是否满足刷新条件
if userstoken.CreateUserFactory().TokenIsMeetRefreshCondition(token[1]) {
// 刷新token
if newToken, ok := userstoken.CreateUserFactory().RefreshToken(token[1]); ok {
if customToken, err := userstoken.CreateUserFactory().ParseToken(newToken); err == nil {
key := variable.ConfigYml.GetString("Token.BindContextKeyName")
// token刷新成功同时绑定在请求上下文
context.Set(key, customToken)
}
// 新token放入header返回
context.Header("Refresh-Token", newToken)
context.Header("Access-Control-Expose-Headers", "Refresh-Token")
context.Next()
} else {
response.ErrorTokenRefreshFail(context)
}
} else {
response.ErrorTokenRefreshFail(context)
}
}
} else {
response.ErrorTokenBaseInfo(context)
}
}
}
// RefreshTokenConditionCheck 刷新token条件检查中间件针对已经过期的token要求是token格式以及携带的信息满足配置参数即可
func RefreshTokenConditionCheck() gin.HandlerFunc {
return func(context *gin.Context) {
headerParams := HeaderParams{}
if err := context.ShouldBindHeader(&headerParams); err != nil {
response.TokenErrorParam(context, consts.JwtTokenMustValid+err.Error())
return
}
token := strings.Split(headerParams.Authorization, " ")
if len(token) == 2 && len(token[1]) >= 20 {
// 判断token是否满足刷新条件
if userstoken.CreateUserFactory().TokenIsMeetRefreshCondition(token[1]) {
context.Next()
} else {
response.ErrorTokenRefreshFail(context)
}
} else {
response.ErrorTokenBaseInfo(context)
}
}
}
// CheckCasbinAuth casbin检查用户对应的角色权限是否允许访问接口
func CheckCasbinAuth() gin.HandlerFunc {
return func(c *gin.Context) {
requstUrl := c.Request.URL.Path
method := c.Request.Method
// 模拟请求参数转换后的角色roleId=2
// 主线版本没有深度集成casbin的使用逻辑
// GinSkeleton-Admin 系统则深度集成了casbin接口权限管控
// 详细实现参考地址https://gitee.com/daitougege/gin-skeleton-admin-backend/blob/master/app/http/middleware/authorization/auth.go
role := "2" // 这里模拟某个用户的roleId=2
// 这里将用户的id解析为所拥有的的角色判断是否具有某个权限即可
isPass, err := variable.Enforcer.Enforce(role, requstUrl, method)
if err != nil {
response.ErrorCasbinAuthFail(c, err.Error())
return
} else if !isPass {
response.ErrorCasbinAuthFail(c, "")
return
} else {
c.Next()
}
}
}
// CheckCaptchaAuth 验证码中间件
func CheckCaptchaAuth() gin.HandlerFunc {
return func(c *gin.Context) {
captchaIdKey := variable.ConfigYml.GetString("Captcha.captchaId")
captchaValueKey := variable.ConfigYml.GetString("Captcha.captchaValue")
captchaId := c.PostForm(captchaIdKey)
value := c.PostForm(captchaValueKey)
if captchaId == "" || value == "" {
response.Fail(c, consts.CaptchaCheckParamsInvalidCode, consts.CaptchaCheckParamsInvalidMsg, "")
return
}
if captcha.VerifyString(captchaId, value) {
c.Next()
} else {
response.Fail(c, consts.CaptchaCheckFailCode, consts.CaptchaCheckFailMsg, "")
}
}
}