美文网首页
Spring Boot-Filter过滤器使用

Spring Boot-Filter过滤器使用

作者: 石头耳东 | 来源:发表于2022-04-19 22:22 被阅读0次

    零、本文纲要

    • 一、Filter作用
    • 二、Filter使用
      1、基础准备
      2、编写Filter
      3、扫描Filter
      4、测试
    • 三、使用总结
    • 补充:完整Filter实现类代码

    一、Filter作用

    • ① 权限控制;

    • ② 对request、response拦截处理;

    • ③ 公共代码提取。

    二、Filter使用

    1、基础准备

    • ① 引入依赖
    <!--spring_boot-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <scope>compile</scope>
    </dependency>
    
    • ② 编写配置文件
    server:
      port: 8080
    
    • ③ 编写启动类
    @Slf4j // lombok 提供的日志注解,方便直接使用 log 输出指定日志
    @SpringBootApplication
    public class TemplateApplication {
        public static void main(String[] args) {
            SpringApplication.run(ReggieApplication.class, args);
            log.info("项目启动成功!");
        }
    }
    

    2、编写Filter

    • 基础:Filter接口
    public interface Filter {
        default void init(FilterConfig filterConfig) throws ServletException {
        }
    
        void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; // 此处关注此方法
    
        default void destroy() {
        }
    }
    

    此处我们重点关注doFilter方法,编写实现类重写该方法。

    • ① 编写登录过滤器

    Ⅰ 在自定义Filter实现类上使用@WebFilter注解;

    Ⅱ 自定义filterName(随意,不要与其他Filter重复即可);

    Ⅲ 定义urlPatterns此处设置为"/*",表示拦截所有请求;

    /**
     * 检查用户是否已经登录的过滤器
     */
    @Slf4j
    @WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
    public class LoginCheckFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
                throws IOException, ServletException {
                ... ...
        }
    }
    
    • ② 编写详细方法体

    Ⅰ 将传入的requestresponse对象转换为Http类型

    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    

    Ⅱ 自定义不需要处理的URI数组

    // 定义不需要处理的请求路径
    String[] urls = new String[]{
            "/employee/login", // 员工登录
            "/employee/logout", // 员工登出
            "/backend/**", // 管理后台静态资源
            "/front/**", // 移动端静态资源
            "/common/**", // 通用部分
            "/user/sendMsg", // 移动端短信验证码
            "/user/login" // 移动端登录
    };
    

    Ⅲ 获取请求URI

    // A. 获取本次请求的URI
    String requestURI = request.getRequestURI();
    

    Ⅳ 判断本次请求

    // B. 判断本次请求, 是否需要登录, 才可以访问
    boolean check = checkURI(urls, requestURI);
    

    封装checkURI方法,如下:

    a、注入PATH_MATCHER,用于路径比较,如下:

    // Spring 框架提供的用于路径比较的类:路径匹配器,支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    

    b、方法体,如下:

    /**
     * 路径匹配,检查本次请求是否需要放行
     * @param urls 需放行的 urls
     * @param requestURI 请求进来的 URI
     * @return 放回结果
     */
    public boolean checkURI(String[] urls, String requestURI){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match) return true;
        }
        return false;
    }
    

    Ⅴ 不需要拦截,则直接放行

    // C. 如果不需要拦截,则直接放行
    if (check){
        log.info("本次{}请求不需要处理!", requestURI);
        filterChain.doFilter(request, response);
        return;
    }
    

    Ⅵ 其余路径,判断登录状态

    a、已登录,则放行

    BaseContext是自定义存储线程userId的类

    // D. 判断登录状态,如果已登录,则直接放行
    // 【EMPLOYEE】:用于员工登录判断
    Long empId = (Long) request.getSession().getAttribute(CUR_EMPLOYEE);
    if (empId != null){
        log.info("用户已登录,用户ID为:{}", empId);
        // 将 empId 存入 ThreadLocal
        BaseContext.setCurrentId(empId);
    
        filterChain.doFilter(request, response);
        return;
    }
    // 【USER】:用于用户登录判断
    Long userId = (Long) request.getSession().getAttribute(CUR_USER);
    if (userId != null){
        log.info("用户已登录,用户ID为:{}", userId);
        // 将 userId 存入 ThreadLocal
        BaseContext.setCurrentId(userId);
    
        filterChain.doFilter(request, response);
        return;
    }
    

    b、未登录,拦截

    自定义常量

    public static final String NOT_LOGIN = "NOTLOGIN"; // LoginCheckFilter 拦截器使用
    

    拦截,此处使用response向前端返回响应数据R.error(NOT_LOGIN)

    // E. 如果未登录, 则返回未登录结果
    // 通过输出流方式,向客户端页面响应数据
    log.info("用户未登录!");
    response.getWriter().write(JSON.toJSONString(R.error(NOT_LOGIN)));
    

    3、扫描Filter

    在启动类上添加@ServletComponentScan注解,用于扫描 @WebFilter 注解,如下:

    @Slf4j // lombok 提供的日志注解,方便直接使用 log 输出指定日志
    @SpringBootApplication
    @ServletComponentScan // 扫描 @WebFilter 注解
    public class ReggieApplication {
        public static void main(String[] args) {
            SpringApplication.run(ReggieApplication.class, args);
            log.info("项目启动成功!");
        }
    }
    

    4、测试

    编写Controller类,启动测试。

    三、使用总结

    • ① 拦截所有请求;
    • ② 自定义放行URI数组;
    • ③ 对比放行URI数组直接放行;
    • ④ 未直接放行部分判断条件,满足放行;
    • ⑤ 不满足,使用response对象返回结果响应。

    补充:完整Filter实现类代码

    /**
     * 检查用户是否已经登录的过滤器
     */
    @Slf4j
    @WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
    public class LoginCheckFilter implements Filter {
    
        // Spring 框架提供的用于路径比较的类:路径匹配器,支持通配符
        public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
                throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            HttpServletResponse response = (HttpServletResponse) servletResponse;
    
            // A. 获取本次请求的URI
            String requestURI = request.getRequestURI();
            log.info("拦截到请求:{}", requestURI);
            // 定义不需要处理的请求路径
            String[] urls = new String[]{
                    "/employee/login",
                    "/employee/logout",
                    "/backend/**",
                    "/front/**",
                    "/common/**",
                    "/user/sendMsg", // 移动端短信验证码
                    "/user/login" // 移动端登录
            };
    
            // B. 判断本次请求, 是否需要登录, 才可以访问
            boolean check = checkURI(urls, requestURI);
    
            // C. 如果不需要拦截,则直接放行
            if (check){
                log.info("本次{}请求不需要处理!", requestURI);
                filterChain.doFilter(request, response);
                return;
            }
    
            // D. 判断登录状态,如果已登录,则直接放行
            // 【EMPLOYEE】:用于员工登录判断
            Long empId = (Long) request.getSession().getAttribute(CUR_EMPLOYEE);
            if (empId != null){
                log.info("用户已登录,用户ID为:{}", empId);
                // 将 empId 存入 ThreadLocal
                BaseContext.setCurrentId(empId);
    
                filterChain.doFilter(request, response);
                return;
            }
            // 【USER】:用于用户登录判断
            Long userId = (Long) request.getSession().getAttribute(CUR_USER);
            if (userId != null){
                log.info("用户已登录,用户ID为:{}", userId);
                // 将 empId 存入 ThreadLocal
                BaseContext.setCurrentId(userId);
    
                filterChain.doFilter(request, response);
                return;
            }
    
            // E. 如果未登录, 则返回未登录结果
            // 通过输出流方式,向客户端页面响应数据
            log.info("用户未登录!");
            response.getWriter().write(JSON.toJSONString(R.error(NOT_LOGIN)));
        }
    
        /**
         * 路径匹配,检查本次请求是否需要放行
         * @param urls 需放行的 urls
         * @param requestURI 请求进来的 URI
         * @return 放回结果
         */
        public boolean checkURI(String[] urls, String requestURI){
            for (String url : urls) {
                boolean match = PATH_MATCHER.match(url, requestURI);
                if (match) return true;
            }
            return false;
        }
    }
    

    四、结尾

    以上即为Filter基础使用的内容,感谢阅读。

    相关文章

      网友评论

          本文标题:Spring Boot-Filter过滤器使用

          本文链接:https://www.haomeiwen.com/subject/xkayertx.html