You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security 5.x升级至6.x后,POST /login请求返回404错误

Spring Security 5.x升级至6.x后,POST /login请求返回404错误

我帮你分析下这个问题的核心原因:你把formLogin配置和OAuth2授权服务器的配置绑定在了同一个SecurityFilterChain里,再加上securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())的限制,导致处理POST /login的UsernamePasswordAuthenticationFilter根本没被应用到这个请求上,自然就返回404了。

具体来说,securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())会让当前FilterChain只对OAuth2授权服务器的专属端点生效(比如/oauth2/authorize、/oauth2/token这类),而你的登录配置是挂在这个链上的,所以当你提交POST /login时,这个请求不会匹配到这个FilterChain,也就没有对应的过滤器来处理登录逻辑,直接返回404。


解决方案:拆分独立的SecurityFilterChain

你需要把普通Web安全配置(登录、静态资源放行、自定义登录页)和OAuth2授权服务器配置拆分成两个独立的SecurityFilterChain Bean,让它们各自负责不同的请求范围。Spring Security 6.x会按@Order注解的优先级(数字越小,优先级越高)来匹配请求。

修改后的完整配置代码如下:

@Configuration
@EnableWebSecurity
public class DefaultSecurityConfig {

    private final ThrottlingFilter throttlingFilter;
    private final UserRepository userRepository;

    DefaultSecurityConfig(ThrottlingFilter throttlingFilter, UserRepository userRepository) {
        this.throttlingFilter = throttlingFilter;
        this.userRepository = userRepository;
    }

    // 普通Web安全FilterChain:处理登录、静态资源等非授权服务器请求
    @Bean
    @Order(1) // 优先级高于授权服务器FilterChain(默认Order为100)
    public SecurityFilterChain webSecurityFilterChain(HttpSecurity http) throws Exception {
        http
            .formLogin(form -> form
                .loginPage("/portal-login") // 自定义登录页
                .loginProcessingUrl("/login") // 显式指定登录处理URL,避免配置歧义
                .permitAll()
            )
            .addFilterBefore(throttlingFilter, UsernamePasswordAuthenticationFilter.class)
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers(HttpMethod.OPTIONS).permitAll()
                .requestMatchers(HttpMethod.GET,
                    "/index*",
                    "/static/**",
                    "/*.js",
                    "/*.json",
                    "/*.ico",
                    "/rest",
                    "/*logo*.png"
                ).permitAll()
                .requestMatchers("/aps/v1/featureFlag/isEnabled/**").permitAll()
                .anyRequest().authenticated()
            )
            .cors(Customizer.withDefaults());

        return http.build();
    }

    // OAuth2授权服务器专属FilterChain:仅处理授权服务器端点
    @Bean
    @Order(2)
    public SecurityFilterChain authorizationServerSecurityFilterChain(
        HttpSecurity http,
        JpaRegisteredClientRepository jpaRegisteredClientRepository,
        JpaOAuth2AuthorizationService jpaOAuth2AuthorizationService
    ) throws Exception {
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
            OAuth2AuthorizationServerConfigurer.authorizationServer();

        http
            .securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
            .with(authorizationServerConfigurer, authorizationServer -> {
                authorizationServer
                    .oidc(Customizer.withDefaults()) // 启用OpenID Connect 1.0
                    .registeredClientRepository(jpaRegisteredClientRepository)
                    .authorizationService(jpaOAuth2AuthorizationService);
            })
            .authorizeHttpRequests(authorize -> authorize
                .anyRequest().authenticated()
            );

        return http.build();
    }

    @Bean
    UserDetailsService userDetailsService() {
        return username -> {
            User user = userRepository.findByUsername(username);
            if (user == null) {
                throw new UsernameNotFoundException("User details not found for user: " + username);
            }
            return new ApsUserPrincipal(
                    user.getId(),
                    user.getUsername(),
                    user.getPassword(),
                    user.getEmail(),
                    user.getEnabled() > 0,
                    user.getFirstName(),
                    user.getLastName(),
                    user.getRole(),
                    user.getFiId(),
                    user.getCountry()
            );
        };
    }
}

关键修改说明

  1. 拆分FilterChain:把原有的单一配置拆成两个独立Bean,分别处理普通Web请求和授权服务器请求
  2. 优先级控制:用@Order指定执行顺序,普通Web链先匹配,确保登录请求被优先处理
  3. 调整formLogin位置:把登录配置移到普通Web链中,让UsernamePasswordAuthenticationFilter正确注册并处理POST /login请求
  4. 显式指定登录URL:虽然默认处理URL就是/login,但显式声明可以避免配置歧义

额外检查点

  • 确认你的自定义登录页表单的action为"/login",method为POST,例如:
<form action="/login" method="post">
  <input type="text" name="username" placeholder="用户名">
  <input type="password" name="password" placeholder="密码">
  <button type="submit">登录</button>
</form>
  • 确保没有其他配置类或过滤器拦截了/login请求

备注:内容来源于stack exchange,提问作者MOHAMMAD WASEEM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 11:59:33