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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 11:17:44