主题
16 · JWT 鉴权
目标:签发与校验 JWT,用中间件保护需登录接口。
前置:15 · Validator
库:golang-jwt/jwt · 对照:jsonwebtoken
概念:JWT.io Introduction
1. 心智模型
text
登录成功 → 服务端签 JWT(header.payload.signature)
客户端 Authorization: Bearer <token>
受保护路由 → 中间件验签 + 过期 → c.Set("userId", ...)1
2
3
2
3
| 放哪 | 常见做法 |
|---|---|
| Access Token | 短过期(如 2h),放 Header |
| Refresh | 另议;入门可先只做 Access |
| 密钥 | 仅环境变量;HS256 入门够用 |
Journal H5 粘贴 JWT 的体验,与「服务端如何验」是同一枚硬币的两面。
2. 最小可跑
go
package auth
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
type Claims struct {
UserID string `json:"uid"`
jwt.RegisteredClaims
}
func Sign(secret, userID string, ttl time.Duration) (string, error) {
claims := Claims{
UserID: userID,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(ttl)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return t.SignedString([]byte(secret))
}
func Parse(secret, tokenStr string) (*Claims, error) {
t, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(t *jwt.Token) (any, error) {
if t.Method != jwt.SigningMethodHS256 {
return nil, errors.New("unexpected signing method")
}
return []byte(secret), nil
})
if err != nil {
return nil, err
}
claims, ok := t.Claims.(*Claims)
if !ok || !t.Valid {
return nil, errors.New("invalid token")
}
return claims, nil
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Gin 中间件:
go
func JWTAuth(secret string) gin.HandlerFunc {
return func(c *gin.Context) {
h := c.GetHeader("Authorization")
if !strings.HasPrefix(h, "Bearer ") {
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
return
}
claims, err := auth.Parse(secret, strings.TrimPrefix(h, "Bearer "))
if err != nil {
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
return
}
c.Set("userId", claims.UserID)
c.Next()
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3. 动手清单
- [ ]
POST /auth/login返回 token(可先写死用户表) - [ ]
GET /me需 JWT,返回 userId - [ ] 过期 token → 401
- [ ] 篡改 payload → 401
- [ ] 密钥来自 Viper/环境变量
4. 项目驱动
| 场景 | 要点 |
|---|---|
| 手帐 CRUD | 写操作挂 JWT;列表可按产品决定是否公开 |
| Token 缓存 | 下一阶段 Redis 存黑名单或 session(21) |
| 管理端 | 可另 claims 加 role |
5. 常见坑 + AI 审查
| 坑 | 说明 |
|---|---|
不校验 alg | 经典漏洞;上面显式限制 HS256 |
密钥用 "secret" 提交仓库 | 禁止 |
| 把 JWT 当加密 | 只是签名;敏感数据别塞 payload |
AI 用已废弃 dgrijalva/jwt-go | 用 golang-jwt/jwt/v5 |
