JWT已经成为了主流的认证与授权解决方案。它的高效、无状态的特性使得其成为微服务架构中认证和授权的理想选择。JWT 可以在多个系统或服务之间传递身份信息,而不需要为每个请求维护会话信息。JWT 认证的基本流程是:客户端使用用户名和密码登录,服务器生成一个 JWT,并返回给客户端。客户端在后续请求中携带这个 JWT,服务器根据 JWT 来验证用户身份和权限。
JWT组成
一个完整的 JWT 由三部分组成:
- Header(头部) :描述 JWT 的元数据,通常包括令牌类型(JWT)和使用的签名算法(如 HMAC SHA256 或 RSA)。
示例:
{ "alg": "HS256", "typ": "JWT" } - Payload(有效载荷) :包含声明(Claims),即需要传递的信息。可以是用户的身份信息(如用户 ID)和其他一些数据(如权限、过期时间等)。声明有三种类型:
注册声明(Registered Claims):这些是预定义的声明,例如
iss(发行者)、exp(过期时间)、sub(主题)、aud(受众)等。 公共声明(Public Claims):这些是可以自定义的声明,但是应避免冲突。 私有声明(Private Claims):这些是用于在客户端和服务器之间共享信息的自定义声明。{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 } - Signature(签名) :为了防止数据被篡改,JWT 的最后一部分是签名。签名的生成方式是:将编码后的 header 和 payload 用密钥和指定的算法(如
HMAC SHA256或RSA)进行签名。HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), your-256-bit-secret)
JWT使用
- JWT 结构体定义
首先,定义一个
JWT结构体来封装生成和验证 JWT 所需的关键信息,包括密钥、过期时间、最大刷新时间等。 - 生成 JWT
通过
NewJWT方法,我们可以创建一个新的 JWT 实例,用于生成和管理令牌。expireTime指定了 JWT 的过期时间,maxRefresh则指定了可以刷新 JWT 的最大时间。 - 解析 JWT
ParseToken方法用来解析 JWT。它会验证 JWT 是否有效,并解析 JWT 中的声明信息。若 JWT 无效或过期,返回相应的错误。 - 刷新 JWT
在 JWT 过期后,用户可以通过刷新 JWT 获取新的有效令牌。
RefreshToken方法在最大刷新时间内有效,若超时,则令牌不可再刷新。
JWT 鉴权用户认证流程
1. 用户登录
用户使用用户名和密码向服务器发送登录请求。服务器验证用户信息的正确性,如果成功,则生成 JWT 返回给用户。 示例:
// 伪代码,用户登录并生成 JWT
func login(w http.ResponseWriter, r *http.Request) {
username := r.FormValue("username")
password := r.FormValue("password")
// 验证用户名和密码
user, err := authenticateUser(username, password)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// 创建一个 JWT
token, err := createJWT(user)
if err != nil {
http.Error(w, "Failed to create token", http.StatusInternalServerError)
return
}
// 返回 JWT 给客户端
w.Header().Set("Authorization", "Bearer "+token)
w.Write([]byte("Login successful"))
}
在用户成功登录后,服务器生成一个 JWT,并将其返回给客户端。客户端一般会将 JWT 存储在本地的存储中(如 localStorage 或 sessionStorage),并在之后的请求中将 JWT 作为请求头传递。
2. 客户端携带 JWT 发起请求
在客户端发起每个请求时,都会将 JWT 放在请求的 Authorization 头部
// 发送包含 JWT 的请求
req, err := http.NewRequest("GET", "http://localhost:8080/protected", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer " + token) // JWT token
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
3. 服务器验证 JWT
服务器在收到请求时,会检查请求头中的 Authorization 字段,提取 JWT,并验证其有效性。验证的主要内容包括:
- 签名验证:验证 JWT 是否在传输过程中被篡改。
- 过期时间验证:验证
exp(过期时间)是否有效。 - 有效载荷验证:根据有效载荷(如用户 ID、角色、权限等)进行权限控制。