update sa-token 1.28.0 => 1.29.0 修复Xss注解字段值为空时的异常问题 删除方法无返回值时,方法注释上的@HTTP4O4 update 使用 satoken 自带的 BCrypt 工具 替换 Security 加密工具 减少依赖 优化流程任务方法,统一响应格式 fix 修复 R 参数位置错误 fix 修复 验证码 强制校验问题 update 返回体 统一 修复自定义组件file-upload无法显示第一个文件,列表显示的文件比实际文件少一个的问题 update 修改验证码校验 增加 uuid 空判断 update 优化代码生成 fix 修复因升级 sa-token 导致 doLogin 无法获取 token 问题 update 更新 swagger 配置类错误注释 update 优化 TreeBuildUtils 工具 使用反射自动获取顶级父id fix 回滚代码生成部分优化 修复优化导致的问题 update 使用 hutool Dict 优化 JsonUtils 防止类型解析异常 update 优化代码生成 使用新 JsonUtils.parseMap 方法
168 lines
7.1 KiB
Java
168 lines
7.1 KiB
Java
package com.ruoyi.system.service;
|
|
|
|
import cn.dev33.satoken.secure.BCrypt;
|
|
import cn.dev33.satoken.stp.StpUtil;
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
import com.ruoyi.common.constant.Constants;
|
|
import com.ruoyi.common.core.domain.dto.RoleDTO;
|
|
import com.ruoyi.common.core.domain.entity.SysUser;
|
|
import com.ruoyi.common.core.domain.model.LoginUser;
|
|
import com.ruoyi.common.core.service.LogininforService;
|
|
import com.ruoyi.common.enums.DeviceType;
|
|
import com.ruoyi.common.enums.UserStatus;
|
|
import com.ruoyi.common.exception.user.CaptchaException;
|
|
import com.ruoyi.common.exception.user.CaptchaExpireException;
|
|
import com.ruoyi.common.exception.user.UserException;
|
|
import com.ruoyi.common.helper.LoginHelper;
|
|
import com.ruoyi.common.utils.DateUtils;
|
|
import com.ruoyi.common.utils.MessageUtils;
|
|
import com.ruoyi.common.utils.ServletUtils;
|
|
import com.ruoyi.common.utils.StringUtils;
|
|
import com.ruoyi.common.utils.redis.RedisUtils;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import java.util.List;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/**
|
|
* 登录校验方法
|
|
*
|
|
* @author Lion Li
|
|
*/
|
|
@RequiredArgsConstructor
|
|
@Slf4j
|
|
@Service
|
|
public class SysLoginService {
|
|
|
|
private final ISysUserService userService;
|
|
private final ISysConfigService configService;
|
|
private final LogininforService asyncService;
|
|
private final SysPermissionService permissionService;
|
|
|
|
/**
|
|
* 登录验证
|
|
*
|
|
* @param username 用户名
|
|
* @param password 密码
|
|
* @param code 验证码
|
|
* @param uuid 唯一标识
|
|
* @return 结果
|
|
*/
|
|
public String login(String username, String password, String code, String uuid) {
|
|
HttpServletRequest request = ServletUtils.getRequest();
|
|
boolean captchaOnOff = configService.selectCaptchaOnOff();
|
|
// 验证码开关
|
|
if (captchaOnOff) {
|
|
validateCaptcha(username, code, uuid, request);
|
|
}
|
|
// 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip)
|
|
Integer errorNumber = RedisUtils.getCacheObject(Constants.LOGIN_ERROR + username);
|
|
// 锁定时间内登录 则踢出
|
|
if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(Constants.LOGIN_ERROR_NUMBER)) {
|
|
asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", Constants.LOGIN_ERROR_LIMIT_TIME), request);
|
|
throw new UserException("user.password.retry.limit.exceed", Constants.LOGIN_ERROR_LIMIT_TIME);
|
|
}
|
|
|
|
SysUser user = loadUserByUsername(username);
|
|
|
|
if (!BCrypt.checkpw(password, user.getPassword())) {
|
|
// 是否第一次
|
|
errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1;
|
|
// 达到规定错误次数 则锁定登录
|
|
if (errorNumber.equals(Constants.LOGIN_ERROR_NUMBER)) {
|
|
RedisUtils.setCacheObject(Constants.LOGIN_ERROR + username, errorNumber, Constants.LOGIN_ERROR_LIMIT_TIME, TimeUnit.MINUTES);
|
|
asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", Constants.LOGIN_ERROR_LIMIT_TIME), request);
|
|
throw new UserException("user.password.retry.limit.exceed", Constants.LOGIN_ERROR_LIMIT_TIME);
|
|
} else {
|
|
// 未达到规定错误次数 则递增
|
|
RedisUtils.setCacheObject(Constants.LOGIN_ERROR + username, errorNumber);
|
|
asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", errorNumber), request);
|
|
throw new UserException("user.password.retry.limit.count", errorNumber);
|
|
}
|
|
}
|
|
|
|
// 登录成功 清空错误次数
|
|
RedisUtils.deleteObject(Constants.LOGIN_ERROR + username);
|
|
asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
|
|
recordLoginInfo(user.getUserId(), username);
|
|
|
|
LoginUser loginUser = buildLoginUser(user);
|
|
|
|
// 生成token
|
|
LoginHelper.loginByDevice(loginUser, DeviceType.PC);
|
|
return StpUtil.getTokenValue();
|
|
}
|
|
|
|
/**
|
|
* 校验验证码
|
|
*
|
|
* @param username 用户名
|
|
* @param code 验证码
|
|
* @param uuid 唯一标识
|
|
*/
|
|
public void validateCaptcha(String username, String code, String uuid, HttpServletRequest request) {
|
|
String verifyKey = Constants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
|
|
String captcha = RedisUtils.getCacheObject(verifyKey);
|
|
RedisUtils.deleteObject(verifyKey);
|
|
if (captcha == null) {
|
|
asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request);
|
|
throw new CaptchaExpireException();
|
|
}
|
|
if (!code.equalsIgnoreCase(captcha)) {
|
|
asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request);
|
|
throw new CaptchaException();
|
|
}
|
|
}
|
|
|
|
private SysUser loadUserByUsername(String username) {
|
|
SysUser user = userService.selectUserByUserName(username);
|
|
if (ObjectUtil.isNull(user)) {
|
|
log.info("登录用户:{} 不存在.", username);
|
|
throw new UserException("user.not.exists", username);
|
|
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
|
|
log.info("登录用户:{} 已被删除.", username);
|
|
throw new UserException("user.password.delete", username);
|
|
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
|
log.info("登录用户:{} 已被停用.", username);
|
|
throw new UserException("user.blocked", username);
|
|
}
|
|
return user;
|
|
}
|
|
|
|
/**
|
|
* 构建登录用户
|
|
*/
|
|
private LoginUser buildLoginUser(SysUser user) {
|
|
LoginUser loginUser = new LoginUser();
|
|
loginUser.setUserId(user.getUserId());
|
|
loginUser.setDeptId(user.getDeptId());
|
|
loginUser.setUsername(user.getUserName());
|
|
loginUser.setNickName(user.getNickName());
|
|
loginUser.setUserType(user.getUserType());
|
|
loginUser.setMenuPermission(permissionService.getMenuPermission(user));
|
|
loginUser.setRolePermission(permissionService.getRolePermission(user));
|
|
loginUser.setDeptName(user.getDept().getDeptName());
|
|
List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
|
|
loginUser.setRoles(roles);
|
|
return loginUser;
|
|
}
|
|
|
|
/**
|
|
* 记录登录信息
|
|
*
|
|
* @param userId 用户ID
|
|
*/
|
|
public void recordLoginInfo(Long userId, String username) {
|
|
SysUser sysUser = new SysUser();
|
|
sysUser.setUserId(userId);
|
|
sysUser.setLoginIp(ServletUtils.getClientIP());
|
|
sysUser.setLoginDate(DateUtils.getNowDate());
|
|
sysUser.setUpdateBy(username);
|
|
userService.updateUserProfile(sysUser);
|
|
}
|
|
}
|