1、JWT简介
JWT (JSON Web Token) 是一种基于 JSON 格式的开放标准(RFC 7519),用于在不同系统间作为一种安全的、紧凑的令牌实现信息的传递。它通常用于身份验证、授权以及信息安全传递
1.1、JWT 的组成
JWT 包含三个部分,每部分用 . 分隔:
Header.Payload.Signature
1、Header(头部)
Header 通常包含两部分信息:
- 类型(typ):JWT
- 签名算法(alg):例如 HMAC、SHA256 或 RSA
示例 :
{
"alg": "HS256",
"typ": "JWT"
}
经过 Base64Url 编码后:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2、Payload(载荷)
Payload 是存储实际数据的部分,可以包含用户信息或声明(Claims)。通常包含以下两类声明:
- Registered Claims(预定义声明):如 iss(签发者)、exp(过期时间)、sub(主题)、aud(受众)等
- Public Claims(公共声明):开放给所有用户使用,但需避免冲突
- Private Claims(私有声明):为双方协商使用
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
经过 Base64Url 编码后:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
3、Signature(签名)
签名是通过将 Header 和 Payload 的 Base64 编码部分连接起来,用指定的算法(如 HMAC SHA256)与密钥进行加密生成的
生成签名的公式:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
1.2、JWT 的工作原理
1、客户端登录:
用户向服务器发送登录请求,验证成功后,服务器生成 JWT,发送给客户端。
2、客户端存储 Token:
客户端通常将 Token 存储在 Cookie 或 LocalStorage 中。
3、请求携带 Token:
客户端每次请求 API 时,将 JWT 放在请求头的 Authorization 中,例如:
Authorization: Bearer <JWT_TOKEN>
4、服务器验证 Token:
服务器通过解析 JWT 验证签名是否正确,以及 Token 是否过期
1.3、JWT 优缺点
优点 :
1、自包含性:
JWT 包含了所有必要信息,无需额外的数据库查询
2、轻量和跨语言支持:
JWT 是一个紧凑的字符串,传输开销小
各种语言都有库支持(如 Go、Java、Python、Node.js)
3、易于扩展:
可以在 Payload 中存储自定义数据
缺点 :
1、无法撤销:
如果 JWT 未存储在服务器端,一旦签发无法轻易撤销
2、体积可能较大:
如果 Payload 数据过多,传输体积会增加
3、安全风险:
密钥一旦泄露,JWT 的签名将不再可信
必须小心管理 secret 并限制 Payload 的敏感数据
1.4、JWT 的应用场景
1、身份验证
用户登录后,JWT 可用作会话标识,客户端每次请求时携带 JWT 作为用户身份
2、信息交换
JWT 的签名可以确保信息的完整性,因此它也可以用于不同系统之间安全传递信息
3、单点登录(SSO)
JWT 可以在不同系统之间安全地传递用户身份,适用于 SSO 场景
2、go JWT 示例
2.1、安装依赖
go get github.com/golang-jwt/jwt/v4
2.2、生成和解析 JWT 的完整代码
package main
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v4"
)
// 定义 JWT 的密钥
var jwtKey = []byte("my_secret_key")
// 自定义的 Claims 结构
type Claims struct {
Username string `json:"username"`
jwt.RegisteredClaims
}
// 生成 JWT
func generateJWT(username string) (string, error) {
// 设置过期时间
expirationTime := time.Now().Add(5 * time.Minute)
// 创建自定义的 Claims
claims := &Claims{
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
},
}
// 创建 token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 使用密钥签名
tokenString, err := token.SignedString(jwtKey)
if err != nil {
return "", err
}
return tokenString, nil
}
// 验证 JWT
func parseJWT(tokenString string) (*Claims, error) {
claims := &Claims{}
// 解析 token
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
// 验证使用的是 HMAC 的签名方法
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return jwtKey, nil
})
if err != nil {
return nil, err
}
// 检查 token 是否有效
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}
return claims, nil
}
func main() {
// 生成一个 JWT
token, err := generateJWT("testuser")
if err != nil {
fmt.Println("Error generating token:", err)
return
}
fmt.Println("Generated Token:", token)
// 解析并验证 JWT
claims, err := parseJWT(token)
if err != nil {
fmt.Println("Error parsing token:", err)
return
}
fmt.Printf("Token is valid! Username: %s, ExpiresAt: %s\n", claims.Username, claims.ExpiresAt.Time)
}
运行结果 :
Generated Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3R1c2VyIiwiZXhwIjoxNzMzMjIzMzEzfQ.XMFme_R3lSicA_4nATbx_wEMjJYbS6BhkEdECRVEsLc
Token is valid! Username: testuser, ExpiresAt: 2024-12-03 18:55:13 +0800 CST
3、java JWT 示例
3.1、pom 依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
3.2、完整代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class Demo {
public static void main(String[] args) throws Exception {
// 1. 定义密钥
SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256); // 生成随机密钥
// 2. 生成 JWT
String jwt = generateJwt(secretKey);
System.out.println("生成的 JWT: " + jwt);
// 3. 验证 JWT
JwtResult jwtResult = validateJwt(jwt, secretKey);
System.out.println("JWT 是否有效: " + jwtResult.valid);
if (jwtResult.valid) {
Claims claims = jwtResult.getClaims();
String userId = claims.get("userId").toString();
String username = claims.get("username").toString();
Boolean isAdmin = Boolean.getBoolean(claims.get("admin").toString());
System.out.println("userId :" + userId + "; username :" + username + "; isAdmin :" + isAdmin + "; expiration : " + claims.getExpiration());
}
}
// 生成 JWT 的方法
public static String generateJwt(SecretKey secretKey) {
// 自定义 Payload 数据
Map<String, Object> claims = new HashMap<>();
claims.put("userId", "12345");
claims.put("username", "john_doe");
claims.put("admin", true);
// 生成 JWT
return Jwts.builder()
.setClaims(claims) // 添加自定义声明
.setSubject("User Authentication") // 设置主题
.setIssuer("MyApp") // 设置签发者
.setIssuedAt(new Date()) // 设置签发时间
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 设置过期时间 (1小时)
.signWith(secretKey) // 使用密钥签名
.compact();
}
// 验证 JWT 的方法
public static JwtResult validateJwt(String jwt, SecretKey secretKey) {
try {
// 解析 JWT
Jws<Claims> claimsJws = Jwts.parserBuilder()
.setSigningKey(secretKey) // 设置密钥
.build()
.parseClaimsJws(jwt);
return new JwtResult(true, claimsJws.getBody());
} catch (Exception e) {
System.out.println("JWT 验证失败: " + e.getMessage());
return new JwtResult(false ,null);
}
}
static class JwtResult {
private Boolean valid;
private Claims claims;
public JwtResult(Boolean valid, Claims claims) {
this.valid = valid;
this.claims = claims;
}
public Boolean getValid() {
return valid;
}
public void setValid(Boolean valid) {
this.valid = valid;
}
public Claims getClaims() {
return claims;
}
public void setClaims(Claims claims) {
this.claims = claims;
}
}
}
运行结果 :
生成的 JWT: eyJhbGciOiJIUzI1NiJ9.eyJhZG1pbiI6dHJ1ZSwidXNlcklkIjoiMTIzNDUiLCJ1c2VybmFtZSI6ImpvaG5fZG9lIiwic3ViIjoiVXNlciBBdXRoZW50aWNhdGlvbiIsImlzcyI6Ik15QXBwIiwiaWF0IjoxNzMzMjIzMTk1LCJleHAiOjE3MzMyMjY3OTV9.3npD-fv37fGlP573GSAG6OvqQ9m5cbBFFufFz75lXRY
JWT 是否有效: true
userId :12345; username :john_doe; isAdmin :false; expiration : Tue Dec 03 19:53:15 CST 2024
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。