Spring Security 登录失败后如何停止跳转到登录页面

Posted

技术标签:

【中文标题】Spring Security 登录失败后如何停止跳转到登录页面【英文标题】:How to stop being diverted to login page after unsuccessful login in Spring Security 【发布时间】:2021-07-26 13:15:20 【问题描述】:

我有一个带有映射的 Spring Boot 应用程序:

@GetMapping(path = "/users/username")
public User getUser(@PathVariable String username) 
    Optional<User> userOptional = userService.getByUsername(username);
    return userOptional.orElseThrow(() -> new UserNotFoundException("Username " + username + " is not found."));

我还有一个自定义异常处理程序:

@ExceptionHandler(UserNotFoundException.class)
public final ResponseEntity<Object> handleUserNotFoundException(Exception ex, WebRequest request) 
    var exception = new ExceptionResponse(LocalDateTime.now(), ex.getMessage(), request.getDescription(false));
    return new ResponseEntity<>(exception, HttpStatus.FORBIDDEN);

我的 WebSecurityConfig 是这样的:

    http.cors().and().csrf().disable()
            .authorizeRequests()
             // Many matchers here
             // .....
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginProcessingUrl("/authenticate").usernameParameter("user").passwordParameter("pwd").permitAll();

然后我尝试使用不存在的用户名登录。

我所期望的是错误的单个 JSON 表示。但是,我总是会重新转到登录页面。

有人可以建议我如何防止 Spring Security 转移我的注意力,而是只返回 JSON 表单中的 ResponseEntity 吗?

【问题讨论】:

【参考方案1】:

您的应用程序可能比您共享的内容更多,但如果登录页面中的用户名无效,您的@GetMapping 将不会被调用,您的@ExceptionHandler 也不会被调用。这是因为 Spring Security 过滤器链永远不允许在身份验证之前调用您的应用程序代码。

为了处理身份验证错误,您需要注册一个AuthenticationFailureHandler,其中需要手动编写 json 响应。例如:

    public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler 

        private final ObjectMapper objectMapper = new ObjectMapper()
                .registerModule(new JavaTimeModule())
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException 
            ExceptionResponse exceptionResponse = new ExceptionResponse(LocalDateTime.now(), exception.getMessage(), String.format("uri=%s", request.getServletPath()));
            String json = objectMapper.writeValueAsString(exceptionResponse);
            response.getWriter().write(json);
            response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        

    

您可以直接使用 formLogin() 配置注册:

http
    // ...
    .formLogin()
    .loginProcessingUrl("/authenticate").usernameParameter("user").passwordParameter("pwd").permitAll()
    .failureHandler(new CustomAuthenticationFailureHandler());

查看docs,了解有关过滤器链如何处理身份验证请求的更多信息。

【讨论】:

以上是关于Spring Security 登录失败后如何停止跳转到登录页面的主要内容,如果未能解决你的问题,请参考以下文章

使用 Grails Spring Security Rest 插件时如何获取客户端登录失败的原因?

Spring Boot + Spring Security Restful 登录

防止 Spring Security 在登录/注销后进行 302 重定向

使用 Spring Security 登录失败

使用 Spring Security 成功登录后如何正确更新登录日期时间?

为什么Spring Security看不见登录失败或者注销的提示