• SpringBoot-过滤器Filter+JWT令牌实现登录验证


    登录校验-Filter

    分析

    过滤器Filter的快速入门以及使用细节我们已经介绍完了,接下来最后一步,我们需要使用过滤器Filter来完成案例当中的登录校验功能。

    在这里插入图片描述

    我们先来回顾下前面分析过的登录校验的基本流程:

    • 要进入到后台管理系统,我们必须先完成登录操作,此时就需要访问登录接口login。

    • 登录成功之后,我们会在服务端生成一个JWT令牌,并且把JWT令牌返回给前端,前端会将JWT令牌存储下来。

    • 在后续的每一次请求当中,都会将JWT令牌携带到服务端,请求到达服务端之后,要想去访问对应的业务功能,此时我们必须先要校验令牌的有效性。

    • 对于校验令牌的这一块操作,我们使用登录校验的过滤器,在过滤器当中来校验令牌的有效性。如果令牌是无效的,就响应一个错误的信息,也不会再去放行访问对应的资源了。如果令牌存在,并且它是有效的,此时就会放行去访问对应的web资源,执行相应的业务操作。

    大概清楚了在Filter过滤器的实现步骤了,那在正式开发登录校验过滤器之前,我们思考两个问题:

    1. 所有的请求,拦截到了之后,都需要校验令牌吗?

      • 答案:登录请求例外
    2. 拦截到请求后,什么情况下才可以放行,执行业务操作?

      • 答案:有令牌,且令牌校验通过(合法);否则都返回未登录错误结果

    具体流程

    我们要完成登录校验,主要是利用Filter过滤器实现,而Filter过滤器的流程步骤:

    在这里插入图片描述

    基于上面的业务流程,我们分析出具体的操作步骤:

    1. 获取请求url
    2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行
    3. 获取请求头中的令牌(token)
    4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)
    5. 解析token,如果解析失败,返回错误结果(未登录)
    6. 放行

    代码实现

    分析清楚了以上的问题后,我们就参照接口文档来开发登录功能了,登录接口描述如下:

    • 请求参数

      参数格式:application/json

      参数说明:

      名称类型是否必须备注
      usernamestring必须用户名
      passwordstring必须密码

      请求数据样例:

      {
      	"username": "jinyong",
          "password": "123456"
      }
      
      • 1
      • 2
      • 3
      • 4
    • 响应数据

      参数格式:application/json

      参数说明:

      名称类型是否必须默认值备注其他信息
      codenumber必须响应码, 1 成功 ; 0 失败
      msgstring非必须提示信息
      datastring必须返回的数据 , jwt令牌

      响应数据样例:

      {
        "code": 1,
        "msg": "success",
        "data": "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi6YeR5bq4IiwiaWQiOjEsInVzZXJuYW1lIjoiamlueW9uZyIsImV4cCI6MTY2MjIwNzA0OH0.KkUc_CXJZJ8Dd063eImx4H9Ojfrr6XMJ-yVzaWCVZCo"
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 备注说明

      用户登录成功后,系统会自动下发JWT令牌,然后在后续的每次请求中,都需要在请求头header中携带到服务端,请求头的名称为 token ,值为 登录时下发的JWT令牌。

      如果检测到用户未登录,则会返回如下固定错误信息:

      {
      	"code": 0,
      	"msg": "NOT_LOGIN",
      	"data": null
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5

    登录校验过滤器:LoginCheckFilter

    @Slf4j
    @WebFilter(urlPatterns = "/*") //拦截所有请求
    public class LoginCheckFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
            //前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            HttpServletResponse response = (HttpServletResponse) servletResponse;
    
            //1.获取请求url
            String url = request.getRequestURL().toString();
            log.info("请求路径:{}", url); //请求路径:http://localhost:8080/login
    
    
            //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
            if(url.contains("/login")){
                chain.doFilter(request, response);//放行请求
                return;//结束当前方法的执行
            }
    
    
            //3.获取请求头中的令牌(token)
            String token = request.getHeader("token");
            log.info("从请求头中获取的令牌:{}",token);
    
    
            //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
            if(!StringUtils.hasLength(token)){
                log.info("Token不存在");
    
                Result responseResult = Result.error("NOT_LOGIN");
                //把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
                String json = JSONObject.toJSONString(responseResult);
                response.setContentType("application/json;charset=utf-8");
                //响应
                response.getWriter().write(json);
    
                return;
            }
    
            //5.解析token,如果解析失败,返回错误结果(未登录)
            try {
                JwtUtils.parseJWT(token);
            }catch (Exception e){
                log.info("令牌解析失败!");
    
                Result responseResult = Result.error("NOT_LOGIN");
                //把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
                String json = JSONObject.toJSONString(responseResult);
                response.setContentType("application/json;charset=utf-8");
                //响应
                response.getWriter().write(json);
    
                return;
            }
    
    
            //6.放行
            chain.doFilter(request, response);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    JWT导入的maven依赖

    
            <dependency>
                <groupId>io.jsonwebtokengroupId>
                <artifactId>jjwtartifactId>
                <version>0.9.1version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    JWT的工具类

    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import java.util.Date;
    import java.util.Map;
    
    public class JwtUtils {
    
        private static String signKey = "shisan";
        private static Long expire = 43200000L;
    
        /**
         * 生成JWT令牌
         * @param claims JWT第二部分负载 payload 中存储的内容
         * @return
         */
        public static String generateJwt(Map<String, Object> claims){
            String jwt = Jwts.builder()
                    .addClaims(claims)
                    .signWith(SignatureAlgorithm.HS256, signKey)
                    .setExpiration(new Date(System.currentTimeMillis() + expire))
                    .compact();
            return jwt;
        }
    
        /**
         * 解析JWT令牌
         * @param jwt JWT令牌
         * @return JWT第二部分负载 payload 中存储的内容
         */
        public static Claims parseJWT(String jwt){
            Claims claims = Jwts.parser()
                    .setSigningKey(signKey)
                    .parseClaimsJws(jwt)
                    .getBody();
            return claims;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    在上述过滤器的功能实现中,我们使用到了一个第三方json处理的工具包fastjson。我们要想使用,需要引入如下依赖:

    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>fastjsonartifactId>
        <version>1.2.76version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    登录校验的过滤器我们编写完成了,接下来我们就可以重新启动服务来做一个测试:

    测试前先把之前所编写的测试使用的过滤器,暂时注释掉。直接将@WebFilter注解给注释掉即可。

    • 测试1:未登录是否可以访问部门管理页面

      首先关闭浏览器,重新打开浏览器,在地址栏中输入:http://localhost:9528/#/system/dept

      由于用户没有登录,登录校验过滤器返回错误信息,前端页面根据返回的错误信息结果,自动跳转到登录页面了

    在这里插入图片描述

    • 测试2:先进行登录操作,再访问部门管理页面

      登录校验成功之后,可以正常访问相关业务操作页面

      在这里插入图片描述
      总结: 这个过滤器检查请求的URL是否包含 “login”。如果包含,则直接允许请求继续。如果不包含 “login”,则检查JWT令牌的存在和有效性。如果令牌不存在或无效,返回一个JSON格式的 “NOT_LOGIN” 错误响应。如果令牌存在并且有效,则允许请求继续。

  • 相关阅读:
    多门店座号扫码点餐先付后餐公众号小程序开源版开发
    DOM中text节点详解
    IF 22.1,中科院1区TOP,顶级期刊更名!
    暑假算法训练day1
    docker的安装部署nginx和mysql
    APP上架 篇三:ICP备案
    2023lc笔试复盘
    maven打包可执行jar含依赖lib
    Timer实现简单计时控制器,毫秒级误差,浅谈守护线程和用户线程
    GBase8s数据库SET Database Object Mode 语句
  • 原文地址:https://blog.csdn.net/apple_51927114/article/details/134449082