springboot 整合 JWT 和请求拦截,实现 token 做请求安全拦截校验,且实现阻止并发登录
目录
二、编写 jwt 工具类,实现生成 token 和解析 token
2、实现WebMvcConfigurer接口,重写实现其添加拦截器方法
一、导入依赖
导入 jwt 的依赖
-
<!-- jjwt-->
-
<dependency>
-
<groupId>io.jsonwebtoken</groupId>
-
<artifactId>jjwt</artifactId>
-
<version>0.9.1</version>
-
</dependency>
二、编写 jwt 工具类,实现生成 token 和解析 token
jwt 工作流程
可以传入具体的用户信息,方便解析校验
-
// Jwt工具类
-
public class JwtUtil {
-
-
//private static long time = 1000*10; // token 有效期为10秒
-
private static long time = 1000*60*60*24; // token 有效期为一天
-
private static String signature = "admin";
-
-
// 生成token ,三个参数是我实体类的字段,可根据自身需求来传,一般只需要用户id即可
-
public static String createJwtToken(String operNo,String operName ,String organNo){
-
JwtBuilder builder = Jwts.builder();
-
String jwtToken = builder
-
// header
-
.setHeaderParam("typ","JWT")
-
.setHeaderParam("alg","HS256")
-
// payload 载荷
-
.claim("operNo",operNo)
-
.claim("operName",operName)
-
.claim("organNo",organNo)
-
.claim("date",new Date())
-
.setSubject(operNo)
-
.setExpiration(new Date(System.currentTimeMillis() time))
-
.setId(UUID.randomUUID().toString())
-
// signature 签名信息
-
.signWith(SignatureAlgorithm.HS256,signature)
-
// 用.拼接
-
.compact();
-
return jwtToken;
-
}
-
-
// 验证token是否还有效,返回具体内容
-
public static Claims checkToken(String token){
-
if(token == null){
-
return null;
-
}
-
JwtParser parser = Jwts.parser();
-
try {
-
Jws<Claims> claimsJws = parser.setSigningKey(signature).parseClaimsJws(token);
-
Claims claims = claimsJws.getBody();
-
System.out.println(claims.get("operNo"));
-
System.out.println(claims.get("operName"));
-
System.out.println(claims.get("organNo"));
-
System.out.println(claims.getId());
-
System.out.println(claims.getSubject()); // 签名
-
System.out.println(claims.getExpiration()); // 有效期
-
// 如果解析token正常,返回claims
-
return claims;
-
}catch (Exception e) {
-
// 如果解析token抛出异常,返回null
-
return null;
-
}
-
-
}
-
-
}
三、在登录请求中向redis中添加token信息
1、先注入redis的接口类
如果不知道怎么配置redis的可以去看这篇文章 => springboot整合redis并实现mybatis二级缓存
-
-
StringRedisTemplate redisTemplate;
2、在登录方法中生成token并插入redis,有效期一天
redis中key值使用字符串 "operToken" 加上用户 id 拼接而成,value 就是 token 的具体内容
也可以插入一个map,redis的键依旧为字符串 "operToken" 加上用户 id 拼接而成,map中的键为token版本号(可以更好的验证并发登录替换了token,不同的随机数即可),值为token的具体内容
-
// 插入JWT的token
-
String token = JwtUtil.createJwtToken(loginOper.getOperNo(),loginOper.getOperName(),loginOper.getOrganNo());
-
loginOper.setToken(token);
-
// 将JWT的token存入redis,有效期一天
-
redisTemplate.opsForValue().set("operToken" loginOper.getOperNo(),token,1, TimeUnit.DAYS);
四、实现请求拦截器
三个请求拦截器
-
/**前置处理:在业务处理器处理请求之前被调用*/
-
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-
throws Exception;
-
-
/**中置处理:在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView ,现在这个很少使用了*/
-
void postHandle(
-
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
-
throws Exception;
-
-
/**后置处理:在DispatcherServlet完全处理完请求后被调用,可用于清理资源等*/
-
void afterCompletion(
-
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
-
throws Exception;
在请求处理之前,切面的给每个请求做一个校验,校验请求头中的token信息是否有效且信息正确
1、编写自定义的请求拦截器
先取出请求头中token的信息,然后判断这个token是否存在,如果存在再去校验这个token是否真实有效,如果有效再取出token中的用户信息,根据用户id来找出redis中的token,再判断redis中的token是否存在,不存在则说明过期了,如果存在则继续对比请求头中的token是否一致,不一致的话则说明token错误或者被别人并发登录,这里就没有办法判断具体是哪种情况,所以用map集合来存入redis就可以更大程度的判断出(先判断最新版本号的token是否和请求头中的相同,再判断过往版本号的token是否有相同,如果有则说明并发登录,如果没有则token错误。加入map之前需要判断map的大小是否超过一定数值,比如5,超过5则删除之前的数据;对map里键值对单独设置过期时也可以)当然,这还是不够精准
-
// @Component注解一定要加上
-
public class Interceptor implements HandlerInterceptor {
-
// 注入redis
-
-
StringRedisTemplate redisTemplate;
-
-
// 处理请求之前执行
-
-
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-
// 取出请求头中Authorization的信息,就是token内容,接下来就是各种判断
-
String requestToken = request.getHeader("Authorization");
-
if(!StringUtils.isEmpty(requestToken)){
-
Claims claims = JwtUtil.checkToken(request.getHeader("Authorization"));
-
if (claims != null) {
-
String token = redisTemplate.opsForValue().get("operToken" claims.get("operNo"));
-
if(Boolean.TRUE.equals(redisTemplate.hasKey("operToken" claims.get("operNo")))){
-
if(requestToken.equals(token)){
-
// token正确
-
return true;
-
}else {
-
// token错误,判为并发登录,挤下线
-
// 对应的修改响应头的状态,用于前端判断做出相应的策略
-
response.setStatus(411);
-
return false;
-
}
-
}else {
-
// token不存在于redis中,已过期
-
response.setStatus(410);
-
return false;
-
}
-
}
-
// 解析token中的用户信息claims为null
-
response.setStatus(409);
-
return false;
-
}
-
// requestToken为空
-
response.setStatus(409);
-
return false;
-
}
-
-
// 处理请求之后执行
-
-
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
-
System.out.println("处理请求之后执行");
-
}
-
-
}
2、实现WebMvcConfigurer接口,重写实现其添加拦截器方法
-
-
public class InterceptorConfig implements WebMvcConfigurer {
-
// 注入自定义拦截器
-
-
private Interceptor interceptor;
-
-
// 重写添加拦截器方法
-
-
public void addInterceptors(InterceptorRegistry registry){ // InterceptorRegistry 为拦截器注册对象
-
registry.addInterceptor(interceptor) // 注册自定义拦截器
-
.addPathPatterns("/sys/basic-api/**")// 拦截的路径
-
.excludePathPatterns(); // 不拦截的路径
-
}
-
}
五、测试总结
1、请求拦截
给刚刚的自定义拦截器加上输出
然后进行测试
①正确 token
可以发现是先打印输出结果,再执行请求,创建SqlSession
②错误的token
因为是错误的token,token校验是通过不了的,因此返回的用户信息也为空,打印完直接结束,没有执行请求
③空token
打印完直接结束,没有执行请求
④从redis中删掉token
redis中查不到token,判定为token过期
2、阻止并发登录
因为每次生成的token中都有随机id,所以每次登录时生成的token肯定都不一样
所以并发登陆以后,之前登录的用户再一次发送请求就会被验证拦截,根据返回的411状态码来判断账号已在别处登录
3、总结
利用token来实现请求拦截校验是完全没有问题的,但是现实中可以尽可能的多加几层校验,确保足够的安全
token的存储方式还是有很大的改进空间,虽然也能勉强实现阻止多并发登录,但是不够完善,不能精准判断出具体的错误情况,所以由此引出 spring security
传送门 ==> 前后端分离项目整合spring security,自定义登录验证接口,并精准有效阻止并发登录
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgbhaka
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01