# 登录与注销

# 登录(是谁?)

所谓登录,指的是:用户提交登录凭据,比如:用户名密码等信息,后台通过与数据库比对后,确定账户正常,准予登录,生成认证成功凭据(如:SessionId、Token等)返回给用户,用户后续访问系统资源携带此凭据可直接访问而无需再次登录。

在 Heimdall框架中,通过 UserDetails 实体具体描述一个认证实体信息,通俗的说就是描述:登录的是谁? UserDetails 定义如下:

public interface UserDetails extends Serializable {
    /**
     * 用这个数据判断用户是否登录,可以是用户id,手机号、用户名等等,只要能全局唯一标识这个用户即可
     * <p>
     * eg:
     * 1、 租户ID+用户名称: 这样,同一个用户名称不同租户的用户可以各自登录不受影响
     * <p>
     * 2、设备类型+用户ID: 同一个用户,可从不同终端登录,而不至于被踢下线
     *
     * @return the principal
     */
    String getPrincipal();

    /**
     * 用这个判断用户是否启用
     *
     * @return the boolean
     */
    boolean enabled();
}

  • principal :主要认证凭据,Heimdall 通过principal来唯一标识一个认证实体。
  • enabled : 是否启用,如果 false,Heimdall 会拒绝此实体认证通过,也就是拒绝登录。

Heimdall 框架不对用户的密码以及其他信息进行验证,Heimdall 只负责对认证的状态进行管理和维护。

第三方系统使用 Heimdall 进行认证,将 UserDetails 提交给 Heimdall 进行管理,在此之前,用户是否有效、用户口令是否正确, 都应该在提交给 Heimdall 之前由第三方系统完成。

参考如下代码:

//绑定认证管理器
@Autowired
private AuthenticationManager authenticationManager;
//登录
public Serializable login(SysUserVO user) {
         //参数校验,也可以注解校验
        if (StrUtil.isBlank(user.getUsername()) || StrUtil.isBlank(user.getPassword())) {
            throw new AccountException("用户名或者密码不能为空,请输入");
        }
        //从数据库查找用户
        final SysUserEntity sysUserEntityByUsername = repository.findSysUserEntityByUsername(user.getUsername());
        if (null == sysUserEntityByUsername) {
            throw new AccountException("用户名密码错误:Non");
        }
        //账号状态判断
        if (sysUserEntityByUsername.getLocked()) {
            throw new HeimdallException("账号被锁定,请联系管理员处理");
        }
        //图形验证码校验
        if (!captchaService.checkCaptcha(user.getUuid(), user.getCaptcha())) {
            throw new HeimdallException("验证码错误");
        }
        //校验密码
        //错误尝试次数限定:不管密码对不对,都记录一次登录尝试,也可以放在验证码校验之前
        //取决于如何确定成功失败的逻辑
//        retryLimit.increase(sysUserEntityByUsername.getUsername());
        if (!passwordEncoder.matches(user.getPassword(), sysUserEntityByUsername.getPassword())) {
            //错误尝试次数限定:密码不对,记录一次
            retryLimit.increase(sysUserEntityByUsername.getUsername());
            final int leftCount = retryLimit.leftCount(sysUserEntityByUsername.getUsername());
            if (leftCount == 0) {
                throw new HeimdallException("用户名密码错误,你已经剩下最后一次机会了,千万输入正确啊....");
            }
            throw new HeimdallException("用户名密码错误");
        }
        //转成 DTO
        final SysUserDTO userDTO = mapper.toDto(sysUserEntityByUsername);
        //构造 UserDetails
        AppUserDetails userDetails = new AppUserDetails(userDTO);
        //执行登录
        final SimpleSession session = authenticationManager.login(userDetails);
        //认证通过后,把密码重试缓存清理掉
        retryLimit.remove(sysUserEntityByUsername.getUsername());
        //把 SessionId 返回
        return session.getId();
    }


# 注销

注销操作分为主动注销和被动注销两种。

  • 主动注销 : 由用户发起,调用系统注销接口,作废已经颁发的合法认证凭据。
  • 被动注销 : 用户长时间未发生操作,闲置时间过长,出于安全考虑系统主动将用户认证凭据作废; 在注销成功后,用户后续访问系统资源,均需要重新登录认证。 用户主动注销实现:
//绑定认证管理器
@Autowired
private AuthenticationManager authenticationManager;
//注销登录
public SimpleSession logout() {
 return authenticationManager.logout();
}

被动注销由系统定时任务实现,当闲置时间超过规定时间后,系统会自动将过期凭据作废;

上次更新:: 1/25/2021, 4:26:40 PM