jwt
jwt包含三个部分:
1.Header(头部) header说明 令牌类型 和 签名算法:
2.payload(载荷) 保存数据信息;
3.signature(签名) 确保安全用的,把header,payload,加入指定密钥,通过指定的签名算法计算而来;
header和payload用的是Base64编码;
------------------------------
/**
* 生成jwt令牌
*/
@Test
public void testGenJwt(){
Map<String, Object> map=new HashMap<>();
map.put("id",1);
map.put("name","xiangxiang");
// 生成jwt令牌
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, "xiangxiang")//设置签名,第一个参数是header中的签名算法,第二个参数是signature中的密钥
.setClaims(map)//设置自定义内容
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置过期时间1h
.compact();
System.out.println(jwt);
}
-------------------------------
/**
* 解析Jwt令牌
*/
@Test
public void testParseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("xiangxiang")//生成jwt时传入的密钥
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoieGlhbmd4aWFuZyIsImlkIjoxLCJleHAiOjE3NjE1NzIyNzl9.KuLBB8oY7YLTWVDzOJw3WNY9whl3NMbG3C3tIWHmADk")//生成的jwt令牌
.getBody();
System.out.println(claims);
}
--------------------------------
注意:
易错点:
parseClaimsJws(String token) parse Claims JSON Web Signature 解析 签名过的 JWT(JWS)
parseClaimsJwt(String token) parse Claims JSON Web Token 解析 未签名的 JWT(纯 JWT)
两个方法差一个字母,一个是用于签过名的jwt,一个用于没签过名的jwt;
开发中一般定义一个工具类来操作jwt
public class JwtUtils {
/**
* 生成jwt令牌
*/
public static String createJwt(String secret, Map<String, Object> claims,Long expire){
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, secret.getBytes(StandardCharsets.UTF_8))
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
/**
* 解析jwt令牌
*/
public static Claims parseJwt(String token,String secret){
Claims claims = Jwts.parser()
.setSigningKey(secret.getBytes(StandardCharsets.UTF_8))
.parseClaimsJws(token)
.getBody();
return claims;
}
}
--------------------------------
注意点:
1.secret.getBytes(StandardCharsets.UTF_8)是为了让 JWT 使用 UTF-8 编码后的二进制密钥来签名或验证,确保跨平台一致且安全
2.工具类方法要加static关键字,使得可以类名直接调用
-------------------------------------
为什么要转成字节数组?
因为底层 HMAC-SHA256 算法操作的是二进制数据;
setSigningKey() 方法支持多种参数类型:String、byte[]、Key;
用字符串可能因为平台编码不同(如 Windows vs Linux)而导致签名不一致;
所以推荐使用固定编码(UTF-8)来转换,确保跨平台一致性
------------------------------------
下一步要注册拦截器:
@Component//记得添加
@Slf4j
public class JwtTokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if(!(handler instanceof HandlerMethod)){
//当前拦截到的不是动态方法,直接放行
return true;
}
//从请求头上获取令牌
String token = request.getHeader("token");//参数是令牌的名称
//检查令牌是否为空
if(token ==null || token.isEmpty()){
log.warn("未检测到token");
response.setStatus(401);//设置异常状态码
return false;
}
//校验令牌
try {
log.info("校验令牌:{}",token);
Claims claims = JwtUtils.parseJwt(token, "xiangxiang");
//如果校验通过,可以把解析出的用户信息存入 request 域中
request.setAttribute("claims",claims);
log.info("校验通过,用户信息为:{}",claims);
return true;//放行
}catch (Exception e){
log.error("JWT验证失败: {}", e.getMessage());
//不通过,响应401状态码
response.setStatus(401);
return false;//不放行
}
}
}
-----------------------------------------
同时要添加拦截器配置:
@Configuration
@Slf4j
public class WebConfig implements WebMvcConfigurer {
@Autowired
private JwtTokenInterceptor jwtTokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册自定义拦截器...");
registry.addInterceptor(jwtTokenInterceptor)//将拦截器注入进来
.addPathPatterns("/login/**")//添加拦截路径
.excludePathPatterns("/login/login");//添加拦截路径下排除的路径
}
}
------------------------------
细节:
request.setAttribute("claims", claims)
就是在 当前请求对象中临时保存解析出的用户信息,
以便后续的 Controller 能直接通过 request.getAttribute("claims") 获取当前登录用户的身份数据
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Hexo!

