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

Spring Security双登录表单loginProcessingUrl失效致404问题求助

解决Spring Security多登录表单404问题

问题根源

你的用户/管理员登录处理URL(/userLogin、/adminLogin)不在对应过滤器链的securityMatcher匹配范围内(/user/**、/admin/**),导致负责处理登录的过滤器链不会拦截这些请求,最终请求落到GeneralSecurityConfig中,但该链未配置登录处理逻辑,直接返回404。

修复方案

1. 调整登录处理URL与过滤器匹配规则

将登录处理URL调整到对应角色的路径下,并修改securityMatcher包含登录页面和登录处理路径,确保对应过滤器链能拦截这些请求。

2. 修改Security配置类代码

package com.business.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.util.matcher.AntPathRequestMatcher;

import com.business.service.impl.UserDetailsServiceImpl;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Autowired
    UserDetailsServiceImpl userDetailService;

    @Bean
    public static BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider authProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailService);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
        throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Order(1)
    public static class UserSecurityConfig {
        @Bean
        public SecurityFilterChain filterChain1(HttpSecurity http) throws Exception {
            http
                // 匹配用户相关的登录、页面及资源路径
                .securityMatcher("/user/**", "/user-login", "/user/login")
                .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/user-login", "/user/login").permitAll()
                    .requestMatchers("/user/**").hasRole("USER")
                )
                .formLogin(form -> form
                    .loginPage("/user-login")
                    .loginProcessingUrl("/user/login")
                    .usernameParameter("email")
                    .passwordParameter("password")
                    .defaultSuccessUrl("/user/home")
                    .permitAll()
                )
                .logout(logout -> logout
                    .logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
                    .logoutSuccessUrl("/")
                )
                .exceptionHandling(ex -> ex
                    .accessDeniedPage("/403")
                );
            return http.build();
        }
    }

    @Order(2)
    public static class AdminSecurityConfig {
        @Bean
        public SecurityFilterChain filterChain2(HttpSecurity http) throws Exception {
            http
                // 匹配管理员相关的登录、页面及资源路径
                .securityMatcher("/admin/**", "/admin-login", "/admin/login")
                .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/admin-login", "/admin/login").permitAll()
                    .requestMatchers("/admin/**").hasRole("ADMIN")
                )
                .formLogin(form -> form
                    .loginPage("/admin-login")
                    .loginProcessingUrl("/admin/login")
                    .usernameParameter("email")
                    .passwordParameter("password")
                    .defaultSuccessUrl("/admin/home")
                    .permitAll()
                )
                .logout(logout -> logout
                    .logoutRequestMatcher(new AntPathRequestMatcher("/admin/logout"))
                    .logoutSuccessUrl("/")
                )
                .exceptionHandling(ex -> ex
                    .accessDeniedPage("/403")
                );
            return http.build();
        }
    }

    @Order(3)
    public static class GeneralSecurityConfig {
        @Bean
        public SecurityFilterChain filterChain3(HttpSecurity http) throws Exception {
            http
                .csrf().disable()
                .headers().frameOptions().disable()
                .and()
                .authorizeHttpRequests(auth -> auth
                    // 仅允许公共路径,避免覆盖前面的权限规则
                    .requestMatchers("/", "/css/**", "/js/**", "/images/**", "/user-register", "/admin-register").permitAll()
                    // 未匹配到的其他请求需要认证
                    .anyRequest().authenticated()
                );
            return http.build();
        }
    }
}

3. 修改登录页面表单提交地址

管理员登录页面调整表单action:

<form th:action="@{/admin/login}" method="post">

用户登录页面同理修改为@{/user/login}。

关键说明

  • securityMatcher必须包含登录页面和登录处理URL,确保对应过滤器链能处理登录请求;
  • hasRole("USER")等价于检查ROLE_USER权限,Spring会自动添加ROLE_前缀,无需手动书写;
  • 区分用户和管理员的登出路径,避免不同角色登出时产生冲突;
  • 通用过滤器链不要使用"/**"匹配所有请求,否则会覆盖前面的权限规则,仅开放公共资源即可。

内容的提问来源于stack exchange,提问作者Thomas Shelby

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 04:25:16