第 41 章 锁定用户

这里我们通过一个常见的功能需求来演示如何锁定一个用户的账号。

下面我们需要实现这样一个功能,当一个用户输错三次密码后,就锁定这个账号。

首先我们要注意到,虽然UserDetails中提供了isAccountNonLocked()方法,框架中也提供了LockedException,但是默认提供的User实现类不太容易实现对用户锁定的操作,为此,我们需要借用???中定义的UserInfo来实现对用户的锁定。

为了监听用户输入错误密码的事件,我们需要自定义一个事件监听器LockUserListener,它的主体代码如下所示:

public void onApplicationEvent(ApplicationEvent event) {

    if (event instanceof AuthenticationFailureBadCredentialsEvent) {
        AuthenticationFailureBadCredentialsEvent authEvent = (AuthenticationFailureBadCredentialsEvent) event;
        Authentication authentication = (Authentication) authEvent.getSource();
        String username = (String) authentication.getPrincipal();
        addCount(username);
    }

    if (event instanceof AuthenticationSuccessEvent) {
        AuthenticationSuccessEvent authEvent = (AuthenticationSuccessEvent) event;
        Authentication authentication = (Authentication) authEvent.getSource();
        UserInfo userInfo = (UserInfo) authentication.getPrincipal();
        String username = userInfo.getUsername();
        cleanCount(username);
    }

}
    

其中,AuthenticationFailureBadCredentialsEvent事件表示用户输入了错误的密码,AuthenticationSuccessEvent表示用户登录成功。

将LockUserListener添加到配置文件中即可监听权限校验的事件。

<beans:bean id="lockUserListener" class="com.family168.springsecuritybook.ch209.LockUserListener">
    <beans:property name="servletContext" ref="servletContext"/>
    <beans:property name="userInfoService" ref="userInfoService"/>
</beans:bean>
    

在获得AuthenticationFailureBadCredentialsEvent事件,也即用户输入错密码时,我们首先要获得对应的用户名,然后将用户名对应的输入密码错误次数加一。当获得AuthenticationSuccessEvent事件时,说明用户已经成功登陆了,这时我们要把用户之前输入错误的密码次数清零,让他再次登录时还可以享有三次错误的机会。

这里为了演示方便,直接将用户输入密码错误的次数记录在ServletContext中,代码如下所示:

protected void addCount(String username) {
    Map<String, Integer> lockUserMap = getLockUserMap();
    Integer count = lockUserMap.get(username);
    if (count == null) {
        lockUserMap.put(username, Integer.valueOf(1));
    } else {
        int resultCount = count.intValue() + 1;
        if (resultCount >= 3) {
            UserInfo userInfo = (UserInfo) userInfoService.loadUserByUsername(username);
            userInfo.lockAccount();
        } else {
            lockUserMap.put(username, Integer.valueOf(resultCount));
        }
    }
}
    

当用户输错密码超过三次时,就会从UserInfoService中取出对应的UserInfo对象,使用lockAccount()方法将该用户账号锁定,之后用户就会在登录上看到对应的错误信息。

用户已被锁定

图 41.1. 用户已被锁定


之后用户再怎么尝试也无法登陆到系统中了。

实例在ch209中。

用户过期与密码过期的处理方法与锁定用户类似。