目录
- 1.概念解释
- 2.DelegatingFilterProxy
- 3.FilterChainProxy和SecurityFilterChain
- 4.自定义过滤器
- 4.1.自定义注册在servlet容器的过滤器
- 4.2.在SecurityFilterChain中注册自定义的过滤器
- 4.3.多个SecurityFilterChain
- 5.小结
目标:
- 了解过滤器
- 尝试自定义过滤器
1.概念解释
Java Web服务器
Java Web服务器是一个能够接收客户端(如浏览器)的HTTP请求,并根据请求调用相应的Java Web应用(如Servlet、JSP等)来生成响应的服务器。这些响应可以是HTML页面、JSON数据、图像或其他类型的文件。常见的Java Web服务器包括Tomcat、Resin、JBoss、WebSphere和WebLogic等。它不仅支持Servlet和JSP等Java技术,还可能提供其他功能,如静态资源服务、安全性、会话管理等。
servlet容器
Servlet容器是Java EE规范中的一个组件,它专门用于运行Servlet和JSP等Java Web应用。Servlet容器提供了Servlet的生命周期管理、请求分发、多线程支持等功能,使得开发者可以专注于业务逻辑的实现,而无需关心底层细节。
Java Web服务器与servlet容器的区别
- Java Web服务器是一个更广泛的概念,它可能包含Servlet容器以及其他功能组件(如静态资源服务器、安全性组件等)。
- Servlet容器则专注于运行Servlet和JSP等Java Web应用,提供了Servlet规范所要求的功能。
Spring与Java Web服务器的集成方式主要有以下几种:
- 基于Servlet的集成:Spring MVC是Spring框架中的一个模块,它提供了基于Servlet的MVC实现。开发者可以将Spring MVC与Java Web服务器(如Tomcat、Jetty等)集成,以构建基于MVC模式的Web应用程序。
- 基于Spring Boot的集成:Spring Boot是Spring框架的一个子项目,它提供了简化的配置和快速的应用程序启动。Spring Boot可以与嵌入式Java Web服务器(如Tomcat、Undertow等)集成,使得开发者可以轻松地构建和运行独立的Web应用程序。
servlet容器与spring容器
- Servlet容器是用于运行Java Servlet的环境。它负责接收HTTP请求,将请求发送给相应的Servlet进行处理,并将处理结果返回给客户端。
- Spring容器是Spring框架提供的用于管理应用程序中的Java对象(Bean)的容器。它使用依赖注入(DI)和控制反转(IOC)等机制,将对象之间的依赖关系从代码中解耦,使得应用程序更加灵活、可维护和可调试。
Servlet容器的工作机制
一个Servlet实例实际上可以处理多个HttpServletRequest和HttpServletResponse。这是因为Servlet容器(如Tomcat)采用了一种多线程的处理机制来应对并发请求。
- 多线程处理:
- 当客户端向服务器发送请求时,Servlet容器会为每个请求创建一个新的线程。
- 这些线程会并发地执行,从而允许服务器同时处理多个请求。
- 请求分发:
- Servlet容器会调用Servlet实例的service(HttpServletRequest request, HttpServletResponse response)方法来处理每个请求。
- 在这个方法内部,根据请求的类型(如GET、POST等),会进一步调用doGet、doPost等相应的方法。
- 为什么一个Servlet能处理多个请求?
- 线程隔离:
- 每个请求都在其自己的线程中处理,这意味着一个请求的处理不会干扰另一个请求的处理。
- 因此,尽管多个请求可能同时由一个Servlet实例处理,但它们实际上是彼此隔离的。
- 无状态性:
- Servlet本身通常是无状态的,即它不会在其成员变量中存储与特定请求相关的数据。
- 所有与请求相关的数据都通过HttpServletRequest和HttpServletResponse对象传递,这些对象在每次请求时都是新的。
由于Servlet容器采用多线程处理机制,并且Servlet本身通常是无状态的,因此一个Servlet实例可以并发地处理多个HttpServletRequest和HttpServletResponse对象。这种设计使得Servlet能够高效地处理并发请求,从而支持高并发的Web应用程序。
Java Web应用程序
Java Web应用程序是一种使用Java语言和相关技术(如Servlet、JSP、JavaBeans等)开发的Web应用程序。它遵循Java EE(Java Enterprise Edition)规范,可以在支持Java的Web服务器上运行,如Tomcat、Jetty、JBoss等。
Spring MVC是Spring框架中的一个核心模块,它实现了MVC(Model-View-Controller)设计模式,用于构建Web应用程序。
Servlet
Servlet是一种运行在服务器端的Java类,它遵循Servlet API规范,用于接收客户端的请求、处理这些请求并生成响应。Servlet是Java Web应用的核心组件之一,它允许开发者使用Java语言来编写动态Web内容。
Servlet的主要作用是处理来自Web客户端(如浏览器)的请求,并生成相应的响应。这些响应可以是HTML页面、JSON数据、XML文档等,具体取决于请求的类型和Servlet的业务逻辑。Servlet通过接收请求、解析请求参数、调用业务逻辑、生成响应内容并发送回客户端的方式,实现了动态Web内容的生成和交互。
过滤器
下图显示了单个HTTP请求的处理程序的典型分层:
过滤器处理请求的工作流程通常包括以下几个步骤:
- 当HTTP请求到达服务器时,它会首先进入过滤器链(FilterChain)。过滤器链是由多个过滤器按照一定顺序组成的,每个过滤器都会对请求进行一定的处理。
- 在过滤器链中,每个过滤器都会执行其doFilter方法。doFilter方法是过滤器的核心,它接收三个参数:ServletRequest、ServletResponse和FilterChain。在doFilter方法中,过滤器可以对请求进行预处理,如身份验证、参数校验等。然后,通过调用FilterChain的doFilter方法,将请求传递给下一个过滤器或目标资源。
- ServletRequest:表示客户端发出的请求
- ServletResponse:表示服务器对请求的响应。
- FilterChain:表示过滤器链,通过它可以调用下一个过滤器或目标资源。
- 当请求传递到目标资源(如Controller中的方法)时,目标资源会处理请求并生成响应。目标资源可以是任何能够处理HTTP请求的组件,如Servlet、控制器等。
- 一旦目标资源处理完请求并生成响应,响应会沿着过滤器链返回。每个过滤器都有机会对响应进行后处理,如修改响应头、压缩响应内容等。
- 经过所有过滤器的后处理后,最终生成的响应会被发送给客户端。客户端接收到响应后,会根据响应内容进行相应的处理。
在Spring MVC应用程序中,Servlet是 DispatcherServlet 的一个实例。
简单了解DispatcherServlet
- DispatcherServlet的单例性:
在Spring MVC框架中,DispatcherServlet作为前端控制器,负责接收客户端发送的请求,并将其分发给相应的Controller进行处理。由于DispatcherServlet在整个应用程序的生命周期中只需要被实例化一次,并且负责处理所有的HTTP请求,因此它是单例的。- DispatcherServlet的线程安全性:
Servlet容器(如Tomcat)通常会为每个请求创建一个新的线程来处理,从而实现并发处理。这样,不同的请求可以并行处理而不会相互影响。Servlet容器会根据具体的配置和使用的线程池来管理线程的数量。
DispatcherServlet被设计为线程安全的,这意味着它可以同时处理多个并发请求而不会导致数据不一致或线程安全问题。这是因为DispatcherServlet避免在其成员变量中存储与请求相关的状态信息,而是将所有与请求相关的数据封装在方法参数中,这些数据在每次请求时都是新的,并且属于当前处理该请求的线程的私有变量。
FilterChain、Filter与Servlet的关系
- FilterChain(过滤器链):
- FilterChain是一个对象,它定义了一组Filter(过滤器)的调用顺序
- 当一个请求到达Servlet容器时,容器会根据配置的Filter顺序,依次调用每个Filter的doFilter方法。
- 每个Filter可以在调用链中的下一个Filter之前或之后对请求或响应进行预处理或后处理。
- 最终,请求会到达目标Servlet进行处理,生成的响应会沿着相同的FilterChain返回给客户端。
- Filter(过滤器):
- Filter是一个实现了javax.servlet.Filter接口的Java类。
- 它主要用于在请求到达Servlet之前或响应发送回客户端之前对请求或响应进行拦截和处理。
- 常见的应用场景包括身份验证、日志记录、数据压缩、字符编码转换等。
- 每个Filter都会注册到Servlet容器中,并配置一个URL模式,以便对匹配的请求进行拦截。
- Servlet:
- Servlet是一个实现了javax.servlet.Servlet接口的Java类,用于处理来自Web客户端的请求并生成响应。
- Servlet是Java Web应用中的核心组件之一,它负责具体的业务逻辑处理和响应内容的生成。
- 当请求通过FilterChain到达Servlet时,Servlet会根据请求的类型和参数调用相应的业务方法进行处理。
- 处理完成后,Servlet会生成响应内容,并通过FilterChain将响应发送回客户端。
Servlet容器是一个Java运行时环境,它提供了对Servlet、Filter以及FilterChain的支持。Servlet容器负责加载Servlet类和Filter类、处理请求和响应、管理会话、维护Servlet生命周期等。FilterChain、Filter与Servlet都运行在Servlet容器中,它们共同构成了Java Web应用的基础架构。
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() {}
}
2.DelegatingFilterProxy
DelegatingFilterProxy是Spring框架提供的一个特殊的Servlet过滤器,它作为过滤器代理使用。DelegatingFilterProxy是Spring框架中的一个类(org.springframework.web.filter.DelegatingFilterProxy
),它实现了javax.servlet.Filter接口。然而,它并不直接处理请求和响应,而是作为一个代理,将请求转发给Spring容器中的一个目标过滤器bean进行处理。DelegatingFilterProxy的主要作用是在Servlet容器和Spring容器之间建立一个桥梁,使得可以在Spring容器中配置和管理过滤器,这样做的好处是可以利用Spring的依赖注入等特性,使得过滤器的配置更加灵活和可维护。
3.FilterChainProxy和SecurityFilterChain
下图显示了FilterChainProxy和多个SecurityFilterChain的关系:
FilterChainProxy是Spring Security中的一个核心组件,它扮演着管理和执行安全过滤器链的重要角色。由于 FilterChainProxy 是一个Bean,它通常被包裹在 DelegatingFilterProxy 中。
虽然 DelegatingFilterProxy 是在 Servlet 容器中初始化的,但它实际上是一个代理类,它代理的目标过滤器(即 FilterChainProxy)是由 Spring 容器管理的。
当请求到达 Servlet 容器并触发 DelegatingFilterProxy 时,DelegatingFilterProxy 会根据配置找到 Spring 容器中的 FilterChainProxy。这通常是通过 Spring 容器的 Bean 名称来实现的,可以在 DelegatingFilterProxy 的配置中指定要代理的 Bean 名称。
由于 FilterChainProxy 是由 Spring 容器管理的,因此它可以享受 Spring 的依赖注入功能。这意味着 FilterChainProxy 可以注入其他 Spring 管理的 Bean,如安全上下文、认证管理器等。
FilterChainProxy通过维护一个或多个过滤器链(SecurityFilterChain
)来管理安全过滤器。每个过滤器链都包含一组按顺序排列的过滤器。当请求到达FilterChainProxy时,它会根据请求的URL或其他条件,选择相应的过滤器链来处理请求。然后,请求会依次通过过滤器链中的每个过滤器,直到所有过滤器都处理完毕。
4.自定义过滤器
4.1.自定义注册在servlet容器的过滤器
首先,我们通过调试程序,查看Servlet 容器初始化了哪些Servlet和Filter:
了解ServletContextInitializerBeans
ServletContextInitializerBeans 是 Spring Boot 框架中的一个重要组件,它主要用于在 Spring Boot 应用启动时配置和初始化 Servlet 容器。它通过与 Servlet 容器的交互,管理 Servlet、Filter 和 EventListener 的注册,确保这些组件能够按照预期的顺序和配置在 Servlet 容器中生效。
找到org.springframework.boot.web.servlet.ServletContextInitializerBeans
这个类,在logMappings
方法上打断点,在调试窗口中查看`initializers``变量的值。
DelegatingFilterProxyRegistrationBean 是 Spring Boot 提供的针对 Servlet 3.0+ Web 的一个注册器 Bean(RegistrationBean)。它继承自 AbstractFilterRegistrationBean,专门用于向 Servlet 容器注册一个 DelegatingFilterProxy 过滤器。
FilterRegistrationBean 是 Spring 框架中的一个类,它专门用于在 Servlet 容器中注册一个过滤器(Filter)。
FilterRegistrationBean 是用于注册任意类型的过滤器的通用工具类,而 DelegatingFilterProxyRegistrationBean 是专门用于注册 DelegatingFilterProxy 过滤器的注册器 Bean。
DispatcherServletRegistrationBean 是 Spring Boot 框架中用于注册和配置 DispatcherServlet 的重要类。它确保了 DispatcherServlet 能够被正确地注册到 Servlet 容器中,并配置好相关属性,从而保证了 Spring MVC 的正常运作。
下面是注册在servlet容器中的自定义过滤器的步骤:
- 编写实现`jakarta.servlet.Filter``接口的类
package com.drson.usermanagement.filter;import jakarta.servlet.*;
import lombok.extern.slf4j.Slf4j;import java.io.IOException;@Slf4j
public class MyFilterOne implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("MyFilterOne过滤器正在初始化......");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {log.info("MyFilterOne过滤器正在处理相关业务.......");chain.doFilter(request,response);}@Overridepublic void destroy() {log.info("MyFilterOne过滤器正在销毁......");Filter.super.destroy();}
}
- 编写配置文件,注册过滤器
package com.drson.usermanagement.config;import com.drson.usermanagement.filter.MyFilterOne;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<MyFilterOne> myFilterOne(){FilterRegistrationBean<MyFilterOne> registrationBean = new FilterRegistrationBean<>(new MyFilterOne());//配置需要注册的过滤器registrationBean.addUrlPatterns("/test1");registrationBean.addServletNames("filter1");//未配置过滤器的位置return registrationBean;}
}
- 编写控制器
package com.drson.usermanagement.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TestController {@GetMapping("/test")public String test() {return "test";}@GetMapping("/test1")public String test1() {return "test1";}@GetMapping("/test2")public String test2() {return "test2";}@PostMapping("login")public String login(){return "login";}
}
- 测试效果
运行程序,在Postman中访问localhost:8080/test1
地址,你会看到:
当我们访问其它url时,是不会执行MyFilterOne
过滤器的doFilter
方法的。
5.在application.properties
文件中配置应用程序来记录所有的 security 事件:
logging.level.org.springframework.security=TRACE
这样,我们可以观察到自定义过滤器MyFilterOne
与DelegatingFilterProxy
的执行顺序:
通过调试,验证以下:
5. 注册MyFilterOne
过滤器时,我们配置order
的数值为-101
,比DelegatingFilterProxy
小,看看自定义的过滤器是否比DelegatingFilterProxy
先执行:
package com.drson.usermanagement.config;import com.drson.usermanagement.filter.MyFilterOne;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<MyFilterOne> myFilterOne(){FilterRegistrationBean<MyFilterOne> registrationBean = new FilterRegistrationBean<>(new MyFilterOne());//配置需要注册的过滤器registrationBean.addUrlPatterns("/test1");registrationBean.addServletNames("filter1");//配置过滤器的位置registrationBean.setOrder(-101);return registrationBean;}
}
4.2.在SecurityFilterChain中注册自定义的过滤器
SecurityFilterChain 被 FilterChainProxy 用来确定当前请求应该调用哪些 Spring Security Filter 实例。SecurityFilterChain 中的 Security Filter 通常是Bean,但它们是用 FilterChainProxy 而不是 DelegatingFilterProxy 注册的。
具体的注册步骤如下:
- 编写继承了
jakarta.servlet.Filter
接口的类:
package com.drson.usermanagement.filter;import jakarta.servlet.*;
import lombok.extern.slf4j.Slf4j;import java.io.IOException;@Slf4j
public class MySecurityFilterOne implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("MySecurityFilterOne过滤器正在初始化......");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {log.info("MySecurityFilterOne过滤器正在处理相关业务.......");chain.doFilter(request,response);}@Overridepublic void destroy() {log.info("MySecurityFilterOne过滤器正在销毁......");Filter.super.destroy();}
}
- 编写配置文件,注册过滤器
package com.drson.usermanagement.config;import com.drson.usermanagement.filter.MySecurityFilterOne;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.AuthorizationFilter;@Configuration
@EnableWebSecurity
public class WebSecurityConfig {//定义密码编码器@Beanpublic BCryptPasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth.requestMatchers("/login").permitAll().anyRequest().authenticated()).httpBasic(Customizer.withDefaults());//添加自定义过滤器到过滤器链,位置在AuthorizationFilter过滤器之前http.addFilterBefore(new MySecurityFilterOne(), AuthorizationFilter.class);return http.build();}
}
- 效果
当修改自定义过滤器位置为addFilterAfter
时:
http.addFilterAfter(new MySecurityFilterOne(), AuthorizationFilter.class);
效果如下:
4. 还可以使用@Component
注解自定义过滤器类:
package com.drson.usermanagement.filter;import jakarta.servlet.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.io.IOException;@Component
@Slf4j
public class MySecurityFilterOne implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("MySecurityFilterOne过滤器正在初始化......");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {log.info("MySecurityFilterOne过滤器正在处理相关业务.......");chain.doFilter(request,response);}@Overridepublic void destroy() {log.info("MySecurityFilterOne过滤器正在销毁......");Filter.super.destroy();}
}
可以发现自定义的过滤器在启动阶段有调用初始化方法(留意一下,上面没有使用@Component时,是没有调用自定义过滤器的初始化方法的):
当我们试图访问一个url时:
注意
上面的认证方式,使用的时Basic Http。将使用BasicAuthenticationFilter
过滤器去认证用户输入用户名和密码。
你会发现自定义的MySecurityFilterOne
过滤器的doFilter
方法被调用了两次:
这是,因为 Spring Boot 会自动 在嵌入式容器中注册它。这可能会导致 filter 被调用两次,一次由容器调用,一次由 Spring Security 调用,而且顺序不同。
- 解决多次调用的问题
如果你仍然想把你的 filter 声明为 Spring Bean,以利用依赖注入,避免重复调用,你可以通过声明FilterRegistrationBean
Bean 并将其enabled
属性设置为false
来告诉 Spring Boot 不要向容器注册它:
package com.drson.usermanagement.config;import com.drson.usermanagement.filter.MySecurityFilterOne;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.AuthorizationFilter;@Configuration
@EnableWebSecurity
public class WebSecurityConfig {//定义密码编码器@Beanpublic BCryptPasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}//添加以下Bean,可以避免MySecurityFilterOne过滤器的doFilter方法重复调用@Beanpublic FilterRegistrationBean<MySecurityFilterOne> getRegistrationBean(MySecurityFilterOne filter){FilterRegistrationBean<MySecurityFilterOne> registrationBean = new FilterRegistrationBean<>(filter);registrationBean.setEnabled(false);//重点是配置这个return registrationBean;}@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth.requestMatchers("/login").permitAll().anyRequest().authenticated()).httpBasic(Customizer.withDefaults());//添加自定义过滤器到过滤器链http.addFilterBefore(new MySecurityFilterOne(), AuthorizationFilter.class);return http.build();}
}
按照上面的配置,就可以避免重复调用:
4.3.多个SecurityFilterChain
- 编写多个controller和filter
package com.drson.usermanagement.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api1")
public class TestController {@GetMapping("/test")public String test() {return "test";}@GetMapping("/test1")public String test1() {return "test1";}@GetMapping("/test2")public String test2() {return "test2";}
}
package com.drson.usermanagement.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api2")
public class TestTwoController {@GetMapping("/test")public String test() {return "test";}@GetMapping("/test1")public String test1() {return "test1";}@GetMapping("/test2")public String test2() {return "test2";}
}
package com.drson.usermanagement.filter;import jakarta.servlet.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.io.IOException;@Component
@Slf4j
public class MySecurityFilterOne implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("MySecurityFilterOne过滤器正在初始化......");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {log.info("MySecurityFilterOne过滤器正在处理相关业务.......");chain.doFilter(request,response);}@Overridepublic void destroy() {log.info("MySecurityFilterOne过滤器正在销毁......");Filter.super.destroy();}
}
package com.drson.usermanagement.filter;import jakarta.servlet.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.io.IOException;@Component
@Slf4j
public class MySecurityFilterTwo implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("MySecurityFilterTwo过滤器正在初始化......");Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {log.info("MySecurityFilterTwo过滤器正在处理相关业务.......");chain.doFilter(request,response);}@Overridepublic void destroy() {log.info("MySecurityFilterTwo过滤器正在销毁......");Filter.super.destroy();}
}
- 编写配置文件
package com.drson.usermanagement.config;import com.drson.usermanagement.filter.MySecurityFilterOne;
import com.drson.usermanagement.filter.MySecurityFilterTwo;
import jakarta.annotation.Resource;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.AuthorizationFilter;@Configuration
@EnableWebSecurity
public class WebSecurityConfig {@Resourceprivate MySecurityFilterOne mySecurityFilterOne;@Resourceprivate MySecurityFilterTwo mySecurityFilterTwo;//定义密码编码器@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic FilterRegistrationBean<MySecurityFilterOne> getRegistrationOneBean(MySecurityFilterOne filter) {FilterRegistrationBean<MySecurityFilterOne> registrationBean = new FilterRegistrationBean<>(filter);registrationBean.setEnabled(false);return registrationBean;}@Beanpublic FilterRegistrationBean<MySecurityFilterTwo> getRegistrationTowBean(MySecurityFilterTwo filter) {FilterRegistrationBean<MySecurityFilterTwo> registrationBean = new FilterRegistrationBean<>(filter);registrationBean.setEnabled(false);return registrationBean;}@Beanpublic SecurityFilterChain filterChainOne(HttpSecurity http) throws Exception {http.securityMatchers(matcher -> matcher.requestMatchers("/api1/**")).authorizeHttpRequests(auth -> auth.anyRequest().authenticated()).httpBasic(Customizer.withDefaults());//使用Basic认证//添加自定义过滤器到过滤器链http.addFilterBefore(mySecurityFilterOne, AuthorizationFilter.class);return http.build();}@Beanpublic SecurityFilterChain filterChainTwo(HttpSecurity http) throws Exception {http.securityMatchers(matcher -> matcher.requestMatchers("/api2/**")).authorizeHttpRequests(auth -> auth.anyRequest().authenticated()).httpBasic(Customizer.withDefaults())//使用Basic认证.addFilterBefore(mySecurityFilterTwo, AuthorizationFilter.class);//添加自定义过滤器到过滤器链return http.build();}
}
- 通过访问不同的url,可以看到效果:
当访问localhost:8080/api1/test2
时:
当访问localhost:8080/api2/test2
时:
5.小结
本章学习了servlet规范中的一些概念,并尝试在servlet容器中和spring容器中注册自定义的过滤器,了解了servlet容器是通过DelegatingFilterProxy 代理调用spring容器中管理的过滤器Bean。也了解了如何配置过滤器的执行顺序,怎样在安全过滤器链中添加安全过滤器。