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

Spring Boot表单登录前出现401 Unauthorized错误求助

问题分析与解决方案

问题根源

你遇到的核心问题是:自定义的authenticationEntryPoint覆盖了Spring Security默认的表单登录入口逻辑。

默认情况下,未认证用户访问受保护资源时,Spring Security会自动重定向到表单登录页面。但你通过exceptionHandling(ex -> ex.authenticationEntryPoint(...))替换了默认的入口处理器,自定义逻辑仅做了日志记录,未触发登录页重定向,直接返回401 Unauthorized,导致用户看不到登录表单。

另外,配置中两次调用exceptionHandling,第二次会覆盖第一次的配置(包括accessDeniedHandler),这也是潜在问题。

修复方案

方案1:合并异常处理配置,保留默认表单登录逻辑

保留默认的表单登录入口处理器,同时在自定义逻辑中补充日志记录:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // 获取默认表单登录入口处理器
    LoginUrlAuthenticationEntryPoint formLoginEntryPoint = new LoginUrlAuthenticationEntryPoint("/login");
    
    http.authorizeHttpRequests(authorize -> authorize
                    .requestMatchers("/admin/**").hasRole("ADMIN")
                    .requestMatchers("/**").hasRole("USER"))
            .formLogin(withDefaults())
            .httpBasic(withDefaults())
            .exceptionHandling(exceptions -> exceptions
                    .accessDeniedHandler(new CustomDeniedHandler())
                    .authenticationEntryPoint((request, response, authException) -> {
                        // 记录登录失败日志
                        String username = "unknown intruder";
                        if (authException instanceof BadCredentialsException) {
                            username = request.getParameter("user");
                        }
                        logWriter.writeLog(HttpStatus.UNAUTHORIZED,
                                new LogRequest("Login denied", username, LocalDateTime.now()));
                        
                        // 执行默认表单登录重定向逻辑
                        formLoginEntryPoint.commence(request, response, authException);
                    }));
    return http.build();
}

方案2:用DelegatingAuthenticationEntryPoint适配多认证场景

如果需要同时支持表单登录和HTTP Basic两种认证的不同入口逻辑,可通过该类分发处理:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // 配置不同请求对应的入口处理器
    LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
    // 表单登录相关请求用默认入口
    entryPoints.put(new AntPathRequestMatcher("/login/**"), new LoginUrlAuthenticationEntryPoint("/login"));
    // HTTP Basic请求用默认Basic入口
    entryPoints.put(AnyRequestMatcher.INSTANCE, new BasicAuthenticationEntryPoint());
    
    DelegatingAuthenticationEntryPoint delegatingEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
    delegatingEntryPoint.setDefaultEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));

    http.authorizeHttpRequests(authorize -> authorize
                    .requestMatchers("/admin/**").hasRole("ADMIN")
                    .requestMatchers("/**").hasRole("USER"))
            .formLogin(withDefaults())
            .httpBasic(withDefaults())
            .exceptionHandling(exceptions -> exceptions
                    .accessDeniedHandler(new CustomDeniedHandler())
                    .authenticationEntryPoint((request, response, authException) -> {
                        // 记录日志
                        String username = "unknown intruder";
                        if (authException instanceof BadCredentialsException) {
                            username = request.getParameter("user");
                        }
                        logWriter.writeLog(HttpStatus.UNAUTHORIZED,
                                new LogRequest("Login denied", username, LocalDateTime.now()));
                        
                        // 交给分发器处理对应入口逻辑
                        delegatingEntryPoint.commence(request, response, authException);
                    }));
    return http.build();
}

额外注意点

  • 不要重复调用exceptionHandling,否则后续配置会覆盖之前的设置。
  • 确保CustomDeniedHandler(处理403权限不足)被配置在同一个exceptionHandling块中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 17:07:06