Spring Boot中为多DispatcherServlet配置差异化安全上下文的方法
解决Spring Boot多DispatcherServlet下的Security隔离问题
这个问题的核心在于Spring Security的默认FilterChainProxy Filter是全局生效的——不管请求由哪个DispatcherServlet处理,它都会拦截所有路径的请求。所以即使你的第二个Servlet上下文没注册SecurityConfig,请求还是会被Security Filter拦下。下面给你两种可行的解决方案:
方案一:通过WebSecurity忽略非API路径
直接在SecurityConfig中配置,让Spring Security完全忽略所有非/api开头的请求,这些请求不会经过任何Security Filter链,彻底禁用安全机制:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final BCryptPasswordEncoder passwordEncoder; private final DataSource dataSource; @Autowired public SecurityConfig(BCryptPasswordEncoder passwordEncoder, DataSource dataSource) { this.passwordEncoder = passwordEncoder; this.dataSource = dataSource; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .usersByUsernameQuery(USERS_BY_USERNAME_QUERY) .authoritiesByUsernameQuery(AUTHORITIES_BY_USERNAME_QUERY) .dataSource(dataSource) .passwordEncoder(passwordEncoder); } // 新增:配置WebSecurity忽略非API路径 @Override public void configure(WebSecurity web) throws Exception { // 正则匹配所有不以/api开头的请求,直接跳过Security处理 web.ignoring().regexMatchers("^(?!/api).*"); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated().and() .formLogin() .loginPage("/api/login") .usernameParameter("username") .passwordParameter("password") .and() .httpBasic().and() .csrf().disable(); } }
这种方式简单直接,适合你的场景:/api/*路径的请求会走完整的Security校验,其他路径(由secondServlet处理)完全不受Security影响。
方案二:自定义Security Filter的映射路径
通过覆盖Spring Boot自动注册的FilterChainProxy的FilterRegistrationBean,让Security Filter只作用于/api/*路径:
@SpringBootApplication(exclude = {DispatcherServletAutoConfiguration.class}) public class Application{ public static void main(String[] args) { SpringApplication.run(Application.class, args); } // ... 已有的FirstServletRegistration和SecondServletRegistration方法 ... // 新增:自定义Security Filter的映射路径 @Bean public FilterRegistrationBean<FilterChainProxy> securityFilterChainProxy(FilterChainProxy filterChainProxy) { FilterRegistrationBean<FilterChainProxy> registrationBean = new FilterRegistrationBean<>(filterChainProxy); // 只让Security Filter拦截/api/*路径的请求 registrationBean.addUrlPatterns("/api/*"); // 设置最高优先级,确保Security Filter在其他Filter之前执行 registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); return registrationBean; } }
这种方式更灵活,如果你后续需要调整Security的作用路径,直接修改addUrlPatterns的参数即可。
额外注意点
- 你的Servlet注册顺序是正确的:
firstServlet映射/api/*(更具体的路径),secondServlet映射/*,Servlet容器会优先匹配更具体的路径,所以/api开头的请求会由firstServlet处理,其他请求由secondServlet处理,这部分无需修改。 - 确保
SecurityConfig只在firstServlet的上下文里注册(你已经做到了,FirstWebConfig的上下文注册了它,而SecondWebConfig没有),避免secondServlet的上下文被Security配置污染。
内容的提问来源于stack exchange,提问作者Shirru
相关产品推荐
相关产品推荐

