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

Spring Security FilterChain执行异常:跨角色访问返回404求助

问题原因分析

当前配置存在两个核心问题:

  1. 每个SecurityFilterChain仅匹配各自模块的路径前缀(/admin/**、/guest/**),对应的登录页(/admin_login、/guest_login)不在匹配范围内,虽不影响正常登录,但会导致权限校验逻辑覆盖不全。
  2. 当已登录用户访问对方模块资源时,系统检测到权限不足后,会跳转到未实现的/access-denied页面,因此返回404;而预期逻辑是清除当前登录状态,重定向到对应身份的登录页。

解决方案

修改两个SecurityFilterChain配置,调整路径匹配范围、放行登录页访问,并自定义权限不足时的跳转逻辑:

修改后的SecurityConfigOrder1类

package com.securityexample;

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.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.securityexample.providers.MyCustomAuthenticationProvider;

@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfigOrder1 {

    @Autowired
    MyCustomAuthenticationProvider myCustomeAuthenticationProvider;

    @Bean
    public SecurityFilterChain filterChainApp1(HttpSecurity http) throws Exception {
        http.authenticationProvider(myCustomeAuthenticationProvider);

        // 匹配/admin路径下资源及管理员登录页
        http.requestMatchers(request -> request.matches("/admin/**", "/admin_login"))
                .authorizeRequests(auth -> auth
                        .antMatchers("/admin_login").permitAll() // 放行登录页匿名访问
                        .anyRequest().hasAuthority("ADMIN"))
                .formLogin(form -> form
                        .loginPage("/admin_login")
                        .loginProcessingUrl("/admin/process-login")
                        .failureUrl("/admin_login?error=error")
                        .defaultSuccessUrl("/admin/page1")
                        .usernameParameter("username")
                        .passwordParameter("password"))
                .logout(logout -> logout
                        .logoutRequestMatcher(new AntPathRequestMatcher("/admin/logout"))
                        .logoutSuccessUrl("/admin_login?logout=true")
                        .deleteCookies("JSESSIONID"))
                .exceptionHandling(ex -> ex
                        // 管理员访问访客资源时,销毁会话并重定向到访客登录页
                        .accessDeniedHandler((request, response, exDenied) -> {
                            request.getSession().invalidate();
                            response.sendRedirect("/guest_login");
                        }))
                .csrf(csrf -> csrf.disable());

        return http.build();
    }
}

修改后的SecurityConfigOrder2类

package com.securityexample;
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.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.securityexample.providers.GuestAuthenticationProvider;

@Configuration
@EnableWebSecurity
@Order(2)
public class SecurityConfigOrder2 {
    @Autowired
    GuestAuthenticationProvider guestAuthenticationProvider;

    @Bean
    public SecurityFilterChain filterChaninApp2(HttpSecurity http) throws Exception {

        http.authenticationProvider(guestAuthenticationProvider);

        // 匹配/guest路径下资源及访客登录页
        http.requestMatchers(request -> request.matches("/guest/**", "/guest_login"))
                .authorizeRequests(auth -> auth
                        .antMatchers("/guest_login").permitAll() // 放行登录页匿名访问
                        .anyRequest().hasAuthority("GUEST_USER"))
                .formLogin(form -> form
                        .loginPage("/guest_login")
                        .loginProcessingUrl("/guest/process-login")
                        .failureUrl("/guest_login?error=error")
                        .defaultSuccessUrl("/guest/page1")
                        .usernameParameter("username")
                        .passwordParameter("otp"))
                .logout(logout -> logout
                        .logoutRequestMatcher(new AntPathRequestMatcher("/guest/logout"))
                        .logoutSuccessUrl("/guest_login?logout=true")
                        .deleteCookies("JSESSIONID"))
                .exceptionHandling(ex -> ex
                        // 访客访问管理员资源时,销毁会话并重定向到管理员登录页
                        .accessDeniedHandler((request, response, exDenied) -> {
                            request.getSession().invalidate();
                            response.sendRedirect("/admin_login");
                        }))
                .csrf(csrf -> csrf.disable());

        return http.build();
    }
}

关键调整说明

  1. 路径匹配范围扩展:使用requestMatchers替代antMatcher,让过滤器链同时处理模块资源和对应登录页,避免逻辑遗漏。
  2. 登录页匿名放行:对/admin_login和/guest_login添加permitAll(),确保未登录用户能正常访问登录页面。
  3. 自定义权限不足处理:通过accessDeniedHandler销毁当前会话(清除已登录状态),并重定向到目标身份的登录页,完全符合需求预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 15:40:48