JWT的基础与使用
JWT(JSON Web Token) 是一种用于在各方之间传输信息的紧凑、安全的方式,常用于身份验证和授权。它以令牌的形式将用户信息编码后传输,可以确保数据的完整性和安全性。
1.JWT的结构
JWT 是一个基于 JSON 的令牌,由三部分组成,通过点号 (.
) 分隔:
header.payload.signature
每部分都是经过 Base64Url 编码的字符串,分别对应以下三个部分:
1)Header(头部):
头部包含令牌的元数据,通常包括令牌类型和签名算法。
例子:
{ "alg": "HS256", "typ": "JWT" } |
alg
: 签名使用的算法(如 HMAC SHA256)。
typ
: 声明令牌类型为 JWT。
2)Payload(有效载荷):
有效载荷包含用户的声明(claims),也就是实际传输的数据。它通常包含身份验证或授权所需的信息。
{ "sub": "1234567890", "name": "John Doe", "admin": true, "iat": 1516239022 } |
常见的标准声明:
- iss (issuer):签发者
- sub (subject):面向的用户
- aud (audience):接收的受众
- exp (expiration):过期时间
- iat (issued at):签发时间
- nbf (not before):生效时间
自定义声明:可以根据需求添加任何信息。
3)Signature(签名):
签名用于验证令牌的真实性,并确保令牌在传输过程中没有被篡改。
签名的生成方式:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret ) |
签名使用头部中声明的算法和一个秘密密钥(secret key)生成。
2.JWT的工作流程
2.1.生成JWT
用户登录后,服务器生成一个包含用户身份信息的 JWT,使用密钥对其进行签名。
2.2.客户端存储JWT
生成的 JWT 会返回给客户端,客户端通常将其存储在浏览器的 localStorage
或 sessionStorage
中,或者存储在 Cookie 中。
2.3.客户端请求时携带JWT
在后续的请求中,客户端将 JWT 附加在请求头的 Authorization
字段中:
Authorization: Bearer <token> |
2.4.服务器验证JWT
服务器接收到 JWT 后,会使用相同的密钥对签名进行验证,确保令牌的完整性和真实性。
如果 JWT 有效,服务器允许访问资源;如果令牌无效或过期,返回 401 未授权状态。
3.JWT的优势
1)无状态:JWT 是无状态的,所有信息都保存在令牌中,服务器不需要维护会话数据。
2)跨域支持:由于 JWT 不依赖于特定的存储机制,适合跨域认证。
3)紧凑性:JWT 的结构紧凑,便于在 URL、HTTP 请求头等地方传输。
4)灵活性:JWT 可以包含任意声明,支持灵活的身份和权限管理。
4.JWT的使用场景
1)身份认证:用户登录后生成 JWT,之后每次请求时验证 JWT 以实现认证。
2)授权:通过 JWT 传递用户权限信息,确保用户只能访问被授权的资源。
3)信息传递:JWT 可以安全地传输一些加密的信息。
5. JWT 的安全性注意事项
签名算法:建议使用强大的签名算法(如 HS256
或 RS256
),避免使用不安全的算法。
HTTPS:JWT 中可能包含敏感信息,传输时必须使用 HTTPS 来防止令牌被截获。
令牌过期:为 JWT 设置合理的过期时间(exp
声明),防止长期有效的令牌被滥用。
刷新令牌:使用短期有效的访问令牌和长期有效的刷新令牌(Refresh Token)机制,以便在访问令牌过期后请求新的令牌。
6.Java代码实现生成和验证JWT
6.1.添加Maven依赖
如果你使用 Maven 构建项目,首先在 pom.xml
中添加 JJWT 库的依赖:
<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> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> </dependency> |
6.2.生成和验证 JWT 的 Java 代码
package com.me.mengyu.auth.net.jwt; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.Claims; import io.jsonwebtoken.security.Keys; import javax.crypto.SecretKey; import java.util.Date; public class JwtExample { // 生成一个密钥,用于签名 private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256); // 创建 JWT public static String createJwt(String subject) { // 设置 JWT 过期时间 long expirationTime = 1000 * 60 * 60; // 1 小时 return Jwts.builder() .setSubject(subject) // 设置主题(可以是用户ID等信息) .setIssuer("your-app") // 设置发行者 .setIssuedAt(new Date()) // 设置签发时间 .setExpiration(new Date(System.currentTimeMillis() + expirationTime)) // 设置过期时间 .signWith(SECRET_KEY) // 使用密钥签名 .compact(); // 生成 JWT } // 验证 JWT 并提取信息 public static void validateJwt(String token) { try { // 解析 JWT Claims claims = Jwts.parserBuilder() .setSigningKey(SECRET_KEY) // 设置密钥 .build() .parseClaimsJws(token) // 验证令牌 .getBody(); // 输出从 JWT 中提取的信息 System.out.println("Subject: " + claims.getSubject()); System.out.println("Issuer: " + claims.getIssuer()); System.out.println("Expiration: " + claims.getExpiration()); } catch (Exception e) { System.out.println("Invalid JWT token: " + e.getMessage()); } } public static void main(String[] args) { // 创建 JWT String jwt = createJwt("1234567890"); // 这里的主题可以是用户的ID System.out.println("Generated JWT: " + jwt); // 验证 JWT validateJwt(jwt); } } |
代码说明:
1)createJwt
方法:生成一个带有签名的 JWT,包含用户身份信息(subject
),签发者(issuer
),签发时间和过期时间。使用 HS256
算法和一个密钥签名。
2) validateJwt
方法:验证 JWT 是否有效,并从中提取 claims
信息(如 subject
,issuer
,expiration
)。如果令牌无效(例如过期或签名不匹配),会抛出异常。
运行结果示例:
Generated JWT: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoieW91ci1hcHAiLCJpYXQiOjE3MjcwMTcyNDYsImV4cCI6MTcyNzAyMDg0Nn0.tNylO-O5Jbazd0QzR85X8OB7p1mDy0IGtyl97Ro0FYQ Subject: 1234567890 Issuer: your-app Expiration: Mon Sep 23 00:00:46 CST 2024 |
说明:
生成的 JWT:格式为 header.payload.signature
,其中 payload 包含 sub
(用户ID)、iss
(发行者)等信息。
验证:输出 Subject
、Issuer
和 Expiration
,表明 JWT 验证成功并解析出了信息。
6.3.注意事项
1)密钥管理:在生产环境中,应该安全地管理密钥,避免泄露。
2)过期时间:务必设置合理的过期时间,防止长时间有效的令牌带来安全风险。
3)异常处理:应当详细处理验证失败的情况,比如过期令牌、签名错误等。