JWT身份验证的流程
-
用户登录: 用户向服务器提供他们的用户名和密码。
-
服务器验证:服务器接收到请求,验证用户名和密码。
-
生成JWT:如果用户名和密码验证通过,服务器将创建一个 JWT。 JWT 包含了一些数据(称为声明),例如用户 ID、用户名、令牌过期时间等。然后,服务器将对 JWT 进行签名,并将其发送回用户。
-
用户存储JWT:用户接收到 JWT,并将其存储在某个位置,例如Web浏览器的localStorage中。
-
发送带有JWT的请求:之后,每当用户向服务器发送请求(例如获取数据)时,他们都将在请求头的 Authorization 字段中包含 JWT。
-
服务器验证JWT:服务器接收到用户的请求,并从 Authorization 头中提取 JWT。然后,服务器验证该 JWT:它会检查 JWT 是否已经过期,验证签名是否有效,还可能验证一些其他的声明。如果 JWT 有效,服务器就知道这个请求是合法的,并继续处理请求。
-
返回响应:一旦服务器处理完用户的请求,它就会将响应发送回用户。
-
刷新JWT:如果 JWT 过期了,服务器可能会返回一个新的 JWT 给用户,或者让用户重新登陆,以便他们可以继续发送合法的请求。
JWT的安全性
如果你改变了 JWT 中的任何部分(包括头部、负载或签名),然后尝试验证它,你可能会遇到几个问题:
-
签名错误:JWT 的签名是根据头部和负载计算得出的。如果你修改了头部或负载的任何部分,那么签名就不再有效,JWT 的验证将会失败。这是因为签名是为了确保 JWT 的头部和负载没有被篡改。
-
格式错误:如果你修改了 JWT 的格式(例如,删除了某个部分,或者改变了部分之间的点号分隔符),那么 JWT 可能无法被正确解析,这将导致错误。
-
负载数据错误:如果你修改了负载中的数据(这是 JWT 中包含的实际信息,如用户 ID、过期时间等),那么这些数据可能无法被正确解析,或者可能导致验证错误(例如,如果你把过期时间改为了过去的时间,那么 JWT 将被视为已经过期)。
SpringBoot中使用JWT
添加maven依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
package xin.students.springbootpro;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.UUID;
@SpringBootTest
class SpringBootProApplicationTests {
// 设置 token 过期时间为24小时
private long time = 1000 * 60 * 60 * 24;
// 使用 HS256 算法生成一个安全的密钥
private SecretKey signature = Keys.secretKeyFor(SignatureAlgorithm.HS256);
@Test
public void createToken() {
// 构建一个 JWT Builder
JwtBuilder jwtBuilder = Jwts.builder();
// 生成 JWT Token
String jwtToken = jwtBuilder
.setHeaderParam("typ", "JWT") // 设置 JWT 类型
.setHeaderParam("alg", "HS256") // 设置签名算法
.claim("username", "tom") // 添加 username 到载荷中(自定义的key:value)
.claim("role", "admin") // 添加 role 到载荷中(自定义的key:value)
.setSubject("admin-test") // 设置主题
.setExpiration(new Date(System.currentTimeMillis() + time)) // 设置过期时间
.setId(UUID.randomUUID().toString()) // 设置 JWT ID,通常是随机生成的
.signWith(signature) // 签名 JWT,用以验证其完整性
.compact(); // 压缩 JWT 到一个 compact, URL-safe string
System.out.println(jwtToken);
System.out.println("下面是解密后的原文------------------------------------");
// 解析生成的 token
parseToken(jwtToken);
}
public void parseToken(String token) {
// 创建一个 JWT 解析器
JwtParser jwtParser = Jwts.parserBuilder()
.setSigningKey(signature)
.build();
// 解析 token
Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token);
// 获取载荷
Claims claims = claimsJws.getBody();
// 打印载荷中的各种信息
System.out.println(claims.get("username")); // 打印 username对应的值
System.out.println(claims.get("role")); // 打印 role对应的值
System.out.println(claims.getId()); // 打印 JWT ID
System.out.println(claims.getSubject()); // 打印主题
System.out.println(claims.getExpiration()); // 打印过期时间
}
}