Spring MVC中能否为各Servlet WebApplicationContext配置独立Spring Security上下文?
你遇到的错误并非意味着只能在Root WebApplicationContext中配置Spring Security,实际上可以为每个Servlet WebApplicationContext实现独立的安全配置,问题的核心在于DelegatingFilterProxy的默认查找上下文是Root上下文,只要调整它的配置指向对应Servlet上下文即可。
问题根源
DelegatingFilterProxy初始化时,默认会从Root WebApplicationContext中查找名为springSecurityFilterChain的Bean。而你的Spring Security配置类是注册在Servlet WebApplicationContext中的,所以过滤器找不到对应的Bean,抛出NoSuchBeanDefinitionException。
实现独立配置的步骤
1. 隔离各Servlet上下文的Security配置
为每个DispatcherServlet创建独立的Spring Security配置类(继承WebSecurityConfigurerAdapter),并确保这些配置类仅被对应的Servlet上下文扫描,不会被Root上下文加载。
比如:
- 为
app1Servlet创建App1SecurityConfig - 为
app2Servlet创建App2SecurityConfig
在各自的Servlet配置文件(如app1-servlet.xml)中扫描对应安全包:
<!-- app1-servlet.xml --> <context:component-scan base-package="com.yourpackage.app1.security" />
Root上下文的配置文件(如root-context.xml)中排除这些安全包,避免重复加载:
<!-- root-context.xml --> <context:component-scan base-package="com.yourpackage"> <context:exclude-filter type="regex" expression="com.yourpackage.app1.security.*" /> <context:exclude-filter type="regex" expression="com.yourpackage.app2.security.*" /> </context:component-scan>
2. 为每个Servlet配置专属的DelegatingFilterProxy
在web.xml中,为每个DispatcherServlet创建独立的DelegatingFilterProxy实例,通过contextAttribute参数指定该过滤器要查找的Servlet WebApplicationContext的属性名。
每个DispatcherServlet初始化时,会将自身的WebApplicationContext绑定到ServletContext的属性中,属性名格式为org.springframework.web.servlet.FrameworkServlet.CONTEXT.[servlet-name]。
示例配置:
<!-- 对应app1Servlet的安全过滤器 --> <filter> <filter-name>app1SecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <!-- 指定要查找的目标Bean名称 --> <param-name>targetBeanName</param-name> <param-value>springSecurityFilterChain</param-value> </init-param> <init-param> <!-- 指定要查找的Servlet上下文属性名 --> <param-name>contextAttribute</param-name> <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.app1Servlet</param-value> </init-param> </filter> <filter-mapping> <filter-name>app1SecurityFilterChain</filter-name> <url-pattern>/app1/*</url-pattern> </filter-mapping> <!-- 对应app2Servlet的安全过滤器 --> <filter> <filter-name>app2SecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>springSecurityFilterChain</param-value> </init-param> <init-param> <param-name>contextAttribute</param-name> <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.app2Servlet</param-value> </init-param> </filter> <filter-mapping> <filter-name>app2SecurityFilterChain</filter-name> <url-pattern>/app2/*</url-pattern> </filter-mapping>
关键注意事项
- 每个过滤器的
filter-mapping必须严格对应到所属Servlet的URL路径,避免不同Servlet的安全规则互相干扰。 - 确保安全配置类不会被多个上下文扫描到,否则会引发Bean重复定义的异常。
- 你使用的Spring Security 5.4.5版本中,
WebSecurityConfigurerAdapter仍处于可用状态,上述配置方式完全适配该版本。
内容的提问来源于stack exchange,提问作者Pavel Varchenko

