Vaadin24(Java)中如何为用户添加登录尝试次数统计与限制
解决真实登录尝试次数统计问题
你之前在beforeEnter方法中统计登录次数的问题在于:这个方法只要进入登录页面就会触发,不管用户有没有提交表单,而且会话内的计数无法跨会话追踪用户的真实尝试次数。要统计用户提交含凭证的表单的真实尝试,必须把统计逻辑放到表单提交的处理流程中。
具体实现步骤
1. 实现登录尝试统计服务
首先创建一个专门的服务类,负责持久化用户的登录尝试次数(示例用内存存储,实际项目建议用数据库或Redis实现跨会话/跨服务器的统计):
@Service public class LoginAttemptService { // 实际项目替换为数据库操作或Redis存储 private final ConcurrentHashMap<String, Integer> attemptCache = new ConcurrentHashMap<>(); // 递增指定用户的尝试次数 public void incrementAttempt(String username) { attemptCache.put(username, attemptCache.getOrDefault(username, 0) + 1); // 示例:如果用JPA,可调用 repository.incrementAttemptsByUsername(username); } // 获取指定用户的尝试次数 public int getAttemptCount(String username) { return attemptCache.getOrDefault(username, 0); } }
2. 修改LoginView,添加表单提交处理逻辑
在LoginView中注入统计服务,新增处理表单POST提交的方法,把统计逻辑放在这里:
@AnonymousAllowed @PageTitle("Login") @Route(value = "login") @RouteAlias(value = "") public class LoginView extends LoginOverlay implements BeforeEnterObserver { private final AuthenticatedUser authenticatedUser; private final LoginAttemptService loginAttemptService; public LoginView(AuthenticatedUser authenticatedUser, LoginAttemptService loginAttemptService) { this.authenticatedUser = authenticatedUser; this.loginAttemptService = loginAttemptService; setAction(RouteUtil.getRoutePath(VaadinService.getCurrent().getContext(), getClass())); // 原有国际化配置保留 LoginI18n i18n = LoginI18n.createDefault(); i18n.setHeader(new LoginI18n.Header()); i18n.getHeader().setTitle("Guard"); i18n.getHeader().setDescription("Enter login and pass"); i18n.setAdditionalInformation(null); i18n.getForm().setTitle("Authorisation"); i18n.getForm().setUsername("Login"); i18n.getForm().setPassword("Pass"); i18n.getForm().setSubmit("Enter"); setI18n(i18n); setForgotPasswordButtonVisible(false); setOpened(true); } @Override public void beforeEnter(BeforeEnterEvent event) { // 原有已登录用户跳转逻辑保留 if (authenticatedUser.get().isPresent()) { setOpened(false); UserEntity user = authenticatedUser.get().get(); System.out.println("------- "+user.toString()); if (user.getRole().equals(Role.ADMIN)) { event.forwardTo("admin"); } else { event.forwardTo("home"); } } setError(event.getLocation().getQueryParameters().getParameters().containsKey("error")); } // 处理登录表单的POST提交 @PostMapping public void handleLoginSubmit(HttpServletRequest request, HttpServletResponse response) throws IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); // 统计真实登录尝试次数 loginAttemptService.incrementAttempt(username); System.out.println("用户[" + username + "]第" + loginAttemptService.getAttemptCount(username) + "次尝试登录"); // 执行认证逻辑 try { authenticatedUser.login(username, password); // 认证成功,跳转首页/对应页面 response.sendRedirect(request.getContextPath() + "/"); } catch (AuthenticationException e) { // 认证失败,返回登录页并显示错误 response.sendRedirect(request.getContextPath() + "/login?error"); } } }
关键说明
- 统计时机准确:只有当用户提交含用户名和密码的表单时,
handleLoginSubmit方法才会触发,完全避免了进入页面就计数的问题。 - 持久化统计:替换
LoginAttemptService中的内存存储为数据库或Redis,就能实现跨会话、跨服务器的用户登录尝试追踪。 - 灵活调整统计规则:如果只需要统计登录失败的次数,把
incrementAttempt调用移到catch块中即可。
内容的提问来源于stack exchange,提问作者Andy Bay
相关产品推荐
相关产品推荐

