点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

作者 | 码农小胖哥

来源 | 公众号「码农小胖哥」

针对Java开发者的持续交付完整实施指南 | 内含福利

1.前言

有些时候我们需要在 Spring Boot Servlet Web 应用中声明一些自定义的 Servlet Filter 来处理一些逻辑。比如简单的权限系统、请求头过滤、防止 XSS 攻击等。本篇将讲解如何在 Spring Boot 应用中声明自定义 Servlet Filter 以及定义它们各自的作用域和顺序。

2. 自定义 Filter

可能有人说声明 Servlet Filter 不就是实现 Filter 接口嘛,没有什么好讲的!是的这个没错,但是很多时候我们并不想我们声明的 Filter 作用于全部的请求。甚至当一个请求经过多个 Filter 时需要按照既定的顺序执行。接下来我会一一讲解如何实现以上的功能。

2.1 Filter 的声明

在 Spring Boot 中 只需要声明一个实现 javax.servlet.Filter 接口的 Spring Bean 就可以了。如下:

@Configurationpublic class FilterConfig {@Bean    public Filter requestFilter() {        return (request, response, chain) -> {            //todo your business        };    }@Bean    public Filter responseFilter() {        return (request, response, chain) -> {            //todo your business        };    }
}

非常简单不是吗?但是这种方式无法保证顺序,而且作用于所有的请求,即拦截的 Ant 规则为 /*。所以需要我们改进

2.2 实现 Filter 顺序化

如果需要实现顺序化,可以借助于 Spring 提供的 @Order 注解或者 Ordered 接口。这里有一个坑:如果使用 @Order 注解一定要注解标注到具体的类上。为了方便 JavaConfig 风格的声明。我们可以实现 OrderedFilter 接口,该接口是 Filter 接口和 Ordered 接口的复合体,最终上面的配置如下:

@Configuration
public class FilterConfig {@Beanpublic OrderedFilter responseFilter() {return new ResponseFilter("/foo/*");}@Beanpublic OrderedFilter requestFilter() {return new RequestFilter("/foo/*");}}

Filter 执行的规则是 数字越小越先执行 。跟之前 Bean 实例化的优先级是一致的。

2.3 自定义 Filter 作用域

实现了顺序化之后我们来看看如何实现自定义 Filter 的作用域。我们先说一下思路:

通过 ServletRequest 对象来获取请求的 URI,然后对 URI 进行 ANT 风格匹配,关于 ANT 风格可以参考我的这一篇文章[1]。匹配通过执行具体的逻辑,否则跳过该 Filter 。

这里非常适合抽象一个基类来把该流程固定下来,留一个抽象方法作为函数钩子,只需要继承基类实现该抽象方法钩子就可以了。为了保证顺序执行基类我们依然实现了 OrderedFilter 接口,我们来定义基类:

package cn.felord.springboot.filters;
import org.springframework.util.AntPathMatcher;import org.springframework.util.CollectionUtils;
import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import java.io.IOException;import java.util.Collections;import java.util.LinkedHashSet;import java.util.Optional;import java.util.Set;
/** * The type Abstract filter bean. * * @author Felordcn * @since 11 :19 */public abstract class AbstractFilterBean implements OrderedFilter {    private Set urlPatterns = new LinkedHashSet<>();public AbstractFilterBean(String... urlPatterns) {        Collections.addAll(this.urlPatterns, urlPatterns);    }/**     * 各自逻辑的函数钩子     *     * @param request  the request     * @param response the response     */    public abstract void internalHandler(ServletRequest request, ServletResponse response);@Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {         // 进行ant匹配  true 执行具体的拦截逻辑 false  跳过        if (this.antMatch(request)) {            this.internalHandler(request, response);        }        chain.doFilter(request, response);    }private boolean antMatch(ServletRequest request) {        Set urlPatterns = getUrlPatterns();if (!CollectionUtils.isEmpty(urlPatterns)) {            //进行Ant匹配处理            HttpServletRequest httpServletRequest = (HttpServletRequest) request;            String uri = httpServletRequest.getRequestURI();            Optional any = urlPatterns.stream().filter(s -> {                AntPathMatcher antPathMatcher = new AntPathMatcher();                return antPathMatcher.match(s, uri);            }).findAny();return any.isPresent();        }        // 如果 没有元素 表示全部匹配        return true;    }public Set getUrlPatterns() {        return urlPatterns;    }}

我们来实现一个具体的 Filter 逻辑,打印请求的 URI

@Slf4jpublic class RequestFilter extends AbstractFilterBean {public RequestFilter(String... urlPatterns) {        super(urlPatterns);    }@Override    public void internalHandler(ServletRequest request, ServletResponse response) {        HttpServletRequest httpServletRequest = (HttpServletRequest) request;        log.info("request from {}", httpServletRequest.getRequestURI());    }@Override    public int getOrder() {       // 定义自己的优先级        return 0;    }}

然后定义好其 urlPatterns 并将其注册到 Spring IoC 容器中就行了,如果有多个而且希望按照一定的顺序执行,遵循 2.2 章节 提供的方法就可以了。

3. Spring Boot 的机制

以上方式是我们自己造的轮子。其实 Spring Boot 还提供了 Filter 注册机制来实现顺序执行和声明作用域。我们上面的逻辑可以改为:

package cn.felord.springboot.configuration;
import lombok.extern.slf4j.Slf4j;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;
import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;
/** * 使用 Spring Boot 提供的注册机制 * * @author Felordcn * @since 14:27 **/@Configuration@Slf4jpublic class SpringFilterRegistrationConfig {@Bean    public FilterRegistrationBean responseFilter() {        FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();        registrationBean.setName("responseFilter");        registrationBean.setOrder(2);        registrationBean.setFilter((request, response, chain) -> {            HttpServletResponse servletResponse = (HttpServletResponse) response;            log.info("response status {}", servletResponse.getStatus());            chain.doFilter(request,response);        });        registrationBean.addUrlPatterns("/foo/*");        return registrationBean;    }@Bean    public FilterRegistrationBean requestFilter() {        FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();        registrationBean.setName("requestFilter");        registrationBean.setOrder(1);        registrationBean.setFilter((request, response, chain) -> {            HttpServletRequest httpServletRequest = (HttpServletRequest) request;            log.info("request from {}", httpServletRequest.getRequestURI());            chain.doFilter(request,response);        });        registrationBean.addUrlPatterns("/foo/*");        return registrationBean;    }
}

3.1 要点

  • FilterRegistrationBean 与 Filter 之间是一对一关系。

  • 如果存在多个 FilterRegistrationBean 需要调用其 setName(String name) 为其声明唯一名称,否则只有第一个注册成功的有效。

  • 如果需要保证调用顺序可通过调用其 setOrder(int order) 方法进行设置。

4. 总结

我们在本文中通过自定义和 Spring Boot 提供的两种方式实现了使用自定义 Filter ,虽然 Spring Boot 提供的方式更加方便一些,但是自定义的方式更能体现你对面向对象理解和提高你的抽象能力。希望多多关注,与往常一样。

Spring Boot自定义 Servlet Filter 的两种方式相关推荐

  1. RabbitMQ(六)——Spring boot中消费消息的两种方式

    前言 上一篇博客中,我们只是简单总结了Spring boot中整合RabbitMQ的操作,针对消息消费的两种方式只是简单给了一个实例,这篇博客,我们进一步总结关于Spring boot消息消费的相关功 ...

  2. Spring Boot 中密码加密的两种姿势!

    先说一句:密码是无法解密的.大家也不要再问松哥微人事项目中的密码怎么解密了! 密码无法解密,还是为了确保系统安全.今天松哥就来和大家聊一聊,密码要如何处理,才能在最大程度上确保我们的系统安全. 本文是 ...

  3. Spring加载properties文件的两种方式

    2019独角兽企业重金招聘Python工程师标准>>> 在项目中如果有些参数经常需要修改,或者后期可能需要修改,那我们最好把这些参数放到properties文件中,源代码中读取pro ...

  4. Spring Boot整合Servlet,Filter,Listener,访问静态资源

    目录 Spring Boot整合Servlet(两种方式) 第一种方式(通过注解扫描方式完成Servlet组件的注册): 第二种方式(通过方法完成Servlet组件的注册) Springboot整合F ...

  5. spring中AOP动态代理的两种方式

    AOP动态代理的两种方式 Spring AOP动态代理的方式(spring的AOP默认是JDK Proxy) 浅谈这两种动态代理 JDK的动态代理,需要有实现接口 动态代理--JDK Proxy ⚫ ...

  6. Spring中进行事务管理的两种方式

    1.Spring中事务管理的API 事务是指逻辑上要么全部成功.要么全部失败的一组操作.例如用户A给用户B转账,则用户A账户余额减少.用户B账户增加这两个操作就是一组事务,必须全部成功或失败撤回操作, ...

  7. spring boot controller 初始化_使用 Spring 快速创建 web 应用的两种方式

    介绍 本篇文章主要介绍,如何使用 Spring 开发一个 Web 应用. 我们将研究用 Spring Boot 开发一个 web 应用,并研究用非 Spring Boot 的方法. 我们将主要使用 J ...

  8. Spring Boot 项目鉴权的 4 种方式

    转自:枕边书 链接:https://zhenbianshu.github.io/ 文章介绍了spring-boot中实现通用auth的四种方式,包括 传统AOP.拦截器.参数解析器和过滤器,并提供了对 ...

  9. spring---自定义Filter有两种方式

    文章目录 前言 一.基于注解 二.注册bean 前言 在我们开发中经常需要对请求做一些自定义的过滤处理,如最常见的jwt每次请求进来我们都需要去解析判断token这个时候肯定就需要自定义一个filte ...

最新文章

  1. 用边缘计算为智能制造提速,行业的破局者是他们
  2. 让你的PHP更安全之PHP.ini
  3. [源码和文档分享]基于JAVA EE框架的在线考试系统平台的设计与实现
  4. 阅读代码工具:Visual Studio Code
  5. PowerShell 备份sharepoint站点命令
  6. C#语句——循环语句(for循环与for循环嵌套)
  7. 【Nutch2.3基础教程】集成Nutch/Hadoop/Hbase/Solr构建搜索引擎:安装及运行【集群环境】
  8. java web容器_java-实现一个简单的java Web容器
  9. BAT等大厂十年研发经历,总结了12开发条经验(墙裂推荐)
  10. CentOS 6.2配置NIS主/从服务器
  11. 5款常用的数据可视化工具推荐!
  12. 【OpenCV】-重映射
  13. 决策树——员工离职预测模型搭建
  14. js 中按下键盘事件
  15. HTTP头部解释,HTTP头部详细分析,最全HTTP头部信息
  16. 开源备份工具duplicity支持阿里云OSS后端存储
  17. 【教程】CRX格式插件不能离线安装?本地扩展CRX无法安装怎么办?
  18. abaqus帮助html,ABAQUS/CAE 常 问 界 面 操 作(转自SimWe仿真论坛
  19. c语言:运输公司对用户计算运输费用
  20. 记录一次对学校考试系统的漏洞挖掘

热门文章

  1. libuv 高性能事件驱动库 简介
  2. golang 调用库函数错误 cannot refer to unexported name
  3. linux hexdump命令详解
  4. ClamAV病毒库增加特征码
  5. asp中sub与function的区别
  6. 我的VC++——对话框中显示GIF格式的图片
  7. java设计模式---工厂方法模式
  8. Linux内核文件vmlinux 和压缩后的bzImage文件格式分析
  9. 安装 node_Mac下的Node.js安装教程
  10. phpinfo查看可以解析的后缀_配置nginx,Apache支持pathinfo模式-什么是phpinfo模式