在 Spring Boot 中使用 Filters 实现请求过滤和预处理

 ​

 博客主页:     南来_北往

系列专栏:Spring Boot实战


什么是过滤器

过滤器(Filter)是一种在Web应用中用于拦截和处理HTTP请求和响应的对象

在Java Web开发中,过滤器是实现特定功能,如认证、日志记录和字符编码处理的重要工具。具体如下:

  1. 过滤器的基本概念
    • 定义:过滤器是一种可以动态地拦截传入的请求和传出的响应,并对这些请求和响应进行预处理和后处理的对象。
    • 作用:过滤器可以检查和修改请求头和响应头、处理cookies、验证用户提交的数据等。
  2. 过滤器的工作原理
    • 工作流程:当一个请求到达服务器时,它会首先通过过滤器链,每个过滤器都有机会对请求进行处理。如果请求通过了所有过滤器的检验,它最终会到达目标资源,如servlet或JSP页面。
    • 请求处理:在过滤器中可以通过修改请求对象来实现对请求数据的预处理,例如,可以对输入数据进行解码或者验证。
    • 响应处理:在放行请求后,还可以在过滤器中修改返回给用户的响应数据,例如,对输出数据进行编码或者压缩。
  3. 过滤器的生命周期
    • 初始化init方法在过滤器创建时被调用,用于初始化操作。这个方法只运行一次,一般用来读取配置文件和初始化资源。
    • 过滤请求doFilter方法是实际执行过滤操作的地方,每次请求经过过滤器时都会被调用。在这个方法中可以实现具体的过滤逻辑,并通过FilterChain对象决定是否将请求传递到下一个过滤器或者目标资源。
    • 销毁destroy方法在过滤器销毁之前被调用,用于释放资源。这也是只运行一次的方法,通常用于清理操作。
  4. 过滤器的使用场景
    • 身份验证:用于检查用户是否已经登录,确保只有合法用户才能访问受保护的资源。
    • 日志记录:记录每个请求的信息,如IP地址、访问时间等,有助于网站管理和安全监控。
    • 字符编码处理:解决不同字符集之间的兼容问题,避免出现乱码。
    • 输入数据验证:对用户提交的数据进行格式和有效性验证,防止非法输入导致的安全问题。
  5. 如何配置和使用过滤器
    • 注解方式:通过@WebFilter注解直接在过滤器类上指定拦截路径,这种方法更简单、直观。
    • XML配置:在web.xml文件中配置<filter><filter-mapping>元素,以定义过滤器及其应用场景。

综上所述,过滤器在Web应用中起着至关重要的作用,从请求预处理到响应后处理,都能有效地提高应用的安全性、可用性和用户体验。对于开发人员而言,掌握过滤器的使用是提升Web应用质量的重要手段。

为什么我们需要过滤器

过滤器在Web开发中扮演着至关重要的角色,它们用于增强、控制和修改HTTP请求和响应的处理。具体如下:

  1. 为什么需要过滤器
    • 提高代码重用性:通过使用过滤器可以避免在多个Servlet或JSP页面中重复编写相同的代码,从而简化了代码维护并减少了冗余。
    • 实现全局功能:过滤器可以全局处理所有请求,从而统一实现如字符编码处理、敏感词汇过滤等功能。
    • 增强安全性:通过身份验证和授权,过滤器能够阻止未经授权的用户访问受保护的资源,提升应用的安全性。
    • 提供更好的用户体验:例如,过滤器可以用于网站的统一登录功能,用户只需一次登录即可访问所有受保护的资源,而无需多次认证。
  2. 过滤器的工作原理及生命周期
    • 工作流程:当一个请求到达服务器时,它会首先通过过滤器链,每个过滤器都有机会对请求进行处理。如果请求通过了所有过滤器的检验,它最终会到达目标资源,如servlet或JSP页面。
    • 请求处理:在过滤器中可以通过修改请求对象来实现对请求数据的预处理,例如,对输入数据进行解码或验证。
    • 响应处理:在放行请求后,还可以在过滤器中修改返回给用户的响应数据,例如,对输出数据进行编码或压缩。
    • 生命周期方法:过滤器的生命周期包括initdoFilterdestroy三个方法。init方法在过滤器创建时调用,用于初始化操作;doFilter方法在实际请求处理中被调用;destroy方法在过滤器销毁前调用,用于释放资源。
  3. 过滤器的主要应用场景
    • 字符编码处理:通过过滤器可以统一设置请求和响应的字符编码,避免出现乱码问题。这在处理不同语言环境的应用中尤其重要。
    • 权限验证:过滤器可以检查用户是否已经登录或是否有权访问某个资源。这样,只有合法用户才能访问受保护的资源。
    • 日志记录:过滤器可以记录每个请求的信息,如IP地址、访问时间等,有助于网站管理和安全监控。
    • 输入数据验证:过滤器可以对用户提交的数据进行格式和有效性验证,防止非法输入导致的安全问题。
    • 敏感信息过滤:过滤器可以过滤掉请求和响应中的敏感信息,如SQL注入攻击的字符串,确保数据传输的安全。
  4. 如何在Spring Boot中使用过滤器
    • 创建过滤器类:实现javax.servlet.Filter接口,并覆盖doFilter方法以定义过滤器的逻辑。在doFilter方法中,可以对请求和响应进行处理,并通过调用FilterChain.doFilter()将请求传递给下一个过滤器或目标Servlet。
    • 注册过滤器:使用@WebFilter注解将过滤器类标记为过滤器,并通过urlPatterns属性指定要拦截的URL模式。如果不使用@WebFilter注解,也可以通过在Spring Boot配置类中使用FilterRegistrationBean来注册过滤器。先创建一个FilterRegistrationBean实例,然后设置过滤器实例,添加URL模式,并设置优先级。
  5. 过滤器与拦截器的区别
    • 处理范围:过滤器主要作用于Servlet容器层次,可以拦截所有到达Web应用的请求;而拦截器则作用于Spring MVC框架内,只能拦截由DispatcherServlet管理的请求。
    • 执行顺序:过滤器的执行顺序依赖于它们在web.xml中的配置顺序或通过@WebFilter注解声明的顺序;拦截器的执行顺序则依赖于在Spring配置文件中的声明顺序。
    • 功能实现:过滤器更侧重于请求的预处理和响应的后处理,比如字符编码处理、文件上传处理等;拦截器则更侧重于视图渲染前的控制器执行流程,如权限检查、表单验证等。

综上所述,过滤器在Web应用的开发中起着至关重要的作用。从请求预处理到响应后处理,过滤器都能有效地提高应用的安全性、可用性和用户体验。对于开发人员而言,掌握过滤器的使用是提升Web应用质量的重要手段。

创建过滤器

要在Spring Boot中创建过滤器,您可以实现javax.servlet.Filter接口或扩展OncePerRequestFilter类。OncePerRequestFilter确保您的过滤器每个请求只执行一次。

@RestController

@RequestMapping("/customer")

public class CustomerController {


    @GetMapping

    public String getMessage(){

        return "welcome to filter session";

    }

}


@Component

@Slf4j

public class MessageFilter implements Filter {


    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        log.info("[MessageFilter] - Inside doFilter method");

        log.info("Local Port : " + request.getLocalPort());

        log.info("Server Name : " + request.getServerName());


        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        log.info("Method Name : " + httpServletRequest.getMethod());

        log.info("Request URI : " + httpServletRequest.getRequestURI());

        log.info("Servlet Path : " + httpServletRequest.getServletPath());

        chain.doFilter(request, response);

    }

}

过滤器的顺序

过滤器的应用顺序可以通过@Order注解或

@Component

@Slf4j

@Order(2)

public class MessageFilter implements Filter {


    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        log.info("[MessageFilter] - Inside doFilter method");

        log.info("Local Port : " + request.getLocalPort());

        log.info("Server Name : " + request.getServerName());


        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        log.info("Method Name : " + httpServletRequest.getMethod());

        log.info("Request URI : " + httpServletRequest.getRequestURI());

        log.info("Servlet Path : " + httpServletRequest.getServletPath());

        chain.doFilter(request, response);

    }

}


@Component

@Slf4j

@Order(1)

public class ProductFilter implements Filter {


    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        log.info("[ProductFilter] - Inside doFilter method");

        log.info("Local Port : " + request.getLocalPort());

        log.info("Server Name : " + request.getServerName());


        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        log.info("Method Name : " + httpServletRequest.getMethod());

        log.info("Request URI : " + httpServletRequest.getRequestURI());

        log.info("Servlet Path : " + httpServletRequest.getServletPath());

        chain.doFilter(request, response);

    }

}

注意:如果您只想打印MessageFilter并跳过ProductFilter

要在访问/customer API时仅显示MessageFilter的日志,您需要有条件地绕过ProductFilter/customer端点的过滤。您可以通过修改ProductFilter来跳过特定端点的过滤来实现这一点。

@Component

@Slf4j

@Order(1) // 确保此过滤器在MessageFilter之前执行

public class ProductFilter implements Filter {


    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        String requestURI = httpServletRequest.getRequestURI();


        if ("/customer".equals(requestURI)) {

            chain.doFilter(request, response);

            return; // 跳过过滤器的其余部分

        }


        log.info("[ProductFilter] - Inside doFilter method");

        log.info("Local Port : " + request.getLocalPort());

        log.info("Server Name : " + request.getServerName());

        log.info("Method Name : " + httpServletRequest.getMethod());

        log.info("Request URI : " + requestURI);

        log.info("Servlet Path : " + httpServletRequest.getServletPath());


        chain.doFilter(request, response);

    }

}

 注册过滤器

@Configuration

public class FilterConfig {


    @Bean

    public FilterRegistrationBean<MessageFilter> loggingFilter(){

        FilterRegistrationBean<MessageFilter> registrationBean = new FilterRegistrationBean<>();


        registrationBean.setFilter(new MessageFilter());

        registrationBean.addUrlPatterns("/customer/*"); // 如果需要,指定URL模式


        return registrationBean;

    }

}

 OncePerRequestFilter

public class ProductFilter extends OncePerRequestFilter {


    @Override

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        log.info("[ProductFilter] - Inside doFilter method");

        log.info("Local Port : " + request.getLocalPort());

        log.info("Server Name : " + request.getServerName());


        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        log.info("Method Name : " + httpServletRequest.getMethod());

        log.info("Request URI : " + httpServletRequest.getRequestURI());

        log.info("Servlet Path : " + httpServletRequest.getServletPath());

        filterChain.doFilter(request, response);

    }

}

简单案例

1、建一个新的Java类,并实现javax.servlet.Filter接口。例如,创建一个名为MyFilter的类:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化代码(可选)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        // 在这里可以对请求进行预处理操作,例如检查请求头、参数等

        // 继续执行过滤器链中的下一个过滤器或目标资源
        chain.doFilter(request, response);

        // 在这里可以对响应进行后处理操作,例如修改响应头、内容等
    }

    @Override
    public void destroy() {
        // 销毁代码(可选)
    }
}

2、在Spring Boot应用程序的配置类上添加@Configuration注解,并在该类中定义一个方法,该方法返回一个带有@Bean注解的FilterRegistrationBean实例。这将注册你的过滤器并将其添加到过滤器链中。

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<MyFilter> myFilter() {
        FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new MyFilter());
        registrationBean.addUrlPatterns("/api/*"); // 设置过滤器应用于哪些URL模式
        registrationBean.setOrder(1); // 设置过滤器的顺序(数字越小,优先级越高)
        return registrationBean;
    }
}

在上面的示例中,我们为MyFilter设置了URL模式/api/*,这意味着它将应用于所有以/api/开头的请求。你可以根据需要调整URL模式。

3、现在,每当有匹配的请求到达时,MyFilter就会被调用,你可以在doFilter方法中编写自定义的请求过滤和预处理逻辑。同样地,你也可以在响应返回给客户端之前对其进行后处理。

这样,你就成功地在Spring Boot应用程序中实现了请求过滤和预处理功能。记得根据你的需求调整过滤器的逻辑和配置。

  • 25
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Spring Boot 使用 shiro 配置自定义过滤器需要以下几个步骤: 1. 引入 shiro-spring-boot-starter 依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> ``` 2. 创建自定义过滤器: ``` public class CustomFilter extends AccessControlFilter { @Override protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception { // 在这里实现自定义的过滤逻辑,返回 true 表示通过过滤器,返回 false 表示未通过过滤器 return true; } @Override protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { // 如果 isAccessAllowed 返回 false,则会进入到这里,可以在这里处理未通过过滤器的情况 return false; } } ``` 3. 配置 shiro 的 FilterChainDefinition: ``` @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); // 添加自定义过滤器,其 key 是过滤器名称,value 是该过滤器对应的路径 chainDefinition.addPathDefinition("/custom/**", "custom"); return chainDefinition; } ``` 4. 配置自定义过滤器: ``` @Bean("custom") public CustomFilter customFilter() { return new CustomFilter(); } ``` 5. 配置 shiro 的注解支持: ``` @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } ``` 完成以上步骤后,就可以在 Spring Boot 使用 shiro 配置自定义过滤器了。 ### 回答2: 在 Spring Boot 使用 Shiro 配置自定义过滤器分为三个步骤。 第一步,创建自定义过滤器类。可以通过实现 Shiro 的 Filter 接口来创建自定义过滤器。在自定义过滤需要实现过滤规则,并对请求进行相应的处理。 第二步,配置 Shiro 过滤器链。在 Spring Boot 的配置类,通过创建 ShiroFilterFactoryBean 对象来配置 Shiro 的过滤器链。可以使用 Shiro 的 FilterChainDefinitionMap 对象来配置过滤器链,然后将该对象设置给 ShiroFilterFactoryBean。 第三步,启用 Shiro 过滤器。在 Spring Boot 的配置类,通过创建 DefaultFilterChainManager 对象,并将该对象设置给 ShiroFilterFactoryBean,启用自定义过滤器。 有了以上三步,就可以在 Spring Boot 使用 Shiro 配置自定义过滤器了。可以通过在自定义过滤实现过滤规则来对请求进行拦截或处理,然后在 Shiro 过滤器链配置该过滤器,最后启用该过滤器。这样就可以实现请求的自定义过滤器处理。 值得注意的是,在使用 Shiro 进行自定义过滤器配置时,需要保证 Shiro 的配置文件已经进行了相应的配置,包括认证和授权等相关配置。只有在正确配置的前提下,才能正确使用 Shiro 进行自定义过滤器的配置。 ### 回答3: 在Spring Boot使用Shiro配置自定义过滤器通常需要以下几个步骤: 1. 引入Shiro和Spring Boot依赖。在pom.xml文件添加Shiro和Spring Boot Starter依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` 2. 创建自定义过滤器类。可以通过实现`javax.servlet.Filter`接口或者继承`org.apache.shiro.web.servlet.OncePerRequestFilter`类来创建自定义过滤器。例如,创建一个名为`CustomFilter`的自定义过滤器类: ``` public class CustomFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 过滤器逻辑处理 // ... filterChain.doFilter(request, response); } } ``` 3. 在Shiro配置类注册自定义过滤器。创建一个Shiro配置类,并使用`@Configuration`注解标记为配置类。通过`@Bean`注解将自定义过滤器注册到Shiro的过滤器链。例如,在配置类`ShiroConfig`注册`CustomFilter`: ``` @Configuration public class ShiroConfig { @Bean public FilterRegistrationBean<CustomFilter> customFilterRegistrationBean() { FilterRegistrationBean<CustomFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new CustomFilter()); registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 过滤器执行顺序 registrationBean.addUrlPatterns("/*"); // 过滤器路径 return registrationBean; } } ``` 4. 配置Shiro的过滤规则。在Shiro配置文件,可以设置自定义过滤器的拦截规则。例如,在`shiro.ini`配置文件,设置自定义过滤器的拦截规则: ``` [urls] /** = customFilter // 对所有请求使用自定义过滤器 ``` 通过以上步骤,在Spring Boot使用Shiro配置自定义过滤器就可以实现对特定请求的拦截和处理。在`CustomFilter`类的`doFilterInternal`方法编写自定义的过滤器逻辑,例如鉴权、权限验证等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值