jwt.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package jwt
  2. import (
  3. "errors"
  4. "strings"
  5. "time"
  6. "github.com/gin-gonic/gin"
  7. jwtpkg "github.com/golang-jwt/jwt/v5"
  8. "github.com/runningwater/gohub/pkg/app"
  9. "github.com/runningwater/gohub/pkg/config"
  10. "github.com/runningwater/gohub/pkg/logger"
  11. )
  12. var (
  13. ErrTokenExpired error = errors.New("令牌已过期")
  14. ErrTokenNotValidYet error = errors.New("令牌尚未生效")
  15. ErrTokenMalformed error = errors.New("令牌格式错误")
  16. ErrTokenInvalid error = errors.New("令牌无效")
  17. ErrTokenExpiredMaxRefresh error = errors.New("令牌已过期,无法刷新")
  18. ErrHeaderEmpty error = errors.New("需要认证才能访问!")
  19. ErrHeaderMalformed error = errors.New("请求头格式错误")
  20. )
  21. // JWT 结构体,包含签名密钥和刷新 Token 的最大过期时间
  22. type JWT struct {
  23. // 签名密钥,用于生成和验证 JWT 签名,读取 app.key
  24. SigningKey []byte
  25. // 刷新 Token 的最大过期时间
  26. MaxRefresh time.Duration
  27. }
  28. // CustomClaims 自定义的 JWT 载荷结构体,包含用户 ID、用户名 和过期时间
  29. type CustomClaims struct {
  30. // 用户 ID
  31. UserID string `json:"user_id"`
  32. // 用户名
  33. UserName string `json:"user_name"`
  34. // 过期时间
  35. ExpireTime int64 `json:"expire_time"`
  36. // 标准的 JWT 载荷字段
  37. // Audience aud : 受众
  38. // Issuer iss : 签发者
  39. // Not Before nbf : 生效时间
  40. // Issued At iat : 签发时间
  41. // JWT ID jti : JWT ID 编号
  42. // Subject sub : 主题
  43. // Expires At exp : 过期时间
  44. // 标准的 JWT 载荷字段
  45. jwtpkg.RegisteredClaims
  46. }
  47. func NewJWT() *JWT {
  48. return &JWT{
  49. SigningKey: []byte(config.GetString("app.key")),
  50. MaxRefresh: time.Duration(config.GetInt64("jwt.max_refresh_time")) * time.Minute,
  51. }
  52. }
  53. // ParseToken 解析 Token
  54. func (j *JWT) ParseToken(c *gin.Context) (*CustomClaims, error) {
  55. // 1. 获取请求头中的 Token
  56. tokenString, err := j.getTokenFromHeader(c)
  57. if err != nil {
  58. return nil, err
  59. }
  60. // 2. 调用 jwt 库解析用户传入的 token
  61. token, err := j.parseTokenString(tokenString)
  62. // 3. 解析出错, 未报错证明是合法的 token
  63. if err != nil {
  64. return nil, err
  65. // validationErr, ok := err.(*jwtpkg.ValidationError)
  66. // if ok {
  67. // switch validationErr.Errors {
  68. // case jwtpkg.ValidationErrorMalformed:
  69. // return nil, ErrTokenMalformed
  70. // case jwtpkg.ValidationErrorExpired:
  71. // return nil, ErrTokenExpired
  72. // default:
  73. // return nil, ErrTokenInvalid
  74. // }
  75. // }
  76. // }
  77. // return nil, ErrTokenInvalid
  78. }
  79. if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
  80. return claims, nil
  81. }
  82. return nil, ErrTokenInvalid
  83. }
  84. // IssueToken 签发 Token
  85. func (j *JWT) IssueToken(userID, userName string) string {
  86. // 1. 构造用户 claims 信息(负荷)
  87. expireTime := j.expireAtTime()
  88. claims := CustomClaims{
  89. userID,
  90. userName,
  91. expireTime.Unix(),
  92. jwtpkg.RegisteredClaims{
  93. NotBefore: jwtpkg.NewNumericDate(app.TimenowInTimezone()), // 生效时间
  94. IssuedAt: jwtpkg.NewNumericDate(app.TimenowInTimezone()), // 签发时间
  95. ExpiresAt: jwtpkg.NewNumericDate(expireTime), // 过期时间
  96. Issuer: config.GetString("app.name"), // 签发者
  97. Subject: userID, // 主题
  98. Audience: []string{userName}, // 受众
  99. },
  100. }
  101. // 2. 根据 claims 生成 Token
  102. token, err := j.generateToken(claims)
  103. if err != nil {
  104. logger.LogIf(err)
  105. return ""
  106. }
  107. return token
  108. }
  109. // RefreshToken 刷新 Token
  110. func (j *JWT) RefreshToken(c *gin.Context) (string, error) {
  111. // 1. 从 Header 里获取 token
  112. tokenString, err := j.getTokenFromHeader(c)
  113. if err != nil {
  114. return "", err
  115. }
  116. // 2. 调用 jwt 库解析用户传入的 token
  117. token, err := j.parseTokenString(tokenString)
  118. // 3. 解析出错, 未报错证明是合法的 token
  119. if err != nil {
  120. // validationErr, ok := err.(*jwtpkg.ValidationError)
  121. // // 满足刷新条件:只是单一的报错 ValidationErrorExpired
  122. // if !ok || validationErr.Errors != jwtpkg.ValidationErrorExpired {
  123. // return "", err
  124. // }
  125. if errors.Is(err, jwtpkg.ErrTokenExpired) {
  126. // 过期了,返回错误
  127. return "", err
  128. }
  129. }
  130. // 4. 解析 claims
  131. claims := token.Claims.(*CustomClaims)
  132. // 5. 判断是否过了最大允许刷新的时间
  133. x := app.TimenowInTimezone().Add(-j.MaxRefresh).Unix()
  134. if claims.IssuedAt.Unix() > x {
  135. // 修改过期时间
  136. claims.ExpiresAt = jwtpkg.NewNumericDate(j.expireAtTime())
  137. return j.generateToken(*claims)
  138. }
  139. // 6. 超过最大允许刷新的时间,返回错误
  140. return "", ErrTokenExpiredMaxRefresh
  141. }
  142. // generateToken 生成 Token, 内部使用,外部请调用 IssueToken
  143. func (j *JWT) generateToken(claims CustomClaims) (string, error) {
  144. // 创建一个新的 Token 对象,使用 HMAC SHA256 签名方法
  145. token := jwtpkg.NewWithClaims(jwtpkg.SigningMethodHS256, claims)
  146. return token.SignedString(j.SigningKey)
  147. }
  148. // expireAtTime 计算过期时间
  149. func (j *JWT) expireAtTime() time.Time {
  150. timeNow := app.TimenowInTimezone()
  151. var expireTime int64
  152. if config.GetBool("app.debug") {
  153. expireTime = config.GetInt64("jwt.debug_expire_time")
  154. } else {
  155. expireTime = config.GetInt64("jwt.expire_time")
  156. }
  157. expire := time.Duration(expireTime) * time.Minute
  158. return timeNow.Add(expire)
  159. }
  160. // getTokenFromHeader 获取请求头中的 Token
  161. // Authorization: Bearer Token
  162. // 从请求头中获取 Token,格式为 "Bearer Token"
  163. func (j *JWT) getTokenFromHeader(c *gin.Context) (string, error) {
  164. // 获取请求头中的 Token
  165. authHeader := c.Request.Header.Get("Authorization")
  166. if authHeader == "" {
  167. return "", ErrHeaderEmpty
  168. }
  169. // 检查请求头格式是否正确
  170. // Bearer Token
  171. // Bearer 后面有一个空格
  172. parts := strings.SplitN(authHeader, " ", 2)
  173. if len(parts) < 2 || parts[0] != "Bearer" {
  174. return "", ErrHeaderMalformed
  175. }
  176. // 返回 Token 字符串
  177. return parts[1], nil
  178. }
  179. // parseTokenString 解析 Token 字符串
  180. func (j *JWT) parseTokenString(tokenString string) (*jwtpkg.Token, error) {
  181. return jwtpkg.ParseWithClaims(
  182. tokenString,
  183. &CustomClaims{},
  184. func(token *jwtpkg.Token) (any, error) {
  185. return j.SigningKey, nil
  186. },
  187. )
  188. }