fix(auth): 拦截禁用用户登录并撤销会员令牌

This commit is contained in:
YunaiV
2026-07-19 20:18:25 +08:00
parent 5b5bd40b48
commit f639a76b43
11 changed files with 278 additions and 28 deletions
@@ -41,6 +41,11 @@ public class OAuth2TokenApiImpl implements OAuth2TokenCommonApi {
return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class);
}
@Override
public void removeAccessToken(Long userId, Integer userType) {
oauth2TokenService.removeAccessToken(userId, userType);
}
@Override
public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, String clientId) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
@@ -91,10 +91,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
}
// 校验是否禁用
if (CommonStatusEnum.isDisable(user.getStatus())) {
createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED);
throw exception(AUTH_LOGIN_USER_DISABLED);
}
validateUserStatus(user, username, logTypeEnum);
return user;
}
@@ -113,7 +110,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
}
// 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
return createTokenAfterLoginSuccess(user, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
}
@Override
@@ -146,7 +143,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
}
// 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
}
private void createLoginLog(Long userId, String username,
@@ -184,7 +181,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
}
// 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
return createTokenAfterLoginSuccess(user, user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
}
@VisibleForTesting
@@ -209,16 +206,26 @@ public class AdminAuthServiceImpl implements AdminAuthService {
return captchaService.verification(captchaVO);
}
private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
private AuthLoginRespVO createTokenAfterLoginSuccess(AdminUserDO user, String username, LoginLogTypeEnum logType) {
// 统一校验用户状态,避免短信、社交等登录方式遗漏
validateUserStatus(user, username, logType);
// 插入登陆日志
createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
createLoginLog(user.getId(), username, logType, LoginResultEnum.SUCCESS);
// 创建访问令牌
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(),
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(user.getId(), getUserType().getValue(),
OAuth2ClientConstants.CLIENT_ID_DEFAULT, null);
// 构建返回结果
return BeanUtils.toBean(accessTokenDO, AuthLoginRespVO.class);
}
private void validateUserStatus(AdminUserDO user, String username, LoginLogTypeEnum logType) {
if (CommonStatusEnum.isDisable(user.getStatus())) {
createLoginLog(user.getId(), username, logType, LoginResultEnum.USER_DISABLED);
throw exception(AUTH_LOGIN_USER_DISABLED);
}
}
@Override
public AuthLoginRespVO refreshToken(String refreshToken) {
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT);
@@ -271,10 +278,10 @@ public class AdminAuthServiceImpl implements AdminAuthService {
validateCaptcha(registerReqVO);
// 2. 校验用户名是否已存在
Long userId = userService.registerUser(registerReqVO);
AdminUserDO user = userService.registerUser(registerReqVO);
// 3. 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(userId, registerReqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
return createTokenAfterLoginSuccess(user, registerReqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
}
@VisibleForTesting
@@ -38,9 +38,9 @@ public interface AdminUserService {
* 注册用户
*
* @param registerReqVO 用户信息
* @return 用户编号
* @return 用户信息
*/
Long registerUser(@Valid AuthRegisterReqVO registerReqVO);
AdminUserDO registerUser(@Valid AuthRegisterReqVO registerReqVO);
/**
* 修改用户
@@ -124,7 +124,7 @@ public class AdminUserServiceImpl implements AdminUserService {
}
@Override
public Long registerUser(AuthRegisterReqVO registerReqVO) {
public AdminUserDO registerUser(AuthRegisterReqVO registerReqVO) {
// 1.1 校验是否开启注册
if (ObjUtil.notEqual(configApi.getConfigValueByKey(USER_REGISTER_ENABLED_KEY), "true")) {
throw exception(USER_REGISTER_DISABLED);
@@ -144,7 +144,7 @@ public class AdminUserServiceImpl implements AdminUserService {
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
user.setPassword(encodePassword(registerReqVO.getPassword())); // 加密密码
userMapper.insert(user);
return user.getId();
return user;
}
@Override
@@ -215,7 +215,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
return true;
})));
// mock 方法(用户信息)
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L));
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L)
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.getUserByMobile(eq(mobile))).thenReturn(user);
// mock 缓存登录用户到 Redis
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
@@ -243,7 +244,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
when(socialUserService.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), userId));
// mock(用户)
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId)
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.getUser(eq(userId))).thenReturn(user);
// mock 缓存登录用户到 Redis
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
@@ -262,6 +264,62 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
);
}
@Test
public void testSmsLogin_userDisabled() {
// 准备参数
AuthSmsLoginReqVO reqVO = new AuthSmsLoginReqVO(randomString(), randomString());
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
when(userService.getUserByMobile(eq(reqVO.getMobile()))).thenReturn(user);
// 调用,并断言
assertServiceException(() -> authService.smsLogin(reqVO), AUTH_LOGIN_USER_DISABLED);
verify(loginLogService).createLoginLog(argThat(o ->
o.getLogType().equals(LoginLogTypeEnum.LOGIN_MOBILE.getType())
&& o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult())
&& o.getUserId().equals(user.getId())));
verify(oauth2TokenService, never()).createAccessToken(anyLong(), anyInt(), anyString(), any());
}
@Test
public void testSocialLogin_userDisabled() {
// 准备参数
AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class);
Long userId = 1L;
when(socialUserService.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
eq(reqVO.getCode()), eq(reqVO.getState())))
.thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), userId));
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId)
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
when(userService.getUser(eq(userId))).thenReturn(user);
// 调用,并断言
assertServiceException(() -> authService.socialLogin(reqVO), AUTH_LOGIN_USER_DISABLED);
verify(loginLogService).createLoginLog(argThat(o ->
o.getLogType().equals(LoginLogTypeEnum.LOGIN_SOCIAL.getType())
&& o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult())
&& o.getUserId().equals(user.getId())));
verify(oauth2TokenService, never()).createAccessToken(anyLong(), anyInt(), anyString(), any());
}
@Test
public void testRegister_success() {
// 准备参数
AuthRegisterReqVO reqVO = randomPojo(AuthRegisterReqVO.class);
authService.setCaptchaEnable(false);
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L)
.setUsername(reqVO.getUsername()).setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.registerUser(reqVO)).thenReturn(user);
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(user.getId())
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.createAccessToken(eq(user.getId()), eq(UserTypeEnum.ADMIN.getValue()),
eq("default"), isNull())).thenReturn(accessTokenDO);
// 调用,并断言
AuthLoginRespVO loginRespVO = authService.register(reqVO);
assertPojoEquals(accessTokenDO, loginRespVO);
verify(userService, never()).getUser(anyLong());
}
@Test
public void testValidateCaptcha_successWithEnable() {
// 准备参数
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.service.user;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
@@ -278,13 +279,14 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
Integer status = randomCommonStatus();
Integer status = CommonStatusEnum.DISABLE.getStatus();
// 调用
userService.updateUserStatus(userId, status);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals(status, user.getStatus());
verify(oauth2TokenService).removeAccessToken(userId, UserTypeEnum.ADMIN.getValue());
}
@Test