# 登录与注销
# 登录(是谁?)
所谓登录,指的是:用户提交登录凭据,比如:用户名密码等信息,后台通过与数据库比对后,确定账户正常,准予登录,生成认证成功凭据(如: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();
}
被动注销由系统定时任务实现,当闲置时间超过规定时间后,系统会自动将过期凭据作废;