refactor: 拆分多租户

This commit is contained in:
Ray.Hao
2025-12-15 08:05:24 +08:00
parent 3f05f77351
commit 5817826bbd
57 changed files with 297 additions and 2291 deletions

View File

@@ -1,21 +1,14 @@
package com.youlai.boot.auth.controller;
import com.youlai.boot.auth.model.vo.CaptchaVO;
import com.youlai.boot.auth.model.vo.ChooseTenantVO;
import com.youlai.boot.auth.model.dto.LoginRequest;
import com.youlai.boot.auth.model.dto.WxMiniAppPhoneLoginDTO;
import com.youlai.boot.common.enums.LogModuleEnum;
import com.youlai.boot.config.property.TenantProperties;
import com.youlai.boot.core.web.Result;
import com.youlai.boot.auth.service.AuthService;
import com.youlai.boot.auth.model.dto.WxMiniAppCodeLoginDTO;
import com.youlai.boot.common.annotation.Log;
import com.youlai.boot.core.web.ResultCode;
import com.youlai.boot.security.model.AuthenticationToken;
import com.youlai.boot.system.model.entity.User;
import com.youlai.boot.system.model.vo.TenantVO;
import com.youlai.boot.system.service.TenantService;
import com.youlai.boot.system.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -25,8 +18,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
/**
@@ -43,9 +34,6 @@ import java.util.stream.Collectors;
public class AuthController {
private final AuthService authService;
private final UserService userService;
private final TenantService tenantService;
private final TenantProperties tenantProperties;
@Operation(summary = "获取验证码")
@GetMapping("/captcha")
@@ -60,59 +48,8 @@ public class AuthController {
public Result<?> login(@RequestBody @Valid LoginRequest request) {
String username = request.getUsername();
String password = request.getPassword();
Long tenantId = request.getTenantId();
// 如果未启用多租户,直接登录
if (tenantProperties == null || !Boolean.TRUE.equals(tenantProperties.getEnabled())) {
AuthenticationToken authenticationToken = authService.login(username, password, null);
return Result.success(authenticationToken);
}
// 多租户模式如果指定了租户ID直接验证该租户下的密码
if (tenantId != null) {
AuthenticationToken authenticationToken = authService.login(username, password, tenantId);
return Result.success(authenticationToken);
}
// 多租户模式未指定租户ID查询该用户名在所有租户下的账户
List<User> users = userService.findUserAcrossAllTenants(username);
if (users.isEmpty()) {
return Result.failed("用户不存在");
}
// 过滤出正常状态的用户
List<User> activeUsers = users.stream()
.filter(user -> user.getStatus() != null && user.getStatus() == 1)
.toList();
if (activeUsers.isEmpty()) {
return Result.failed("用户已被禁用");
}
// 如果只有1个租户尝试验证该租户下的密码兼容性
if (activeUsers.size() == 1) {
User user = activeUsers.get(0);
// 登录Spring Security 会验证密码)
AuthenticationToken authenticationToken = authService.login(username, password, user.getTenantId());
return Result.success(authenticationToken);
}
// 如果多个租户,返回 choose_tenant 响应(含 tenants 列表)
// 注意:此时不验证密码,直接返回租户列表让用户选择
List<TenantVO> tenants = activeUsers.stream()
.map(user -> tenantService.getTenantById(user.getTenantId()))
.filter(tenant -> tenant != null && (tenant.getStatus() == null || tenant.getStatus() == 1))
.distinct() // 去重(理论上不会有重复,但保险起见)
.collect(Collectors.toList());
if (tenants.isEmpty()) {
return Result.failed("用户所属的租户均不可用");
}
// 返回 choose_tenant 响应
ChooseTenantVO chooseTenantVO = new ChooseTenantVO(tenants);
return Result.failed(ResultCode.CHOOSE_TENANT, chooseTenantVO);
AuthenticationToken authenticationToken = authService.login(username, password);
return Result.success(authenticationToken);
}
@Operation(summary = "短信验证码登录")

View File

@@ -28,8 +28,5 @@ public class LoginRequest {
@Schema(description = "验证码", example = "1234")
private String captchaCode;
@Schema(description = "租户ID可选多租户模式下用于指定租户", example = "1")
private Long tenantId;
}

View File

@@ -1,27 +0,0 @@
package com.youlai.boot.auth.model.vo;
import com.youlai.boot.system.model.vo.TenantVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 选择租户响应VO
*
* @author Ray.Hao
* @since 3.0.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "选择租户响应")
public class ChooseTenantVO implements Serializable {
@Schema(description = "租户列表")
private List<TenantVO> tenants;
}

View File

@@ -18,10 +18,9 @@ public interface AuthService {
*
* @param username 用户名
* @param password 密码
* @param tenantId 租户ID可选多租户模式下用于指定租户
* @return 登录结果
*/
AuthenticationToken login(String username, String password, Long tenantId);
AuthenticationToken login(String username, String password);
/**
* 登出

View File

@@ -21,7 +21,6 @@ import com.youlai.boot.security.model.WxMiniAppCodeAuthenticationToken;
import com.youlai.boot.security.model.WxMiniAppPhoneAuthenticationToken;
import com.youlai.boot.security.token.TokenManager;
import com.youlai.boot.security.util.SecurityUtils;
import com.youlai.boot.common.tenant.TenantContextHolder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
@@ -62,16 +61,10 @@ public class AuthServiceImpl implements AuthService {
*
* @param username 用户名
* @param password 密码
* @param tenantId 租户ID可选多租户模式下用于指定租户
* @return 访问令牌
*/
@Override
public AuthenticationToken login(String username, String password, Long tenantId) {
// 如果指定了租户ID需要先设置租户上下文以便查询该租户下的用户
if (tenantId != null) {
com.youlai.boot.common.tenant.TenantContextHolder.setTenantId(tenantId);
}
public AuthenticationToken login(String username, String password) {
// 1. 创建用于密码认证的令牌(未认证)
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username.trim(), password);