SpringBoot集成JWT实现token验证
一、简介
Jwt
全称是:json web token
。它将用户信息加密到token
里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token
的正确性,只要正确即通过验证。
优点
简洁: 可以通过URL、POST参数或者在HTTP header发送,因为数据量小,传输速度也很快;
自包含:负载中可以包含用户所需要的信息,避免了多次查询数据库;
因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持;
不需要在服务端保存会话信息,特别适用于分布式微服务。
缺点
- 无法作废已颁布的令牌;
- 不易应对数据过期。
二、Jwt
消息构成
1.1
一个token
分3部分,按顺序为
- 头部(
header
) - 载荷(
payload
) - 签证(
signature
)
三部分之间用.
号做分隔。例如:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9. eyJhdWQiOiIxIiwiZXhwIjoxNjQ0MzgxMDI4fQ. 87nwS8ENDOu6RY-4PTLBBzXfDv6-5TiQLQhBXrYGb700
三、Spring Boot
和Jwt
集成示例
3.1导入依赖:
<!-- JWT --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency>
3.2代码结构:
3.3Token生成(TokenUtils )
package com.xxxx.demo.utils; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.xxxx.demo.entity.User; import com.xxxx.demo.service.IUserService; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Date; /** * @ClassName TokenUtils * @Description 整合JWT生成Token * @Author Lishipu * @Date 2023/1/20 16:47 * Version 1.0 **/ @Component public class TokenUtils { private static IUserService staticUserService; @Resource private IUserService userService; @PostConstruct public void setUserService() { staticUserService = userService; } /* * @Author lishipu * @Description 生成Token * @Date 16:50 2023/1/20 * @Param * @return **/ public static String genToken(String userId, String sign) { return JWT.create().withAudience(userId) // 将 user id 保存到 token 里面,作为载荷 .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小时后token过期 .sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥 } }
3.4、拦截器拦截token(JwtInterceptor )
package com.xxxx.demo.config.interceptor; import cn.hutool.core.util.StrUtil; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.JWTVerificationException; import com.xxxx.demo.common.Constants; import com.xxxx.demo.entity.User; import com.xxxx.demo.exception.ServiceException; import com.xxxx.demo.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @ClassName JwtInterceptor * @Description JWT拦截器 * @Author Lishipu * @Date 2023/1/20 17:01 * Version 1.0 **/ public class JwtInterceptor implements HandlerInterceptor { @Autowired private IUserService 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 中的 user id 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; } }
3.5、注册拦截器(InterceptorConfig)
package com.xxxx.demo.config; import com.xxxx.demo.config.interceptor.JwtInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @ClassName InterceptorConfig * @Description Web拦截器 * @Author Lishipu * @Date 2023/1/20 17:08 * Version 1.0 **/ @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor()) .addPathPatterns("/**") // 拦截所有请求,通过判断token是否合法来决定是否需要登录 .excludePathPatterns("/user/login", "/user/register", "/**/export", "/**/import","/file/**");//排除这些,这些不用验证,直接登录 } @Bean public JwtInterceptor jwtInterceptor() { return new JwtInterceptor(); } }
3.6改造登录接口,登录的时候生成token
@Override public UserDTO login(UserDTO userDTO) { User one = getUserInfo(userDTO); if (one != null) { BeanUtil.copyProperties(one, userDTO, true); // 设置token String token = TokenUtils.genToken(one.getId().toString(), one.getPassword()); userDTO.setToken(token); return userDTO; } else { throw new ServiceException(Constants.CODE_600, "用户名或密码错误"); } }
String token = TokenUtils.genToken(one.getId().toString(), one.getPassword());
userDTO.setToken(token);
这是登录的时候用到的一个实体类:
3.7改造
let user = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : null if (user) { config.headers['token'] = user.token; // 设置请求头 } // 当权限验证不通过的时候给出提示 if (res.code === '401') { ElementUI.Message({ message: res.msg, type: 'error' }); }