springboot实现密码过期,以及存储5次密码,第六次密码不能与前五次相同
增加是否启用密码过期配置 application.yml
#密码有效期开关
passwordExpire: true
#密码剩余天数提醒修改密码
passwordExpireTip: '#{3*24*60*60}'
1.已存在用户,第一次登录时判断是否存在redis密码过期设置和存储密码list,不存在,则生成redis信息,不存在,跳过。
代码示例:
@Value(value = "${hhxxweb.passwordExpire}")
private boolean passwordExpire;
@PostMapping("/login")
@ResponseBody
public AjaxResult ajaxLogin(HttpServletRequest request,String username, String password, Boolean rememberMe)
{
try {
String id = request.getSession().getId();
byte[] aesKey = StringUtils.hexToByteArray(RedisTool.get(String.format(Constants.LOGIN_AES_KEY, id), String.class));
if (ArrayUtil.isEmpty(aesKey)){
return error("登录超时,请刷新页面重试");
}
username = Md5Utils.decryptAESCtr(StringUtils.hexToByteArray(username), aesKey);
password = Md5Utils.decryptAESCtr(StringUtils.hexToByteArray(password), aesKey);
if(passwordExpire) {
if(!"zlxt_admin".equals(username)) {
passwordExpire(username, password);
String redisPassword = redisCache.getCacheObject("password:expire:" + username);
if(StringUtils.isEmpty(redisPassword)) {
String msg = "密码已过期,请联系管理员修改!";
return error(msg);
}
}
}
passwordService.validateByUsername(username, password);
} catch (Exception ex) {
String msg = "用户或密码错误";
if (StringUtils.isNotEmpty(ex.getMessage()))
{
msg = ex.getMessage();
}
return error(msg);
}
UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
Subject subject = SecurityUtils.getSubject();
try
{
subject.login(token);
Long deptId = ShiroUtils.getSysUser().getDeptId();
return success(String.valueOf(deptId));
}
catch (AuthenticationException e)
{
String msg = "用户或密码错误";
if (StringUtils.isNotEmpty(e.getMessage()))
{
msg = e.getMessage();
}
return error(msg);
}
}
private void passwordExpire(String username, String password) {
List<Object> cacheList = redisCache.getCacheList("password:list:" + username);
if(StringUtils.isEmpty(cacheList)) {
redisCache.setCacheObject("password:expire:" + username, password, 180, TimeUnit.DAYS);
List<String> list = new LinkedList<String>();
list.add(password);
redisCache.setCacheList("password:list:" + username, list);
}
}
2.新增用户时,redis密码过期设置和存储密码list增加
@Value(value = "${hhxxweb.passwordExpire}")
private boolean passwordExpire;
/**
* 新增保存用户信息
*
* @param user 用户信息
* @return 结果
*/
@Override
@Transactional
public int insertUser(User user)
{
if(passwordExpire) {
redisCache.setCacheObject("password:expire:" + user.getLoginName(), user.getPassword(), 180, TimeUnit.DAYS);
List<String> list = new LinkedList<String>();
list.add(user.getPassword());
redisCache.setCacheList("password:list:" + user.getLoginName(), list);
}
user.randomSalt();
user.setPassword(passwordService.encryptPassword(user.getLoginName(), user.getPassword(), user.getSalt()));
user.setCreateBy(ShiroUtils.getLoginName());
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user.getUserId(), user.getRoleIds());
return rows;
}
3,重置用户密码时,刷新密码过期机制,重新恢复180天,并且判断密码list存储是否等于5个,如果等于,判断是否和前5次存在相同,存在相同,则抛出异常,不存在时,在重置密码实现类中删除最早的密码,保存新密码,重新生成redis的密码list。
@RequiresPermissions("system:user:resetPwd")
@Log(title = "重置密码", businessType = BusinessType.UPDATE)
@PostMapping("/resetPwd")
@ResponseBody
public AjaxResult resetPwdSave(User user)
{
userService.checkUserAllowed(user);
if(passwordExpire) {
List<String> cacheList = redisCache.getCacheList("password:list:" + user.getLoginName());
if(cacheList.size() == 5) {
boolean anyMatch = cacheList.stream().anyMatch(e -> user.getPassword().equals(e));
if(anyMatch) {
return error("新密码与前五次密码出现相同,请重新设置密码!");
}
}
}
if(user.getPassword().contains(user.getLoginName())){
return error("密码不能包含登录名称!");
}
if(!userService.checkPassword(user.getPassword())){
if (strength){
return error("密码需包含数字、大写字母、小写字母、特殊字符!");
}else {
return error("密码需包含数字和字母");
}
}
if (userService.resetUserPwd(user) > 0)
{
if (ShiroUtils.getUserId().longValue() == user.getUserId().longValue())
{
setSysUser(userService.selectUserById(user.getUserId()));
}
return success();
}
return error();
}
接口实现类:
/**
* 修改用户密码
*
* @param user 用户信息
* @return 结果
*/
@Override
public int resetUserPwd(User user)
{
String username = user.getLoginName();
redisCache.setCacheObject("password:expire:" + username, user.getPassword(), 180, TimeUnit.DAYS);
List<String> cacheList = redisCache.getCacheList("password:list:" + username);
if(cacheList.size() == 5) {
cacheList = cacheList.stream().skip(1).collect(Collectors.toList());
}
cacheList.add(user.getPassword());
redisCache.deleteObject("password:list:" + username);
redisCache.setCacheList("password:list:" + username, cacheList);
user.randomSalt();
user.setPassword(passwordService.encryptPassword(username, user.getPassword(), user.getSalt()));
return updateUserInfo(user);
}
4.删除用户时,清除缓存
/**
* 通过用户ID删除用户
*
* @param userId 用户ID
* @return 结果
*/
@Override
@Transactional
public int deleteUserById(Long userId)
{
User selectUserById = userMapper.selectUserById(userId);
redisCache.deleteObject("password:list:" + selectUserById.getLoginName());
redisCache.deleteObject("password:expire:" + selectUserById.getLoginName());
// 删除用户与角色关联
userRoleMapper.deleteUserRoleByUserId(userId);
// 删除用户与岗位表
userPostMapper.deleteUserPostByUserId(userId);
return userMapper.deleteUserById(userId);
}
5.登录成功进入首页时判断密码过期时间是否小于三天,小于三天提示修改密码。
@Value(value = "${hhxxweb.passwordExpire}")
private boolean passwordExpire;
@Value(value = "${hhxxweb.passwordExpireTip}")
private String passwordExpireTip;
boolean isRestPwd = false;
if(passwordExpire) {
if(!"zlxt_admin".equals(user.getLoginName())) {
Long expire = redisTemplate.getExpire("password:expire:" + user.getLoginName());
if(expire < Long.valueOf(passwordExpireTip)) {
long days = TimeUnit.SECONDS.toDays(expire);
long hours = TimeUnit.SECONDS.toHours(expire) - TimeUnit.DAYS.toHours(days);
isRestPwd = true;
mmap.put("timeTip", "密码还有" + days + "天" + hours + "小时到期,点击确定修改密码!");
}
}
}
mmap.put("isRestPwd", isRestPwd);
6.前端部分代码
var isRestPwd = [[${isRestPwd}]] || false;
if(isRestPwd) {
$.modal.confirm(timeTip, function() {
resetPwd();
});
}
/* 用户管理-重置密码 */
function resetPwd() {
var url = ctx + 'system/user/profile/resetPwd';
$.modal.open("重置密码", url, '770', '380');
}