jwt-auth初识
已经被证实,下面的内容注水比较多,如有不适,请及时前往官网(https://jwt.io/)了解
JWT (JSON Web Token) 由三部分组成,每部分之间用点(.)分隔。这三部分分别是:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 头部(Header)包含令牌的类型(即JWT)以及所使用的签名算法(如HMAC SHA256或者RSA)。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 - 载荷(Payload)包含声明,即关于实体的信息和其他数据。声明可以是注册声明(预定义的声明,如iss(发行者)、exp(过期时间)、sub(主题)等)、公共声明、私有声明。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ - 签名(Signature)用于验证消息在此过程中没有被更改,并且,对于带有过期时间的令牌,也可以确认发送者的身份。签名是通过对头部和载荷进行Base64Url编码后的字符串,连同密钥一起使用指定的算法计算得出。
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
1. 头部(Header)
头部通常包含两部分信息:令牌的类型(即JWT)和所使用的签名算法(如HMAC SHA256或者RSA)。例如:
{
"alg": "HS256",
"typ": "JWT"
}
2. 载荷(Payload)
载荷包含了声明(claims),即您想要传达的信息。声明分为三种类型:注册声明(registered claims)、公共声明(public claims)和私有声明(private claims)。例如:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
3. 签名(Signature)
签名是用来验证消息在传输过程中没有被更改,并且,对于某些算法,还可以验证发送者的身份。签名是通过编码后的头部和载荷以及一个秘钥或私钥生成的。根据不同的加密方式,签名的生成方法也不同。
使用加密的方式多种样,包括使用 JJWT,Nimbus JOSE+JWT,Auth0 Java JWT等,万变不离其宗,所以记住定义以及各个部分比较重要。
这里使用Auth0 Java JWT实现:
依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.2.1</version> <!-- 请检查是否有更新版本 -->
</dependency>
代码
@Test
void contextLoads() {
try {
// 创建一个带有签名算法的JWT
String token = createToken();
System.out.println("Generated JWT: " + token);
// 验证并解析JWT
DecodedJWT jwt = verifyToken(token);
System.out.println("Decoded JWT: " + jwt.getToken());
// 注册声明(registered claims)
System.out.println(jwt.getSubject());
// 公共声明(public claims)
System.out.println(jwt.getIssuer());
// 私有声明(private claims)
System.out.println(jwt.getClaim("admin"));
} catch (JWTCreationException | JWTVerificationException exception){
// 异常处理:无效的签名/过期的令牌/算法不匹配等
System.out.println(exception.getMessage());
}
}
private String createToken() throws JWTCreationException {
Algorithm algorithm = Algorithm.HMAC256(SECRET.getBytes());
Map<String, Object> headerClaimsMap = new HashMap<>();
headerClaimsMap.put("alg","HS256");
headerClaimsMap.put("typ","JWT");
Map<String, Object> payloadClaimsMap = new HashMap<>();
payloadClaimsMap.put("sub", "1234567890");
payloadClaimsMap.put("name","John Doe");
payloadClaimsMap.put("admin", true);
return JWT.create()
// 头部(Header)
.withHeader(headerClaimsMap)
// 载荷(Payload)
.withPayload(payloadClaimsMap)
// 签名(Signature)
.withIssuer("auth0")
// 一天后过期
.withExpiresAt(new Date(System.currentTimeMillis() + 86400000))
.sign(algorithm);
}
private DecodedJWT verifyToken(String token) throws JWTVerificationException {
Algorithm algorithm = Algorithm.HMAC256(SECRET.getBytes());
return JWT.require(algorithm)
.withIssuer("auth0") // 检查发行者是否为"auth0"
.build() // 返回一个JWTVerifier
.verify(token); // 对JWT进行验证
}
流程
1. 用户登录
客户端向服务器发送用户名和密码进行认证。
如果提供的凭证正确,服务器会生成一个JWT并将其返回给客户端。
2. JWT 的生成
服务器创建一个包含声明(claims)的JWT。声明是有关实体(通常是用户)和其他数据的陈述。
服务器使用一个密钥或私钥对JWT进行签名。对于对称算法(如HMAC),服务器使用相同的密钥来签名和验证;对于非对称算法(如RSA),则分别使用私钥签名和公钥验证。
3. 客户端保存 JWT
客户端接收到JWT后,通常会将其存储在本地(例如,在浏览器中作为HTTP-only cookie、localStorage 或 sessionStorage 中),以便后续请求时使用。
4. 后续请求
在后续与服务器的通信中,客户端会在每个请求的Authorization头中包含JWT(Bearer
服务器接收到来自客户端的请求后,会从请求头中提取JWT,并对其进行验证(检查签名的有效性、过期时间等)。
5. 验证与处理
如果JWT有效,服务器将处理请求并将响应返回给客户端。
如果JWT无效(例如,签名不匹配或令牌已过期),服务器将拒绝请求,通常返回401 Unauthorized状态码。
6. 登出
当用户选择登出时,客户端应当删除保存的JWT以确保它不再被使用。对于存储在HTTP-only cookies中的JWT,可以通过设置过期时间为过去的时间点来实现登出效果。
加密方式
HMAC: 使用相同的密钥进行签名和验证。适用于服务端到服务端通信,当双方都可以安全地持有相同的密钥时。
RSA: 使用私钥签名,公钥验证。适合于开放系统,因为任何人都可以拥有公钥来验证签名,但只有持有私钥的一方能够生成签名。

浙公网安备 33010602011771号