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

登录失败时获取用户详情并统计失败次数的实现方案咨询

如何在Spring Security登录失败时获取用户详情并保存到数据库?

首先明确回答你的疑惑:AuthenticationFailureHandler 并不只适用于formLogin,它是Spring Security中处理认证失败逻辑的通用接口——只要认证过程抛出AuthenticationException,不管你用的是表单登录、自定义JWT登录端点还是其他认证方式,都可以用它来处理失败场景。不过你的代码是自定义的登录端点,不是依赖Spring Security的默认formLogin流程,所以需要稍微调整实现方式。

下面结合你的现有代码,给出具体的实现步骤:

1. 自定义AuthenticationFailureHandler处理失败逻辑

先创建一个实现AuthenticationFailureHandler的类,在这里完成用户失败次数统计、保存到数据库的逻辑:

@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {

    private static final Logger logger = LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);
    
    // 注入你的用户Repository或服务类,用于更新用户失败次数
    @Autowired
    private UserRepository userRepository;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        logger.info("> CustomAuthenticationFailureHandler triggered for login failure");
        
        // 解析请求体中的LoginRequest,获取用户名
        ObjectMapper objectMapper = new ObjectMapper();
        LoginRequest loginRequest = objectMapper.readValue(request.getInputStream(), LoginRequest.class);
        String username = loginRequest.getUsername();
        
        // 根据用户名查找用户并更新失败次数
        Optional<User> userOptional = userRepository.findByUsername(username);
        if (userOptional.isPresent()) {
            User user = userOptional.get();
            // 自增失败次数,同时记录最后失败时间
            user.setLoginFailureCount(user.getLoginFailureCount() + 1);
            user.setLastLoginFailureTime(LocalDateTime.now());
            userRepository.save(user);
            logger.info("Updated login failure count to {} for user: {}", user.getLoginFailureCount(), username);
        } else {
            logger.warn("Attempted login with non-existent username: {}", username);
        }
        
        // 返回自定义的JSON错误响应(替代默认的sendError)
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        objectMapper.writeValue(response.getWriter(), new ApiResponse(false, "Login failed: " + exception.getMessage()));
    }
}

2. 修改登录端点,捕获认证异常并调用FailureHandler

你的登录端点是手动调用authenticationManager.authenticate(),这个方法在认证失败时会抛出AuthenticationException,所以需要在端点中捕获这个异常,然后调用我们自定义的失败处理器:

@CrossOrigin
@RequestMapping(value = "/signin", method = RequestMethod.POST)
@ApiOperation(value = "Sign in endpoint", notes = "You have to provide a valid login request")
public ResponseEntity<?> authenticateUser(
        @ApiParam(value = "The login request", required = true) 
        @Valid @RequestBody LoginRequest loginRequest,
        HttpServletRequest request,
        HttpServletResponse response,
        @Autowired CustomAuthenticationFailureHandler failureHandler) { // 注入失败处理器
    try {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsername(), 
                        loginRequest.getPassword()));

        UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
        // 这里保留你原来的登录成功逻辑(生成JWT、返回响应等)
        // ...
        
        return ResponseEntity.ok(new JwtResponse(/* 你的JWT响应内容 */));
    } catch (AuthenticationException authEx) {
        // 调用自定义失败处理器处理逻辑
        failureHandler.onAuthenticationFailure(request, response, authEx);
        // 因为失败处理器已经处理了响应输出,这里返回null即可
        return null;
    }
}

3. 区分AuthEntryPointJwt和AuthenticationFailureHandler的作用

你现有的AuthEntryPointJwt是用来处理未认证用户访问受保护资源的场景(比如用户没带JWT token就访问/test/**),而登录失败是用户主动提交登录请求但凭证错误的场景——这两个是完全不同的认证环节,不要混淆使用。

额外优化建议

  • 确保LoginRequest类有默认构造函数和字段的getter/setter,否则ObjectMapper无法正确解析请求体。
  • 可以增加登录失败次数限制逻辑,比如失败超过5次就锁定账号,提升系统安全性。
  • 更新用户失败次数时,建议加上事务注解(@Transactional),保证数据库操作的原子性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 08:52:57