JWT 认证机制
JWT(JSON Web Token)是一种轻量级的身份认证机制,广泛应用于现代 Web 开发中,尤其是在分布式系统和微服务中。它通过签名技术确保数据的真实性和完整性。
1. JWT 的基本结构
JWT 是一个由三部分组成的字符串(头部,负载,签名):
Header.Payload.Signature
1.1 Header(头部)
- 作用:声明 Token 的元信息,如签名算法和 Token 类型。
- 典型结构:
{ "alg": "HS256", // 签名算法,如 HMAC SHA256 "typ": "JWT" // Token 类型 }
- Base64 编码:经过 Base64 编码后,生成 Header 部分。
1.2 Payload(负载)
- 作用:
- Payload 部分是一个 JSON 对象,用来存放实际需要传递的数据。
- 存储声明(claims),包括用户信息和元数据。
- 三种声明分类:
- 注册声明(Registered Claims):
标准字段,JWT 规定了7个官方字段,供选用。- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
- 公开声明(Public Claims):自定义数据字段,如
user_id
、role
。 - 私有声明(Private Claims):仅供双方约定的字段。
- 注册声明(Registered Claims):
- 典型结构:
{ "sub": "1234567890", "name": "John Doe", "admin": true, "iat": 1516239022 // 签发时间 }
- Base64 编码:经过 Base64 编码后,生成 Payload 部分。
1.3 Signature(签名)
- 作用:保证 Token 的完整性和不可篡改。
- 生成方式:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
- 使用 Header 和 Payload 的 Base64 编码内容生成签名。Base64URL与Base64算法类似,Base64中+、/和= 在URL中有特殊含义,因此Base64URL 对它们做了处理
secret
是服务器端的私钥,仅服务端可知。
JWT 是怎么工作的?
JWT 的工作流程简单,可以分为两步:生成 Token 和 验证 Token。
1. 生成 Token
- 用户登录时,服务器验证用户名和密码。
- 验证通过后,服务器根据用户信息生成 JWT。
- 把这个 Token 返回给客户端,客户端存起来(如
localStorage
或cookie
)。
2. 验证 Token
- 客户端每次请求时,把 Token 放到 HTTP 请求头里:
Authorization: Bearer <Token>
- 服务器拿到 Token 后,检查:
- 签名:确认 Token 没被改过。
- 过期时间:检查
exp
是否有效。
- 验证通过后,服务器允许访问资源。
JWT 的优点
- 无状态:服务器不用存储会话信息,特别适合分布式系统。
- 高效:Token 放在客户端,服务器验证时直接解析,无需查询数据库。
- 灵活:可以在 Payload 中添加任何需要的用户信息。
JWT 的缺点
- Token 无法主动失效:
- 如果用户登出或权限变更,旧 Token 仍然有效,除非改签名密钥。
- Token 体积较大:
- 包含 Header、Payload 和签名,可能会影响网络性能。
- JWT默认不加密,在不加密的情况下,不要携带敏感数据
- JWT 的组成部分(Header、Payload、Signature)是以 Base64 编码存储的,而 Base64 不是加密,只是编码。这意味着任何拿到 JWT 的人都可以轻松解码出 Header 和 Payload,看到里面的内容。
例子:日常生活中的 JWT
假设你去一个游乐园玩,买了一张票(JWT)。
- 票上的信息(Payload):你的姓名、有效时间、可玩的项目。
- 防伪标识(Signature):游乐园的官方签名,防止伪造票。
你拿着票(Token)可以自由进入项目(资源)。
如果票到期(过期时间),你需要重新购买(重新登录)。
4. 安全注意事项
- 使用 HTTPS:
- 确保 Token 传输过程中不会被窃听。
- 设置合理的过期时间:
- 控制 Token 的有效期,减少风险。
- 定期更换密钥:
- 避免长期使用同一密钥。
- Token 存储位置:
- 避免将 Token 存储在不安全的位置(如
localStorage
)。
- 避免将 Token 存储在不安全的位置(如
- Token 黑名单:
- 通过数据库记录已失效的 Token 实现黑名单机制,弥补无状态的不足。
5. 实现示例(Go 语言)
依赖安装
使用 github.com/golang-jwt/jwt/v4
包:
go get github.com/golang-jwt/jwt/v4
代码实现
package main
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v4"
)
var secretKey = []byte("my_secret_key") // 密钥
// 生成 JWT
func GenerateJWT(userID string) (string, error) {
claims := jwt.MapClaims{
"user_id": userID,
"exp": time.Now().Add(time.Hour * 2).Unix(), // 过期时间
"iat": time.Now().Unix(), // 签发时间
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(secretKey)
}
// 验证 JWT
func ValidateJWT(tokenString string) (string, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("签名方法不匹配")
}
return secretKey, nil
})
if err != nil {
return "", err
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims["user_id"].(string), nil
}
return "", fmt.Errorf("无效的 Token")
}
func main() {
// 生成 Token
token, err := GenerateJWT("12345")
if err != nil {
fmt.Println("生成 Token 失败:", err)
return
}
fmt.Println("生成的 Token:", token)
// 验证 Token
userID, err := ValidateJWT(token)
if err != nil {
fmt.Println("验证 Token 失败:", err)
return
}
fmt.Println("验证成功,用户 ID:", userID)
}
标签:err,JWT,jwt,token,Token,go,Payload From: https://blog.csdn.net/m0_74282926/article/details/145287785