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
相关产品推荐
相关产品推荐

