2025-06-28 16:28:15 +08:00
|
|
|
|
package com.fuyuanshen.app.service;
|
|
|
|
|
|
|
|
|
|
import cn.dev33.satoken.exception.NotLoginException;
|
|
|
|
|
import cn.dev33.satoken.stp.StpUtil;
|
|
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
|
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
|
|
|
import com.fuyuanshen.app.domain.vo.AppRoleVo;
|
|
|
|
|
import com.fuyuanshen.app.domain.vo.AppUserVo;
|
|
|
|
|
import com.fuyuanshen.common.core.constant.Constants;
|
|
|
|
|
import com.fuyuanshen.common.core.constant.SystemConstants;
|
|
|
|
|
import com.fuyuanshen.common.core.constant.TenantConstants;
|
|
|
|
|
import com.fuyuanshen.common.core.domain.dto.RoleDTO;
|
|
|
|
|
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
|
|
|
|
|
import com.fuyuanshen.common.core.enums.LoginType;
|
|
|
|
|
import com.fuyuanshen.common.core.exception.user.UserException;
|
|
|
|
|
import com.fuyuanshen.common.core.utils.MessageUtils;
|
|
|
|
|
import com.fuyuanshen.common.core.utils.ServletUtils;
|
|
|
|
|
import com.fuyuanshen.common.core.utils.SpringUtils;
|
|
|
|
|
import com.fuyuanshen.common.core.utils.StringUtils;
|
|
|
|
|
import com.fuyuanshen.common.log.event.LogininforEvent;
|
|
|
|
|
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
2025-06-30 15:29:49 +08:00
|
|
|
|
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
|
2025-06-28 16:28:15 +08:00
|
|
|
|
import com.fuyuanshen.common.satoken.utils.LoginHelper;
|
|
|
|
|
import com.fuyuanshen.common.tenant.exception.TenantException;
|
|
|
|
|
import com.fuyuanshen.common.tenant.helper.TenantHelper;
|
|
|
|
|
import com.fuyuanshen.system.domain.vo.SysTenantVo;
|
|
|
|
|
import com.fuyuanshen.system.service.ISysTenantService;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
import java.time.Duration;
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.util.function.Supplier;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 登录校验方法
|
|
|
|
|
*
|
|
|
|
|
* @author Lion Li
|
|
|
|
|
*/
|
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
|
@Slf4j
|
|
|
|
|
@Service
|
|
|
|
|
public class AppLoginService {
|
|
|
|
|
|
|
|
|
|
@Value("${user.password.maxRetryCount}")
|
|
|
|
|
private Integer maxRetryCount;
|
|
|
|
|
|
|
|
|
|
@Value("${user.password.lockTime}")
|
|
|
|
|
private Integer lockTime;
|
|
|
|
|
|
|
|
|
|
private final ISysTenantService tenantService;
|
2025-07-01 14:12:41 +08:00
|
|
|
|
private final IAppRoleService appRoleService;
|
2025-06-28 16:28:15 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 登录账户密码错误次数 redis key
|
|
|
|
|
*/
|
|
|
|
|
String PWD_ERR_CNT_KEY = "app_pwd_err_cnt:";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 退出登录
|
|
|
|
|
*/
|
|
|
|
|
public void logout() {
|
|
|
|
|
try {
|
2025-06-30 15:29:49 +08:00
|
|
|
|
AppLoginUser loginUser = AppLoginHelper.getLoginUser();
|
2025-06-28 16:28:15 +08:00
|
|
|
|
if (ObjectUtil.isNull(loginUser)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
|
|
|
|
|
// 超级管理员 登出清除动态租户
|
|
|
|
|
TenantHelper.clearDynamic();
|
|
|
|
|
}
|
|
|
|
|
recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
|
|
|
|
|
} catch (NotLoginException ignored) {
|
|
|
|
|
} finally {
|
|
|
|
|
try {
|
|
|
|
|
StpUtil.logout();
|
|
|
|
|
} catch (NotLoginException ignored) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 记录登录信息
|
|
|
|
|
*
|
|
|
|
|
* @param tenantId 租户ID
|
|
|
|
|
* @param username 用户名
|
|
|
|
|
* @param status 状态
|
|
|
|
|
* @param message 消息内容
|
|
|
|
|
*/
|
|
|
|
|
public void recordLogininfor(String tenantId, String username, String status, String message) {
|
|
|
|
|
LogininforEvent logininforEvent = new LogininforEvent();
|
|
|
|
|
logininforEvent.setTenantId(tenantId);
|
|
|
|
|
logininforEvent.setUsername(username);
|
|
|
|
|
logininforEvent.setStatus(status);
|
|
|
|
|
logininforEvent.setMessage(message);
|
|
|
|
|
logininforEvent.setRequest(ServletUtils.getRequest());
|
|
|
|
|
SpringUtils.context().publishEvent(logininforEvent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 构建登录用户
|
|
|
|
|
*/
|
|
|
|
|
public AppLoginUser buildLoginUser(AppUserVo user) {
|
|
|
|
|
AppLoginUser loginUser = new AppLoginUser();
|
|
|
|
|
Long userId = user.getUserId();
|
|
|
|
|
loginUser.setTenantId(user.getTenantId());
|
|
|
|
|
loginUser.setUserId(userId);
|
|
|
|
|
loginUser.setDeptId(user.getDeptId());
|
|
|
|
|
loginUser.setUsername(user.getUserName());
|
|
|
|
|
loginUser.setNickname(user.getNickName());
|
|
|
|
|
loginUser.setUserType(user.getUserType());
|
|
|
|
|
Set<String> perms = new HashSet<>();
|
|
|
|
|
loginUser.setMenuPermission(perms);
|
|
|
|
|
loginUser.setRolePermission(perms);
|
2025-07-01 14:12:41 +08:00
|
|
|
|
List<AppRoleVo> roles = appRoleService.selectRolesByUserId(userId);
|
2025-06-28 16:28:15 +08:00
|
|
|
|
loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
|
|
|
|
|
return loginUser;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 登录校验
|
|
|
|
|
*/
|
|
|
|
|
public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
|
|
|
|
|
String errorKey = PWD_ERR_CNT_KEY + username;
|
|
|
|
|
String loginFail = Constants.LOGIN_FAIL;
|
|
|
|
|
|
|
|
|
|
// 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip)
|
|
|
|
|
int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0);
|
|
|
|
|
// 锁定时间内登录 则踢出
|
|
|
|
|
if (errorNumber >= maxRetryCount) {
|
|
|
|
|
recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
|
|
|
|
|
throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (supplier.get()) {
|
|
|
|
|
// 错误次数递增
|
|
|
|
|
errorNumber++;
|
|
|
|
|
RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
|
|
|
|
|
// 达到规定错误次数 则锁定登录
|
|
|
|
|
if (errorNumber >= maxRetryCount) {
|
|
|
|
|
recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
|
|
|
|
|
throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
|
|
|
|
|
} else {
|
|
|
|
|
// 未达到规定错误次数
|
|
|
|
|
recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
|
|
|
|
|
throw new UserException(loginType.getRetryLimitCount(), errorNumber);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 登录成功 清空错误次数
|
|
|
|
|
RedisUtils.deleteObject(errorKey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 校验租户
|
|
|
|
|
*
|
|
|
|
|
* @param tenantId 租户ID
|
|
|
|
|
*/
|
|
|
|
|
public void checkTenant(String tenantId) {
|
|
|
|
|
if (!TenantHelper.isEnable()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (StringUtils.isBlank(tenantId)) {
|
|
|
|
|
throw new TenantException("tenant.number.not.blank");
|
|
|
|
|
}
|
|
|
|
|
if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
|
|
|
|
|
if (ObjectUtil.isNull(tenant)) {
|
|
|
|
|
log.info("登录租户:{} 不存在.", tenantId);
|
|
|
|
|
throw new TenantException("tenant.not.exists");
|
|
|
|
|
} else if (SystemConstants.DISABLE.equals(tenant.getStatus())) {
|
|
|
|
|
log.info("登录租户:{} 已被停用.", tenantId);
|
|
|
|
|
throw new TenantException("tenant.blocked");
|
|
|
|
|
} else if (ObjectUtil.isNotNull(tenant.getExpireTime())
|
|
|
|
|
&& new Date().after(tenant.getExpireTime())) {
|
|
|
|
|
log.info("登录租户:{} 已超过有效期.", tenantId);
|
|
|
|
|
throw new TenantException("tenant.expired");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|