1、 JWT简介
- 什么是JWT?
- JWT(JSON Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。
- 它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证;应用场景如用户登录。
- 为什么使用JWT?
随着技术的发展,分布式web应用的普及,通过session管理用户登录状态成本越来越高,因此慢慢发展成为token的方式做登录身份校验,然后通过token去取redis中的缓存的用户信息,随着之后jwt的出现,校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录更为简单。
- 传统Cookie+Session与JWT对比
① 在传统的用户登录认证中,因为http是无状态的,所以都是采用session方式。用户登录成功,服务端会保证一个session,当然会给客户端一个sessionId,客户端会把sessionId保存在cookie中,每次请求都会携带这个sessionId。
cookie+session这种模式通常是保存在内存中,而且服务从单服务到多服务会面临的session共享问题,随着用户量的增多,开销就会越大。而JWT不是这样的,只需要服务端生成token,客户端保存这个token,每次请求携带这个token,服务端认证解析就可。
② JWT方式校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录,验证token更为简单。
- JWT的组成(3部分)
第一部分为头部(header),第二部分我们称其为载荷(payload),第三部分是签证(signature)。【中间用 . 分隔】
一个标准的JWT生成的token格式如下:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI1IiwiaWF0IjoxNTY1NTk3MDUzLCJleHAiOjE1NjU2MDA2NTN9.qesdk6aeFEcNafw5WFm-TwZltGWb1Xs6oBEk5QdaLzlHxDM73IOyeKPF_iN1bLvDAlB7UnSu-Z-Zsgl_dIlPiw
2、SpringBoot整合JWT
- 导入依赖
<!--JWT-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.2.1</version>
</dependency>
- JwtInterceptor.java
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("token");
//如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)){
return true;
}
//执行认证
if (StrUtil.isBlank(token)){
throw new ServiceException(Constants.CODE_401,"无token,请重新登录!");
}
//获取token中的userId
String userId;
try{
userId= JWT.decode(token).getAudience().get(0);
}catch (JWTDecodeException j){
throw new ServiceException(Constants.CODE_401,"token验证失败,请重新登录!");
}
//根据token中的userId查询数据库
User user=userService.getById(userId);
if (user==null){
throw new ServiceException(Constants.CODE_401,"用户不存在,请重新登录!");
}
//用户密码加签验证token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try{
jwtVerifier.verify(token); //验证token
}catch (JWTVerificationException e){
throw new ServiceException(Constants.CODE_401,"token验证失败,请重新登录!");
}
return true;
}
}
- InterceptorConfig.java
@Configuration
public class InterceptorConfig implements WebMvcConfigurer{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/user/login","/user/register","/**/export","/**/import"); //拦截除登录注册、导入导出以外请求,通过判断token是否合法来决定是否需要登录
}
@Bean
public JwtInterceptor jwtInterceptor(){
return new JwtInterceptor();
}
}
- 设置拦截器,需要拦截的路径等
- TokenUtils.java
@Component
public class TokenUtils {
private static UserService staticUserService;
@Autowired
private UserService userService;
@PostConstruct
public void setUserService(){
staticUserService=userService;
}
public static String genToken(String userId,String sign){
return JWT.create().withAudience(userId) //将userId 保存到token里面,作为载荷
.withExpiresAt(DateUtil.offsetHour(new Date(),2))//2小时后token过期
.sign(Algorithm.HMAC256(sign)); //以sign作为token的密钥
}
//获取当前登录的用户信息
public static User getCurrentUser(){
try{
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader("token");
if (StrUtil.isNotBlank(token)){
String userId = JWT.decode(token).getAudience().get(0);
return staticUserService.getById(Integer.valueOf(userId));
}
}catch (Exception e){
return null;
}
return null;
}
}
这样就在SpringBoot中整合好了JWT
3、前端测试接口
- 和之前登录相比,需要改造一下,因为现在是会生成一个token登录
request.js
import axios from 'axios'
import ElementUI from 'element-ui';
const request = axios.create({
baseURL: 'http://localhost:8081',
timeout: 5000
})
// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
let user = localStorage.getItem("user")? JSON.parse(localStorage.getItem("user")):null
if(user){
config.headers['token'] = user.token; // 设置请求头
}
return config
}, error => {
return Promise.reject(error)
});
// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
response => {
let res = response.data;
// 如果是返回的文件
if (response.config.responseType === 'blob') {
return res
}
// 兼容服务端返回的字符串数据
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res
}
//当权限验证不通过的时候给出提示
if(res.code==='401'){
ElementUI.Message({
message: res.msg,
type:'error'
});
}
return res;
},
error => {
console.log('err' + error) // for debug
return Promise.reject(error)
}
)
export default request
- 测试登录
- 未登录时直接进入首页
获取不到用户数据,并会弹出 无token,请重新登录
- 登录账号后,进入首页
可以拿到数据,并且我们打开检查,看network里,刚才发送的请求里面,header中是有token的
退出登录或者删除token,便查不到数据,只有token匹配成功才能拿到完整数据。这就是用JWT做的登录和拦截。