diff --git a/config.toml b/config.toml index f0ce875..30cec60 100644 --- a/config.toml +++ b/config.toml @@ -51,7 +51,6 @@ OpenCaptchaTimeout = 3600 [Http.JWT] SigningKey = "f3a0ed18-3eea-4bc9-b440-d56c3bb77bd8" ExpiresTime = "168h" # 7 days -BufferTime = "24h" Issuer = "AtomFramework" [Http.Cors] diff --git a/middleware/jwt.go b/middleware/jwt.go index 45b4c44..6b7852d 100644 --- a/middleware/jwt.go +++ b/middleware/jwt.go @@ -8,7 +8,7 @@ import ( "github.com/rogeecn/gen" ) -func JWTAuth(jwt *jwt.JWT) gin.HandlerFunc { +func JWTAuth(j *jwt.JWT) gin.HandlerFunc { return func(c *gin.Context) { // 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录 token := c.Request.Header.Get("Authorization") @@ -19,7 +19,7 @@ func JWTAuth(jwt *jwt.JWT) gin.HandlerFunc { } // parseToken 解析token包含的信息 - claims, err := jwt.ParseToken(token) + claims, err := j.ParseToken(token) if err != nil { gen.NewBusError(http.StatusBadRequest, http.StatusBadRequest, err.Error()).JSON(c, false) c.Abort() @@ -35,7 +35,7 @@ func JWTAuth(jwt *jwt.JWT) gin.HandlerFunc { // c.Abort() //} - c.Set("claims", claims) + c.Set(jwt.CtxKey, claims) c.Next() } } diff --git a/middleware/operation.go b/middleware/operation.go index 6de67e3..d657fd1 100644 --- a/middleware/operation.go +++ b/middleware/operation.go @@ -26,7 +26,7 @@ func init() { } } -func OperationRecord(jwt *jwt.JWT) gin.HandlerFunc { +func OperationRecord() gin.HandlerFunc { return func(c *gin.Context) { var body []byte var userId int64 @@ -51,9 +51,16 @@ func OperationRecord(jwt *jwt.JWT) gin.HandlerFunc { } body, _ = json.Marshal(&m) } - claims, _ := jwt.GetClaims(c) - if claims.UserID != 0 { - userId = int64(claims.UserID) + + claimsCtx, exists := c.Get(jwt.CtxKey) + if !exists { + c.Next() + return + } + + claims := claimsCtx.(jwt.Claims) + if claims.UID != 0 { + userId = int64(claims.UID) } else { id, err := strconv.Atoi(c.Request.Header.Get("x-user-id")) if err != nil { diff --git a/middleware/rbac.go b/middleware/rbac.go index ad3c0a5..6df4b9f 100644 --- a/middleware/rbac.go +++ b/middleware/rbac.go @@ -12,19 +12,20 @@ import ( ) // Permission 拦截器 -func CheckPermission(config *config.Config, rbac rbac.IRbac, jwt *jwt.JWT) gin.HandlerFunc { +func CheckPermission(config *config.Config, rbac rbac.IRbac) gin.HandlerFunc { return func(c *gin.Context) { if config.App.Mode != "production" { c.Next() return } - claim, err := jwt.GetClaims(c) - if err != nil { + claimsCtx, exists := c.Get(jwt.CtxKey) + if !exists { gen.NewBusError(http.StatusBadRequest, http.StatusBadRequest, "Token 获取失败").JSON(c, false) c.Abort() return } + claims := claimsCtx.(jwt.Claims) //获取请求的PATH path := c.Request.URL.Path @@ -33,7 +34,7 @@ func CheckPermission(config *config.Config, rbac rbac.IRbac, jwt *jwt.JWT) gin.H method := c.Request.Method // 获取用户的角色 - role := strconv.Itoa(int(claim.RoleID)) + role := strconv.Itoa(int(claims.Role)) if rbac.Can(role, method, path) == false { gen.NewBusError(http.StatusForbidden, http.StatusForbidden, "未登录或非法访问").JSON(c, false) diff --git a/modules/auth/controller/permission.go b/modules/auth/controller/permission.go index b9c8413..61542c6 100755 --- a/modules/auth/controller/permission.go +++ b/modules/auth/controller/permission.go @@ -2,9 +2,12 @@ package controller import ( "atom/providers/jwt" + "atom/providers/log" "atom/providers/rbac" + "net/http" "github.com/gin-gonic/gin" + "github.com/rogeecn/gen" ) type PermissionController interface { @@ -24,12 +27,14 @@ func NewPermissionController( } func (c *permissionControllerImpl) Get(ctx *gin.Context) (string, error) { - claims, err := c.jwt.GetClaims(ctx) - if err != nil { - return "", err + claimsCtx, exists := ctx.Get(jwt.CtxKey) + if !exists { + return "", gen.NewBusError(http.StatusBadRequest, http.StatusBadRequest, "Token 获取失败") } + claims := claimsCtx.(jwt.Claims) + log.Debug("claim: ", claims) - perm, err := c.rbac.JsonPermissionsForUser(claims.Username) + perm, err := c.rbac.JsonPermissionsForUser("Rogee") if err != nil { return "", err } diff --git a/modules/auth/routes/routes.go b/modules/auth/routes/routes.go index d3bd8d1..285b65c 100755 --- a/modules/auth/routes/routes.go +++ b/modules/auth/routes/routes.go @@ -68,9 +68,9 @@ func (r *Route) Register() { )) } - permissionGroup := group.Group("permission") + permissionGroup := group.Group("permissions") { - permissionGroup.GET("/permissions", gen.DataFunc(r.permission.Get)) + permissionGroup.GET("", gen.DataFunc(r.permission.Get)) } } diff --git a/modules/auth/service/user.go b/modules/auth/service/user.go index 6c80830..7c3907c 100755 --- a/modules/auth/service/user.go +++ b/modules/auth/service/user.go @@ -59,10 +59,7 @@ func (svc *userService) AuthMatchPassword(ctx context.Context, req *dto.LoginReq func (svc *userService) GenerateJWTTokenFromUser(ctx context.Context, user *models.User) (string, error) { return svc.jwt.CreateToken(svc.jwt.CreateClaims(jwt.BaseClaims{ - UUID: user.UUID, - UserID: user.ID, - Username: user.Username, - NickName: user.Nickname, - RoleID: user.RoleID, + UID: user.ID, + Role: user.RoleID, })) } diff --git a/providers/config/section_http.go b/providers/config/section_http.go index f87a5e8..976cc8c 100644 --- a/providers/config/section_http.go +++ b/providers/config/section_http.go @@ -23,7 +23,6 @@ type Http struct { type JWT struct { SigningKey string // jwt签名 ExpiresTime string // 过期时间 - BufferTime string // 缓冲时间 Issuer string // 签发者 } @@ -35,14 +34,6 @@ func (j JWT) ExpiresTimeDuration() time.Duration { return d } -func (j JWT) BufferTimeDuration() time.Duration { - d, err := time.ParseDuration(j.BufferTime) - if err != nil { - log.Fatal(err) - } - return d -} - type Whitelist struct { AllowOrigin string AllowHeaders string diff --git a/providers/jwt/jwt.go b/providers/jwt/jwt.go index 746995c..164f373 100644 --- a/providers/jwt/jwt.go +++ b/providers/jwt/jwt.go @@ -8,9 +8,7 @@ import ( "strings" "time" - "github.com/gin-gonic/gin" jwt "github.com/golang-jwt/jwt/v4" - uuid "github.com/satori/go.uuid" "golang.org/x/sync/singleflight" ) @@ -25,23 +23,19 @@ func init() { } } +type BaseClaims struct { + UID uint64 `json:"uid,omitempty"` + Role uint64 `json:"role,omitempty"` +} + // Custom claims structure -type CustomClaims struct { +type Claims struct { BaseClaims - BufferTime int64 jwt.RegisteredClaims } const TOKEN_PREFIX = "Bearer " -type BaseClaims struct { - UUID string - UserID uint64 - Username string - NickName string - RoleID uint64 -} - type JWT struct { config *config.Config singleflight *singleflight.Group @@ -62,12 +56,10 @@ func NewJWT(config *config.Config) (*JWT, error) { }, nil } -func (j *JWT) CreateClaims(baseClaims BaseClaims) CustomClaims { - bf, _ := time.ParseDuration(j.config.Http.JWT.BufferTime) +func (j *JWT) CreateClaims(baseClaims BaseClaims) Claims { ep, _ := time.ParseDuration(j.config.Http.JWT.ExpiresTime) - claims := CustomClaims{ + claims := Claims{ BaseClaims: baseClaims, - BufferTime: int64(bf / time.Second), // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失 RegisteredClaims: jwt.RegisteredClaims{ NotBefore: jwt.NewNumericDate(time.Now().Add(-time.Second * 10)), // 签名生效时间 ExpiresAt: jwt.NewNumericDate(time.Now().Add(ep)), // 过期时间 7天 配置文件 @@ -78,13 +70,13 @@ func (j *JWT) CreateClaims(baseClaims BaseClaims) CustomClaims { } // 创建一个token -func (j *JWT) CreateToken(claims CustomClaims) (string, error) { +func (j *JWT) CreateToken(claims Claims) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(j.SigningKey) } // CreateTokenByOldToken 旧token 换新token 使用归并回源避免并发问题 -func (j *JWT) CreateTokenByOldToken(oldToken string, claims CustomClaims) (string, error) { +func (j *JWT) CreateTokenByOldToken(oldToken string, claims Claims) (string, error) { v, err, _ := j.singleflight.Do("JWT:"+oldToken, func() (interface{}, error) { return j.CreateToken(claims) }) @@ -92,9 +84,9 @@ func (j *JWT) CreateTokenByOldToken(oldToken string, claims CustomClaims) (strin } // 解析 token -func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) { +func (j *JWT) ParseToken(tokenString string) (*Claims, error) { tokenString = strings.TrimPrefix(tokenString, TOKEN_PREFIX) - token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) { + token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (i interface{}, e error) { return j.SigningKey, nil }) if err != nil { @@ -112,7 +104,7 @@ func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) { } } if token != nil { - if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { + if claims, ok := token.Claims.(*Claims); ok && token.Valid { return claims, nil } return nil, TokenInvalid @@ -121,68 +113,3 @@ func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) { return nil, TokenInvalid } } - -func (j *JWT) GetClaims(c *gin.Context) (*CustomClaims, error) { - token := c.Request.Header.Get(HttpHeader) - claims, err := j.ParseToken(token) - if err != nil { - log.Error("从Gin的Context中获取从jwt解析信息失败, 请检查请求头是否存在 Authorization 且 Claims 为规定结构") - } - return claims, err -} - -// GetUserID 从Gin的Context中获取从jwt解析出来的用户ID -func (j *JWT) GetUserID(c *gin.Context) uint64 { - if claims, exists := c.Get(CtxKey); !exists { - if cl, err := j.GetClaims(c); err != nil { - return 0 - } else { - return cl.UserID - } - } else { - waitUse := claims.(*CustomClaims) - return waitUse.UserID - } -} - -// GetUserUuid 从Gin的Context中获取从jwt解析出来的用户UUID -func (j *JWT) GetUserUuid(c *gin.Context) string { - if claims, exists := c.Get(CtxKey); !exists { - if cl, err := j.GetClaims(c); err != nil { - return uuid.UUID{}.String() - } else { - return cl.UUID - } - } else { - waitUse := claims.(*CustomClaims) - return waitUse.UUID - } -} - -// GetUserAuthorityId 从Gin的Context中获取从jwt解析出来的用户角色id -func (j *JWT) GetRoleId(c *gin.Context) uint64 { - if claims, exists := c.Get(CtxKey); !exists { - if cl, err := j.GetClaims(c); err != nil { - return 0 - } else { - return cl.RoleID - } - } else { - waitUse := claims.(*CustomClaims) - return waitUse.RoleID - } -} - -// GetUserInfo 从Gin的Context中获取从jwt解析出来的用户角色id -func (j *JWT) GetUserInfo(c *gin.Context) *CustomClaims { - if claims, exists := c.Get("claims"); !exists { - if cl, err := j.GetClaims(c); err != nil { - return nil - } else { - return cl - } - } else { - waitUse := claims.(*CustomClaims) - return waitUse - } -}