JWT 认证
JSON Web Token
介绍
json web token (jwt) 是一个开放标准(rfc 7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON 对象安全地 传输信息。此信息可以验证和信任,因为他是数字签名的。jwt 可以使用秘钥(使用HMAC算法)或使用 RSA 或 ECDSA 的公钥/私钥对进行签名
通俗解释
JWT 简称 JSON Web Token,也就是通过 JSON 形式作为 Web 应用中的令牌,用于在各方之间安全地将信息作为 JSON 对象传输,在数据传输过程中还可以完成数据加密、签名等相关处理。
JWT 的结构是什么
# 1.令牌的组成
- 1.标头(Header)
- 2.有效载荷(Payload)
- 3.签名(Signatrue)
- 因此,JWT 通常如下所示:xxxxx.yyyyyy.zzzzzzz Header.Payload.Signatrue
- 例子:
Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
# 1.标头(Header)
- 标头通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,例如 HMACSHA256(写为HS256) 或 RSA。它会使用 Base64 编码组成 JWT 结构的第一部分。
- 注意:Base64 是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。
{
"alg":"HS256", 默认推荐算法
"typ":"JWT"
}
# 2.有效载荷(Payload)
- 令牌的第二部分是有效负载,其中包含声明。声明是有关实体 (通常是用户名) 和其他数据的声明。同样的,它会使用 Base64 编码组成 JWT 结构的第二部分
# 2.签名(Signatrue)
- 前面两部分都是使用 Base64 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload 以及 我们提供的一个密钥(secret),然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过
- 如 HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
三部分的组成图示
最后通过 jwt 组合 token 成图示
使用JWT
依赖 JWT pom 文件
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.3.0</version>
</dependency>
生成 token
Calendar instance = Calendar.getInstance(); //日历类
instance.add(Calendar.SECOND,120000);
//header 有默认的 可以不用设置
String token = JWT.create()
.withClaim("userId", "21") //payload
.withClaim("username", "小黑")
.withExpiresAt(instance.getTime()) //指定令牌的 过期时间
.sign(Algorithm.HMAC256("dbl"));//签名 括号中的秘钥可以随便写 切记不可泄露
System.out.println(token);
- 输出结果eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDgzNTY3MjIsInVzZXJJZCI6IjIxIiwidXNlcm5hbWUiOiLlsI_pu5EifQ.ZSIzSbhYDd3unuXNTg9nrN57cbjA3iwYPi2o-Z7SZEM
根据 令牌 和 签名解析数据
//创建验证对象
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("dbl")).build();
DecodedJWT verify = jwtVerifier.verify(token); //解析token 并可以从当前类中获取存入数据
Claim userId = verify.getClaim("userId"); //获取存入的数据
Claim username = verify.getClaim("username");
System.out.println("过期时间"+verify.getExpiresAt());
System.out.println(userId); //输出
System.out.println(username);
封装工具类
package com.li.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;
// 简单的封装
public class JWTUtils {
private static final String TOKEN = "@BanLi";
//生成 token 三部分
public static String getToken(Map<String,String> map){
//创建jwt builder
JWTCreator.Builder builder = JWT.create();
//payload
map.forEach((k,v)->{
builder.withClaim(k,v);
});
Calendar instance = Calendar.getInstance(); //日历类
instance.add(Calendar.HOUR,2); //有效期是 2个小时
builder.withExpiresAt(instance.getTime()); //指定过期时间
return builder.sign(Algorithm.HMAC256(TOKEN));
}
// 验证token 合法性 是否有异常
public static void verify(String token){
JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token);
}
//这两个方法 实际就是表达一个意思
//获取 payload 自包含的数据
public static DecodedJWT getTokenInfo(String token){
return JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token);
}
}
JWT 最好的方式 是作为拦截器
- 拦截器写法
package com.li.config;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.li.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1、从 请求头 中获取令牌
String token = request.getHeader("token");
//2、校验令牌
try {
DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token); // 令牌校验成功
log.info("令牌中包含的内容:{}",tokenInfo.getClaim("userId").asString());
return true;
} catch (Exception e) {
response.setStatus(404);
return false;
}
}
}
- 注册这个 拦截器 以供全局使用
package com.li.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
@Slf4j
public class WebMvcJwt extends WebMvcConfigurer{
@Autowired
private JWTInterceptor jwtInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
log.info("开启了自定义拦截器");
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**") //拦截全部请求
.excludePathPatterns("/login"); //放行登录
}
}