feat(auth): 支持邮箱登录及跨租户账号查询

- 在LoginParam中添加邮箱字段支持
- 新增邮箱登录接口,支持跨租户通过邮箱登录功能
- 实现根据邮箱查询所有账号接口,方便多账号选择
- 添加UserService方法支持根据邮箱查询账号
- 在UserMapper.xml中新增根据邮箱查询所有账号SQL语句
- 登录接口增加用户状态和密码校验逻辑
- 设置登录成功后Token生成及Redis缓存同步
- 处理同一邮箱多个租户账号时的租户选择逻辑
This commit is contained in:
2026-07-18 11:00:07 +08:00
parent aa7f3f7a6e
commit 5e9dc385f5
5 changed files with 101 additions and 0 deletions

View File

@@ -237,6 +237,77 @@ public class MainController extends BaseController {
return success("登录成功", new LoginResult(access_token, user));
}
@Operation(summary = "邮箱登录(支持跨租户)")
@PostMapping("/loginByEmail")
public ApiResult<LoginResult> loginByEmail(@RequestBody LoginParam param, HttpServletRequest request) {
// 设置过期时间
Long tokenExpireTime = configProperties.getTokenExpireTime();
final String email = param.getEmail();
final String password = param.getPassword();
if (StrUtil.isBlank(email) || StrUtil.isBlank(password)) {
return fail("参数不能为空");
}
// 跨租户:按邮箱查询所有租户下的账号
List<User> users = userService.findAccountsByEmail(email);
if (users == null || users.isEmpty()) {
String message = "用户不存在";
loginRecordService.saveAsync(email, LoginRecord.TYPE_ERROR, message, null, request);
return fail(message, null);
}
User user;
if (users.size() == 1) {
user = users.get(0);
} else {
// 同一邮箱在多个租户下存在,需用户选择具体租户
if (param.getTenantId() == null) {
User first = users.get(0);
first.setHasAdminsByPhone(true);
return success("请选择登录用户", new LoginResult(null, first));
}
user = users.stream()
.filter(u -> u.getTenantId().equals(param.getTenantId()))
.findFirst()
.orElse(null);
if (user == null) {
return fail("用户不存在", null);
}
}
if (!user.getStatus().equals(0)) {
String message = "账号被冻结";
loginRecordService.saveAsync(email, LoginRecord.TYPE_ERROR, message, user.getTenantId(), request);
return fail(message, null);
}
if (!userService.comparePassword(user.getPassword(), password)) {
String message = "密码错误";
loginRecordService.saveAsync(email, LoginRecord.TYPE_ERROR, message, user.getTenantId(), request);
return fail(message, null);
}
// 登录成功
loginRecordService.saveAsync(email, LoginRecord.TYPE_LOGIN, null, user.getTenantId(), request);
final JSONObject register = cacheClient.getSettingInfo("register", user.getTenantId());
if (register != null) {
final String ExpireTime = register.getString("tokenExpireTime");
if (ExpireTime != null) {
tokenExpireTime = Long.valueOf(ExpireTime);
}
}
// 签发token
String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()),
tokenExpireTime, configProperties.getTokenKey());
// 同步redis
redisUtil.set("access_token:" + user.getUserId(), access_token, tokenExpireTime, TimeUnit.SECONDS);
return success("登录成功", new LoginResult(access_token, user));
}
@Operation(summary = "查询邮箱下全部账号(跨租户,用于多账号选择)")
@GetMapping("/listAdminsByEmailAll")
public ApiResult<?> listAdminsByEmailAll(LoginParam param) {
final List<User> accounts = userService.findAccountsByEmail(param.getEmail());
return success(accounts);
}
@Operation(summary = "检查用户是否存在")
@GetMapping("/existence")
public ApiResult<?> existence(ExistenceParam<User> param) {

View File

@@ -380,6 +380,17 @@
ORDER BY a.create_time DESC
</select>
<!-- 根据邮箱查询所有账号(忽略租户隔离,用于跨租户邮箱登录) -->
<select id="selectAccountsByEmail" resultType="com.gxwebsoft.common.system.entity.User">
SELECT a.*,
t.tenant_name
FROM sys_user a
LEFT JOIN sys_tenant t ON a.tenant_id = t.tenant_id
WHERE a.deleted = 0
AND a.email = #{email}
ORDER BY a.create_time DESC
</select>
<!-- 根据手机号统计账号数量(忽略租户隔离) -->
<select id="countByPhone" resultType="java.lang.Integer">
SELECT COUNT(1)

View File

@@ -28,6 +28,9 @@ public class LoginParam implements Serializable {
@Schema(description = "手机号码")
private String phone;
@Schema(description = "邮箱")
private String email;
@Schema(description = "短信验证码")
private String code;

View File

@@ -156,6 +156,14 @@ public interface UserService extends IService<User>, UserDetailsService {
*/
List<User> findAccountsByPhone(String phone);
/**
* 根据邮箱查询所有账号(忽略租户隔离,用于跨租户登录)
*
* @param email 邮箱
* @return List<User>
*/
List<User> findAccountsByEmail(String email);
/**
* 根据手机号统计账号数量
*

View File

@@ -473,6 +473,14 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return baseMapper.selectAccountsByPhone(phone);
}
@Override
public List<User> findAccountsByEmail(String email) {
if (StrUtil.isBlank(email)) {
return null;
}
return baseMapper.selectAccountsByEmail(email);
}
@Override
public Integer countAccountsByPhone(String phone) {
if (StrUtil.isBlank(phone)) {