Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -27,4 +27,6 @@ public class AuthLoginRespVO {
|
|||||||
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime expiresTime;
|
private LocalDateTime expiresTime;
|
||||||
|
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public class AuthPermissionInfoRespVO {
|
|||||||
|
|
||||||
@Schema(description = "用户邮箱", example = "yudao@iocoder.cn")
|
@Schema(description = "用户邮箱", example = "yudao@iocoder.cn")
|
||||||
private String email;
|
private String email;
|
||||||
|
private Long tenantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")
|
@Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")
|
||||||
|
|||||||
@@ -178,4 +178,11 @@ public class UserController {
|
|||||||
return success(userService.importUserList(list, updateSupport));
|
return success(userService.importUserList(list, updateSupport));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/fix-tenant-id")
|
||||||
|
@Operation(summary = "修复用户租户ID", description = "为租户ID为空的用户设置当前租户ID")
|
||||||
|
@PreAuthorize("@ss.hasPermission('system:user:update')")
|
||||||
|
public CommonResult<Boolean> fixUserTenantId(@RequestParam("userId") Long userId) {
|
||||||
|
return success(userService.fixUserTenantId(userId));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ public class UserSaveReqVO {
|
|||||||
|
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
// ========== 仅【创建】时,需要传递的字段 ==========
|
// ========== 仅【创建】时,需要传递的字段 ==========
|
||||||
|
|
||||||
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
|
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cn.iocoder.yudao.module.system.dal.dataobject.oauth2;
|
package cn.iocoder.yudao.module.system.dal.dataobject.oauth2;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
@@ -26,7 +27,7 @@ import java.util.Map;
|
|||||||
@KeySequence("system_oauth2_access_token_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
@KeySequence("system_oauth2_access_token_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class OAuth2AccessTokenDO extends TenantBaseDO {
|
public class OAuth2AccessTokenDO extends BaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编号,数据库递增
|
* 编号,数据库递增
|
||||||
@@ -71,5 +72,8 @@ public class OAuth2AccessTokenDO extends TenantBaseDO {
|
|||||||
* 过期时间
|
* 过期时间
|
||||||
*/
|
*/
|
||||||
private LocalDateTime expiresTime;
|
private LocalDateTime expiresTime;
|
||||||
|
/**
|
||||||
|
* 多租户编号
|
||||||
|
*/
|
||||||
|
private Long tenantId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 管理后台的认证 Service 接口
|
* 管理后台的认证 Service 接口
|
||||||
*
|
* <p>
|
||||||
* 提供用户的登录、登出的能力
|
* 提供用户的登录、登出的能力
|
||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
@@ -36,7 +36,7 @@ public interface AdminAuthService {
|
|||||||
/**
|
/**
|
||||||
* 基于 token 退出登录
|
* 基于 token 退出登录
|
||||||
*
|
*
|
||||||
* @param token token
|
* @param token token
|
||||||
* @param logType 登出类型
|
* @param logType 登出类型
|
||||||
*/
|
*/
|
||||||
void logout(String token, Integer logType);
|
void logout(String token, Integer logType);
|
||||||
@@ -87,7 +87,7 @@ public interface AdminAuthService {
|
|||||||
*/
|
*/
|
||||||
void resetPassword(AuthResetPasswordReqVO reqVO);
|
void resetPassword(AuthResetPasswordReqVO reqVO);
|
||||||
|
|
||||||
WxLoginRespVO getSession( WxLoginReqVO reqVO);
|
WxLoginRespVO getSession(WxLoginReqVO reqVO);
|
||||||
|
|
||||||
String getAccessToken();
|
String getAccessToken();
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,15 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
// 创建访问令牌
|
// 创建访问令牌
|
||||||
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
|
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
|
||||||
OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
|
OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
|
||||||
|
|
||||||
|
// 获取用户信息并设置租户ID
|
||||||
|
AdminUserDO user = userService.getUser(userId);
|
||||||
|
if (user != null && user.getTenantId() != null) {
|
||||||
|
accessTokenDO.setTenantId(user.getTenantId());
|
||||||
|
} else {
|
||||||
|
log.warn("[createTokenAfterLoginSuccess][用户({}) 租户ID为空,可能导致登录后无法获取租户信息]", userId);
|
||||||
|
}
|
||||||
|
|
||||||
// 构建返回结果
|
// 构建返回结果
|
||||||
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
return AuthConvert.INSTANCE.convert(accessTokenDO);
|
||||||
}
|
}
|
||||||
@@ -357,8 +366,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
}
|
}
|
||||||
user.setOpenid(reqVO.getOpenid());
|
user.setOpenid(reqVO.getOpenid());
|
||||||
userService.updateByid(user);
|
userService.updateByid(user);
|
||||||
|
AuthLoginRespVO tokenAfterLoginSuccess = createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
|
||||||
return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
|
return tokenAfterLoginSuccess;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[getPhoneNumber][解密手机号失败,encryptedData: {}, sessionKey: {}, iv: {}]",
|
log.error("[getPhoneNumber][解密手机号失败,encryptedData: {}, sessionKey: {}, iv: {}]",
|
||||||
reqVO.getEncryptedData(), reqVO.getSessionKey(), reqVO.getIv(), e);
|
reqVO.getEncryptedData(), reqVO.getSessionKey(), reqVO.getIv(), e);
|
||||||
@@ -377,6 +386,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
adminUserDO.setStatus(CommonStatusEnum.DISABLE.getStatus());
|
adminUserDO.setStatus(CommonStatusEnum.DISABLE.getStatus());
|
||||||
adminUserDO.setPassword("123456");
|
adminUserDO.setPassword("123456");
|
||||||
adminUserDO.setIsSynchronize(1);
|
adminUserDO.setIsSynchronize(1);
|
||||||
|
adminUserDO.setTenantId(1L);
|
||||||
userService.createUser(adminUserDO);
|
userService.createUser(adminUserDO);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -388,6 +398,9 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
@Override
|
@Override
|
||||||
public AuthLoginRespVO getUserInfo(UserReqVO reqVO) {
|
public AuthLoginRespVO getUserInfo(UserReqVO reqVO) {
|
||||||
AdminUserDO user = userService.getUserByMobile(reqVO.getPhone());
|
AdminUserDO user = userService.getUserByMobile(reqVO.getPhone());
|
||||||
|
if (user == null) {
|
||||||
|
throw exception(USER_NOT_EXISTS);
|
||||||
|
}
|
||||||
// 创建 Token 令牌,记录登录日志
|
// 创建 Token 令牌,记录登录日志
|
||||||
return createTokenAfterLoginSuccess(user.getId(), reqVO.getPhone(), LoginLogTypeEnum.LOGIN_USERNAME);
|
return createTokenAfterLoginSuccess(user.getId(), reqVO.getPhone(), LoginLogTypeEnum.LOGIN_USERNAME);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
.setUserId(userId).setUserType(userType)
|
.setUserId(userId).setUserType(userType)
|
||||||
.setClientId(clientDO.getClientId()).setScopes(scopes)
|
.setClientId(clientDO.getClientId()).setScopes(scopes)
|
||||||
.setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getRefreshTokenValiditySeconds()));
|
.setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getRefreshTokenValiditySeconds()));
|
||||||
|
refreshToken.setTenantId(TenantContextHolder.getTenantId());
|
||||||
oauth2RefreshTokenMapper.insert(refreshToken);
|
oauth2RefreshTokenMapper.insert(refreshToken);
|
||||||
return refreshToken;
|
return refreshToken;
|
||||||
}
|
}
|
||||||
@@ -199,13 +200,16 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
private Map<String, String> buildUserInfo(Long userId, Integer userType) {
|
private Map<String, String> buildUserInfo(Long userId, Integer userType) {
|
||||||
if (userType.equals(UserTypeEnum.ADMIN.getValue())) {
|
if (userType.equals(UserTypeEnum.ADMIN.getValue())) {
|
||||||
AdminUserDO user = adminUserService.getUser(userId);
|
AdminUserDO user = adminUserService.getUser(userId);
|
||||||
|
if (user == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
return MapUtil.builder(LoginUser.INFO_KEY_NICKNAME, user.getNickname())
|
return MapUtil.builder(LoginUser.INFO_KEY_NICKNAME, user.getNickname())
|
||||||
.put(LoginUser.INFO_KEY_DEPT_ID, StrUtil.toStringOrNull(user.getDeptId())).build();
|
.put(LoginUser.INFO_KEY_DEPT_ID, StrUtil.toStringOrNull(user.getDeptId())).build();
|
||||||
} else if (userType.equals(UserTypeEnum.MEMBER.getValue())) {
|
} else if (userType.equals(UserTypeEnum.MEMBER.getValue())) {
|
||||||
// 注意:目前 Member 暂时不读取,可以按需实现
|
// 注意:目前 Member 暂时不读取,可以按需实现
|
||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
return null;
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String generateAccessToken() {
|
private static String generateAccessToken() {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
|||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
|
||||||
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
|
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
|
||||||
@@ -286,7 +287,10 @@ public class PermissionServiceImpl implements PermissionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询
|
// 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询
|
||||||
Supplier<Long> userDeptId = Suppliers.memoize(() -> userService.getUser(userId).getDeptId());
|
Supplier<Long> userDeptId = Suppliers.memoize(() -> {
|
||||||
|
AdminUserDO user = userService.getUser(userId);
|
||||||
|
return user != null ? user.getDeptId() : null;
|
||||||
|
});
|
||||||
// 遍历每个角色,计算
|
// 遍历每个角色,计算
|
||||||
for (RoleDO role : roles) {
|
for (RoleDO role : roles) {
|
||||||
// 为空时,跳过
|
// 为空时,跳过
|
||||||
|
|||||||
@@ -215,4 +215,12 @@ public interface AdminUserService {
|
|||||||
boolean isPasswordMatch(String rawPassword, String encodedPassword);
|
boolean isPasswordMatch(String rawPassword, String encodedPassword);
|
||||||
|
|
||||||
void updateByid(AdminUserDO user);
|
void updateByid(AdminUserDO user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修复用户租户ID
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @return 是否修复成功
|
||||||
|
*/
|
||||||
|
Boolean fixUserTenantId(Long userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||||
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
|
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||||
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
|
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
|
||||||
@@ -161,6 +162,13 @@ public class AdminUserServiceImpl implements AdminUserService {
|
|||||||
AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class);
|
AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class);
|
||||||
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
|
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
|
||||||
user.setPassword(encodePassword(registerReqVO.getPassword())); // 加密密码
|
user.setPassword(encodePassword(registerReqVO.getPassword())); // 加密密码
|
||||||
|
// 设置租户ID
|
||||||
|
Long tenantId = TenantContextHolder.getTenantId();
|
||||||
|
if (tenantId != null) {
|
||||||
|
user.setTenantId(tenantId);
|
||||||
|
} else {
|
||||||
|
log.warn("[registerUser][注册用户时租户ID为空,可能导致用户无法正常访问系统]");
|
||||||
|
}
|
||||||
userMapper.insert(user);
|
userMapper.insert(user);
|
||||||
return user.getId();
|
return user.getId();
|
||||||
}
|
}
|
||||||
@@ -586,4 +594,34 @@ public class AdminUserServiceImpl implements AdminUserService {
|
|||||||
return passwordEncoder.encode(password);
|
return passwordEncoder.encode(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean fixUserTenantId(Long userId) {
|
||||||
|
// 获取用户信息
|
||||||
|
AdminUserDO user = userMapper.selectById(userId);
|
||||||
|
if (user == null) {
|
||||||
|
throw exception(USER_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果用户已经有租户ID,则不需要修复
|
||||||
|
if (user.getTenantId() != null) {
|
||||||
|
log.info("[fixUserTenantId][用户已有租户ID,无需修复] 用户ID: {}, 租户ID: {}", userId, user.getTenantId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前租户ID
|
||||||
|
Long currentTenantId = TenantContextHolder.getTenantId();
|
||||||
|
if (currentTenantId == null) {
|
||||||
|
throw exception(USER_NOT_EXISTS, "当前租户上下文为空,无法修复用户租户ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户的租户ID
|
||||||
|
AdminUserDO updateUser = new AdminUserDO();
|
||||||
|
updateUser.setId(userId);
|
||||||
|
updateUser.setTenantId(currentTenantId);
|
||||||
|
userMapper.updateById(updateUser);
|
||||||
|
|
||||||
|
log.info("[fixUserTenantId][用户租户ID修复成功] 用户ID: {}, 原租户ID: null, 新租户ID: {}", userId, currentTenantId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ yudao:
|
|||||||
- /admin-api/system/auth/get-session
|
- /admin-api/system/auth/get-session
|
||||||
- /admin-api/system/auth/get-phone-number
|
- /admin-api/system/auth/get-phone-number
|
||||||
- /admin-api/system/auth/api/user-add
|
- /admin-api/system/auth/api/user-add
|
||||||
- /admin-api/system/api/user-info
|
- /admin-api/system/auth/api/user-info
|
||||||
ignore-visit-urls:
|
ignore-visit-urls:
|
||||||
- /admin-api/system/user/profile/**
|
- /admin-api/system/user/profile/**
|
||||||
- /admin-api/system/auth/**
|
- /admin-api/system/auth/**
|
||||||
|
|||||||
Reference in New Issue
Block a user