springboot整合jwt和请求拦截,实现利用token做请求安全拦截校验,且实现阻止并发登录(代码片段)

Johnson_9 Johnson_9     2023-04-05     161

关键词:

目录

一、导入依赖

二、编写 jwt 工具类,实现生成 token 和解析 token

三、在登录请求中向redis中添加token信息

1、先注入redis的接口类

2、在登录方法中生成token并插入redis,有效期一天

四、实现请求拦截器

1、编写自定义的请求拦截器

2、实现WebMvcConfigurer接口,重写实现其添加拦截器方法

五、测试总结

1、请求拦截

①正确 token

②错误的token

③空token

④从redis中删掉token

2、阻止并发登录

3、总结


一、导入依赖

导入 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二级缓存

@Autowired
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 // @Component注解一定要加上
public class Interceptor implements HandlerInterceptor 
    // 注入redis
    @Autowired
    StringRedisTemplate redisTemplate;

    // 处理请求之前执行
    @Override
    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;
    

    // 处理请求之后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception 
        System.out.println("处理请求之后执行");
    

2、实现WebMvcConfigurer接口,重写实现其添加拦截器方法

@Component
public class InterceptorConfig  implements WebMvcConfigurer 
    // 注入自定义拦截器
    @Autowired
    private Interceptor interceptor;

    // 重写添加拦截器方法
    @Override
    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,自定义登录验证接口,并精准有效阻止并发登录

springboot整合jwt实现登录认证

参考技术A1、JWT的构成-头部(header):描述该JWT的最基本的信息,如类型以及签名所用的算法。-负载(payload):存放有效信息的地方。-签证(signature):base64加密后的header、base64加密后的payload和密钥secret加密后组成。2、整合JWT2.1引入JWT依... 查看详情

jwt整合springboot(代码片段)

文章目录JWT整合Springboot1.基于JWT认证1.1认证流程1.2JWT优势1.3JWT的结构是什么2.使用JWT3.整合SpringbootJWT整合Springboot1.基于JWT认证1.1认证流程首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HT... 查看详情

springboot整合jwt

参考技术A参考网址:https://www.jb51.net/article/153145.htmJSONWebtoken简称JWT,是用于对应用程序上的用户进行身份验证的标记。也就是说,使用JWTS的应用程序不再需要保存有关其用户的cookie或其他session数据。此特性便于可伸缩性,同时保证... 查看详情

springboot2.0+shiro+jwt整合(代码片段)

SpringBoot2.0+Shiro+JWT整合JSONWebToken(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。我们利用一定的编码生成Token,并在Token中加入一些非敏感信息,将其传递。安装环境开发工具:... 查看详情

java安全框架-springboot整合jwt

文章目录1.JWT案列使用1.1.pom.xml导入依赖1.2.生成token1.3.验证token1.4.常见的异常2.Springboot整合JWT2.1.封装JWT工具类2.2.导入依赖2.3.appliaction.properties2.4.数据库配置2.5.Dao层2.6.UserDaoMapper.xml2.7.Service层2.8.Controller层2.9使用拦截器进行优化2 查看详情

springboot整合shiro+jwt实现认证及权限校验

序言本文讲解如何使用SpringBoot整合Shiro框架来实现认证及权限校验,但如今的互联网已经成为前后端分离的时代,所以本文在使用SpringBoot整合Shiro框架的时候会联合JWT一起搭配使用。ShiroShiro是apache旗下一个开源框架,... 查看详情

springboot+security+jwt实现单点登录

  本次整合实现的目标:1、SSO单点登录2、基于角色和springsecurity注解的权限控制。  整合过程如下:  1、使用maven构建项目,加入先关依赖,pom.xml如下:<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/... 查看详情

总结关于springsecurity使用jwt和账户密码登录整合在一起的新感悟

(1)jwt登录拦截,需要在账户密码认证之前进行jwt认证,因此jwt拦截需要在UsernamePasswordAuthenticationFilter之前;(2)jwt验证通过则不需要执行账户密码拦截,但是会执行两次jwt拦截,原因不清楚;(3)如果jwt没有通过,将会执行... 查看详情

springsecurity注解鉴权(整合springboot,jwt,redis)

参考技术A该类实现了UserDetails接口内含登录成功或失败后的处理方法用一个简单的类来接受前端出来的登录信息实现UserDetailsService接口,重写loadUserByUsername方法,按自己的实际需求来编写验证规则该类为token校验器,并封装了用... 查看详情

springboot实现一个监听用户请求的拦截器

...个拦截器来监听,并继续相应的日志记录 项目构建与SpringBoot,SpringBoot实现一个拦截器很容易。SpringBoot的核心启动类继承WebMvcConfigurerAdapter//增加拦截器@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){ 查看详情

springboot整合shiro+jwt实现认证及权限校验(代码片段)

序言本文讲解如何使用SpringBoot整合Shiro框架来实现认证及权限校验,但如今的互联网已经成为前后端分离的时代,所以本文在使用SpringBoot整合Shiro框架的时候会联合JWT一起搭配使用。ShiroShiro是apache旗下一个开源框架,... 查看详情

springboot整合拦截器

过滤器和监听器都属于Servlet的api,还可以使用Spring提供的拦截器(HandlerInterceptor)进行改更精细的控制。  查看详情

5分钟springboot极速整合jwt生成token,一篇文章带你快速了解原理并使用(代码片段)

SpringBoot整合jwt登录功能的实现实现原理Session、Cookie、Token的使用状况实践测试生成Token给客户端pojoJwtUtilController解决跨域问题Vue验证客户端存储Tokenrouter.jsError.vueJwtUtil.javaUserController.java运行效果专业解读登录功能的实现登陆页面... 查看详情

springboot集成jwt实现token登录验证(代码片段)

...?1.2JWT主要使用场景1.3JWT请求流程1.4JWT结构二,SpringBoot集成JWT具体实现过程2.1添加相关依赖2.2自定义跳出拦截器的注解2.3自定义全局统一返回值方法,异常类及相关枚举2.4编写JWT工具类,用于生成Token令牌2.5编写... 查看详情

前后端分离学习笔记---[跨域问题,jwt,路由守卫,axios设置请求拦截和响应拦截](代码片段)

...配置拦截路由守卫Axios设置前端请求拦截和响应拦截基础整合一下登录案例跨域问题跨域是指从一个域名的网页去请求另一个域名的资源。浏览器对JavaScript具有默认的安全限制,同源处理策略;同源:协议,域名,端口都相... 查看详情

前后端分离学习笔记---[跨域问题,jwt,路由守卫,axios设置请求拦截和响应拦截](代码片段)

...配置拦截路由守卫Axios设置前端请求拦截和响应拦截基础整合一下登录案例跨域问题跨域是指从一个域名的网页去请求另一个域名的资源。浏览器对JavaScript具有默认的安全限制,同源处理策略;同源:协议,域名,端口都相... 查看详情

springboot第三章springboot和web组件(代码片段)

第三章SpringBoot和web组件三个组件:拦截器、Servlet、Filter3.1SpringBoot中拦截器拦截器是SpringMVC中一种对象,能拦截器对Controller的请求。拦截器框架中有系统的拦截器,还可以自定义拦截器,实现对请求预先处理。实... 查看详情

深入总结springboot整合jwt,这应该是全网讲的最通俗易懂的了(代码片段)

...,以确保信息是可验证和可信任的。今天我们来深入总结SpringBoot整合JWT前言我们都知道JWT是一个JSON信息传输的开放标准,它可以使用密钥对信息进行数字签名,以确保信息是可验证和可信任的。今天我们来深入总结SpringBoot整合... 查看详情