Spring Security双认证提供者问题:User登录POST请求不被支持
问题描述
应用需区分User和Organization两种用户类型做数据库验证,因此创建了两个独立的WebSecurityConfiguration类,分别配置对应SecurityFilterChain、表单登录逻辑及UserDetailsService实现的认证提供者。
目前Organization的注册、登录端点均正常,会话可成功创建;但User的登录端点/auth/login/user的POST请求出现异常:
- IntelliJ日志提示
Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported] - Postman显示返回200,但实际调试发现请求先返回405,随后重定向至
/auth/login/org,POST请求未完成认证流程。
相关代码
OrgWebSecurityConfiguration的SecurityFilterChain
@Bean @CrossOrigin public SecurityFilterChain orgSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf() .disable() .securityMatchers((matcher) -> matcher .requestMatchers("/auth/*/org").anyRequest()) .authorizeHttpRequests() .requestMatchers("/auth/register/**") .permitAll() .requestMatchers("/auth/login/**") .permitAll() .anyRequest() .authenticated() .and() .formLogin() .loginPage("/auth/login/org") .loginProcessingUrl("/auth/login/org") .defaultSuccessUrl("/auth/org-login-success", true) .permitAll() .and() .authenticationManager(orgAuthenticationManager(http)) .logout() .invalidateHttpSession(true) .clearAuthentication(true) .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/auth/login?logout") .permitAll(); return http.build(); }
UserWebSecurityConfiguration的SecurityFilterChain
@Bean @CrossOrigin public SecurityFilterChain userSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf() .disable() .securityMatchers((matcher) -> matcher .requestMatchers("/auth/*/user").anyRequest()) .authorizeHttpRequests() .requestMatchers("/auth/register/**") .permitAll() .requestMatchers("/auth/login/**") .permitAll() .anyRequest() .authenticated() .and() .formLogin() .loginPage("/auth/login/user") .loginProcessingUrl("/auth/login/user") .defaultSuccessUrl("/auth/login-success", true) .permitAll() .and() .authenticationManager(userAuthenticationManager(http)) .logout() .invalidateHttpSession(true) .clearAuthentication(true) .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/auth/login?logout") .permitAll(); return http.build(); }
userAuthProvider
@Bean public DaoAuthenticationProvider userAuthProvider() { DaoAuthenticationProvider userAuthenticationProvider = new DaoAuthenticationProvider(); userAuthenticationProvider.setUserDetailsService(userDetailsService); userAuthenticationProvider.setPasswordEncoder(passwordEncoder()); return userAuthenticationProvider; }
orgAuthProvider
@Bean public DaoAuthenticationProvider orgAuthProvider() { DaoAuthenticationProvider orgAuthenticationProvider = new DaoAuthenticationProvider(); orgAuthenticationProvider.setUserDetailsService(organizationDetailsService); orgAuthenticationProvider.setPasswordEncoder(passwordEncoder); return orgAuthenticationProvider; }
注:orgAuthenticationManager和userAuthenticationManager均通过AuthenticationManagerBuilder配置对应DaoAuthenticationProvider实现,其中User的认证管理器通过@Primary注解设为优先。已尝试为UserWebSecurityConfiguration添加@Order(0)注解、关闭CSRF,但问题仍未解决。
问题根源与修复方案
1. 修正SecurityFilterChain的匹配范围(核心问题)
两个配置中的securityMatchers写法错误,.requestMatchers("/auth/*/org").anyRequest()会让该FilterChain匹配所有请求,而非仅/auth/*/org路径,导致两条链的请求范围重叠,User的登录请求被Org的链错误拦截。
修改两个配置类的securityMatchers,明确限定各自负责的路径:
- OrgWebSecurityConfiguration:
.securityMatchers((matcher) -> matcher .requestMatchers("/auth/*/org", "/auth/register/org"))
- UserWebSecurityConfiguration:
.securityMatchers((matcher) -> matcher .requestMatchers("/auth/*/user", "/auth/register/user"))
2. 明确FilterChain执行顺序
为两个配置类添加@Order注解,确保优先级明确:
- 在
UserWebSecurityConfiguration类上添加@Order(1) - 在
OrgWebSecurityConfiguration类上添加@Order(2)
注:@Order数值越小,执行优先级越高
3. 消除AuthenticationManager全局冲突
移除认证管理器的@Primary注解,每个FilterChain使用专属的认证管理器,将userAuthenticationManager和orgAuthenticationManager设为配置类的私有方法,避免全局Bean冲突:
// User配置类中的私有方法 private AuthenticationManager userAuthenticationManager(HttpSecurity http) throws Exception { return http.getSharedObject(AuthenticationManagerBuilder.class) .authenticationProvider(userAuthProvider()) .build(); }
4. 验证登录路径匹配
确保loginProcessingUrl的路径在对应FilterChain的securityMatchers范围内,例如User的/auth/login/user已包含在.requestMatchers("/auth/*/user")中,无需额外调整。
验证
修复后重启应用,发送POST请求至/auth/login/user,可正常进入User的认证流程,无405错误及错误重定向。
内容的提问来源于stack exchange,提问作者enid

