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

Spring为何持续返回302状态码重定向至登录页面?

问题:Spring Security 方法级权限校验未生效,访问根路径返回302重定向

我想实现默认锁定所有资源,仅按需开放的安全策略:除少数白名单端点外,所有请求启用方法级安全校验。但目前遇到问题:

  • 若把配置中的.anyRequest.authenticated()改成.anyRequest.permitAll(),方法级安全能正常工作,但需要给每个方法/类加注解锁访问,不符合默认锁定的最佳实践。
  • 按照资料里的方案用.anyRequest.authenticated()配合@PreAuthorize按需开放时,访问根路径/始终返回302重定向到登录页,而不是预期的权限校验失败的4xx状态码。

环境信息

  • Java 24 + Spring Boot 3.4.5

控制器代码

@Controller
@RequestMapping("/")
public class AppController {
    @GetMapping
    @PreAuthorize("hasRole('ANONYMOUS')")
    public String home() {
        return "home"; // 模板文件在 classpath:/templates
    }
}

安全配置

@EnableWebSecurity(debug = true)
@EnableMethodSecurity
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filter(HttpSecurity http) throws Exception {
        var whitelist = new String[]{
            "/styles/**",
            "/listing-images/**",
            "/thumbnail-images/**",
            "/login",
            "/favicon.ico"

        };

        return http
            .authorizeHttpRequests(a -> a
                .requestMatchers(whitelist).permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults())
            .build();
    }
}

Application.yaml配置

spring:
  datasource:
    url: jdbc:postgresql://localhost:9056/db
    username: my_user
    password: my_password
logging:
  level:
    org.springframework.security: debug
server:
  port: 8090

依赖

  • Spring Boot DevTools
  • Spring Web
  • Thymeleaf
  • Spring Security
  • JDBC API
  • PostgreSQL Driver
  • Validation

文件夹结构

文件夹结构

访问根路径后的日志信息

2025-05-15T00:07:38.329-06:00  INFO 2958139 --- [  restartedMain] c.t.n.NewMarketplaceApplication          : Starting NewMarketplaceApplication using Java 24 with PID 2958139 (/home/PcUser/Documents/dev/java/new-marketplace/target/classes started by PcUser in /home/PcUser/Documents/dev/java/new-marketplace)
2025-05-15T00:07:38.331-06:00  INFO 2958139 --- [  restartedMain] c.t.n.NewMarketplaceApplication          : No active profile set, falling back to 1 default profile: "default"
2025-05-15T00:07:38.362-06:00  INFO 2958139 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2025-05-15T00:07:38.362-06:00  INFO 2958139 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2025-05-15T00:07:39.068-06:00  INFO 2958139 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8090 (http)
2025-05-15T00:07:39.076-06:00  INFO 2958139 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2025-05-15T00:07:39.076-06:00  INFO 2958139 --- [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.40]
2025-05-15T00:07:39.096-06:00  INFO 2958139 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2025-05-15T00:07:39.096-06:00  INFO 2958139 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 734 ms
2025-05-15T00:07:39.186-06:00  WARN 2958139 --- [  restartedMain] .s.s.UserDetailsServiceAutoConfiguration : 

Using generated security password: 5a4a254a-cabe-43df-85a2-de1fd0d9a375

This generated password is for development use only. Your security configuration must be updated before running your application in production.

2025-05-15T00:07:39.200-06:00  INFO 2958139 --- [  restartedMain] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name inMemoryUserDetailsManager
2025-05-15T00:07:39.393-06:00 DEBUG 2958139 --- [  restartedMain] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with filters: DisableEncodeUrlFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CsrfFilter, LogoutFilter, UsernamePasswordAuthenticationFilter, DefaultResourcesFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter
2025-05-15T00:07:39.608-06:00  INFO 2958139 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2025-05-15T00:07:39.629-06:00  INFO 2958139 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8090 (http) with context path '/'
2025-05-15T00:07:39.635-06:00  INFO 2958139 --- [  restartedMain] c.t.n.NewMarketplaceApplication          : Started NewMarketplaceApplication in 1.587 seconds (process running for 1.967)
2025-05-15T00:08:32.604-06:00  INFO 2958139 --- [nio-8090-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-05-15T00:08:32.604-06:00  INFO 2958139 --- [nio-8090-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2025-05-15T00:08:32.605-06:00  INFO 2958139 --- [nio-8090-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
2025-05-15T00:08:32.616-06:00 DEBUG 2958139 --- [nio-8090-exec-1] o.s.security.web.FilterChainProxy        : Securing GET /
2025-05-15T00:08:32.621-06:00 DEBUG 2958139 --- [nio-8090-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2025-05-15T00:08:32.634-06:00 DEBUG 2958139 --- [nio-8090-exec-1] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://localhost:8090/?continue to session
2025-05-15T00:08:32.634-06:00 DEBUG 2958139 --- [nio-8090-exec-1] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://localhost:8090/login
2025-05-15T00:08:32.650-06:00 DEBUG 2958139 --- [nio-8090-exec-2] o.s.security.web.FilterChainProxy        : Securing GET /login
2025-05-15T00:08:32.742-06:00 DEBUG 2958139 --- [nio-8090-exec-3] o.s.security.web.FilterChainProxy        : Securing GET /default-ui.css
2025-05-15T00:08:32.778-06:00 DEBUG 2958139 --- [nio-8090-exec-4] o.s.security.web.FilterChainProxy        : Securing GET /favicon.ico
2025-05-15T00:08:32.779-06:00 DEBUG 2958139 --- [nio-8090-exec-4] o.s.security.web.FilterChainProxy        : Secured GET /favicon.ico
2025-05-15T00:08:32.787-06:00 DEBUG 2958139 --- [nio-8090-exec-4] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext

请求状态截图

请求状态码截图


解决方案

问题根源

Spring Security的过滤器链优先级高于方法级安全校验。配置中.anyRequest().authenticated()要求所有非白名单请求必须已认证,匿名访问时未通过这一层校验,直接被ExceptionTranslationFilter拦截触发登录重定向(302),根本没机会执行控制器上的@PreAuthorize注解逻辑。

另外,hasRole('ANONYMOUS')用法有误:Spring Security默认给匿名用户的是ROLE_ANONYMOUS角色,hasRole会自动添加ROLE_前缀,实际判断的是ROLE_ANONYMOUS,更规范的写法是用isAnonymous()。

修复步骤

1. 调整安全配置,让方法级校验生效

将根路径/加入白名单,允许匿名访问,确保请求能到达控制器执行@PreAuthorize校验:

@EnableWebSecurity(debug = true)
@EnableMethodSecurity
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filter(HttpSecurity http) throws Exception {
        var whitelist = new String[]{
            "/", // 新增根路径到白名单
            "/styles/**",
            "/listing-images/**",
            "/thumbnail-images/**",
            "/login",
            "/favicon.ico"
        };

        return http
            .authorizeHttpRequests(a -> a
                .requestMatchers(whitelist).permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults())
            .build();
    }
}

2. 修正@PreAuthorize表达式

把hasRole('ANONYMOUS')改成isAnonymous(),符合Spring Security规范:

@Controller
@RequestMapping("/")
public class AppController {
    @GetMapping
    @PreAuthorize("isAnonymous()")
    public String home() {
        return "home";
    }
}

3. (可选)自定义权限错误响应

如果希望匿名访问不符合权限时返回403状态码,可配置异常处理:

return http
    .authorizeHttpRequests(a -> a
        .requestMatchers(whitelist).permitAll()
        .anyRequest().authenticated()
    )
    .formLogin(Customizer.withDefaults())
    .exceptionHandling(e -> e
        .accessDeniedHandler((request, response, ex) -> {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            // Thymeleaf场景可跳转403错误页,API场景可返回JSON
            response.sendRedirect("/error/403");
        })
    )
    .build();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 03:35:56