mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-23 07:41:53 +08:00
实现管理后台登出时,删除 oauth 令牌
This commit is contained in:
parent
4f52d1367b
commit
5ea9cc3cd7
@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.security.core.filter;
|
|||||||
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||||
@ -44,23 +45,14 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
if (StrUtil.isNotEmpty(token)) {
|
if (StrUtil.isNotEmpty(token)) {
|
||||||
Integer userType = WebFrameworkUtils.getLoginUserType(request);
|
Integer userType = WebFrameworkUtils.getLoginUserType(request);
|
||||||
try {
|
try {
|
||||||
// 验证 token 有效性
|
// 1.1 基于 token 构建登录用户
|
||||||
OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token);
|
LoginUser loginUser = buildLoginUserByToken(token, userType);
|
||||||
if (accessToken != null && ObjectUtil.notEqual(accessToken.getUserType(), userType)) { // 用户类型不匹配,无权限
|
// 1.2 模拟 Login 功能,方便日常开发调试
|
||||||
throw new AccessDeniedException("错误的用户类型");
|
|
||||||
}
|
|
||||||
LoginUser loginUser = null;
|
|
||||||
if (accessToken != null) { // 如果不为空,说明认证通过,则转换成登录用户
|
|
||||||
loginUser = new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
|
|
||||||
.setTenantId(accessToken.getTenantId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模拟 Login 功能,方便日常开发调试
|
|
||||||
if (loginUser == null) {
|
if (loginUser == null) {
|
||||||
loginUser = mockLoginUser(request, token, userType);
|
loginUser = mockLoginUser(request, token, userType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置当前用户
|
// 2. 设置当前用户
|
||||||
if (loginUser != null) {
|
if (loginUser != null) {
|
||||||
SecurityFrameworkUtils.setLoginUser(loginUser, request);
|
SecurityFrameworkUtils.setLoginUser(loginUser, request);
|
||||||
}
|
}
|
||||||
@ -75,6 +67,25 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LoginUser buildLoginUserByToken(String token, Integer userType) {
|
||||||
|
try {
|
||||||
|
OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token);
|
||||||
|
if (accessToken == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 用户类型不匹配,无权限
|
||||||
|
if (ObjectUtil.notEqual(accessToken.getUserType(), userType)) {
|
||||||
|
throw new AccessDeniedException("错误的用户类型");
|
||||||
|
}
|
||||||
|
// 构建登录用户
|
||||||
|
return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
|
||||||
|
.setTenantId(accessToken.getTenantId());
|
||||||
|
} catch (ServiceException serviceException) {
|
||||||
|
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模拟登录用户,方便日常开发调试
|
* 模拟登录用户,方便日常开发调试
|
||||||
*
|
*
|
||||||
|
@ -16,7 +16,6 @@ public enum LoginLogTypeEnum {
|
|||||||
LOGIN_SMS(104), // 使用短信登陆
|
LOGIN_SMS(104), // 使用短信登陆
|
||||||
|
|
||||||
LOGOUT_SELF(200), // 自己主动登出
|
LOGOUT_SELF(200), // 自己主动登出
|
||||||
LOGOUT_TIMEOUT(201), // 超时登出
|
|
||||||
LOGOUT_DELETE(202), // 强制退出
|
LOGOUT_DELETE(202), // 强制退出
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -33,8 +33,6 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent;
|
|
||||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
import static java.util.Collections.singleton;
|
import static java.util.Collections.singleton;
|
||||||
|
|
||||||
@ -63,7 +61,7 @@ public class AuthController {
|
|||||||
@ApiOperation("使用账号密码登录")
|
@ApiOperation("使用账号密码登录")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
||||||
public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
|
public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
|
||||||
String token = authService.login(reqVO, getClientIP(), getUserAgent());
|
String token = authService.login(reqVO);
|
||||||
return success(AuthLoginRespVO.builder().token(token).build());
|
return success(AuthLoginRespVO.builder().token(token).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +114,7 @@ public class AuthController {
|
|||||||
@ApiOperation("使用短信验证码登录")
|
@ApiOperation("使用短信验证码登录")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
||||||
public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) {
|
public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) {
|
||||||
String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
|
String token = authService.smsLogin(reqVO);
|
||||||
// 返回结果
|
// 返回结果
|
||||||
return success(AuthLoginRespVO.builder().token(token).build());
|
return success(AuthLoginRespVO.builder().token(token).build());
|
||||||
}
|
}
|
||||||
@ -146,7 +144,7 @@ public class AuthController {
|
|||||||
@ApiOperation("社交快捷登录,使用 code 授权码")
|
@ApiOperation("社交快捷登录,使用 code 授权码")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
||||||
public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialQuickLoginReqVO reqVO) {
|
public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialQuickLoginReqVO reqVO) {
|
||||||
String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent());
|
String token = authService.socialQuickLogin(reqVO);
|
||||||
return success(AuthLoginRespVO.builder().token(token).build());
|
return success(AuthLoginRespVO.builder().token(token).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +152,7 @@ public class AuthController {
|
|||||||
@ApiOperation("社交绑定登录,使用 code 授权码 + 账号密码")
|
@ApiOperation("社交绑定登录,使用 code 授权码 + 账号密码")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
||||||
public CommonResult<AuthLoginRespVO> socialBindLogin(@RequestBody @Valid AuthSocialBindLoginReqVO reqVO) {
|
public CommonResult<AuthLoginRespVO> socialBindLogin(@RequestBody @Valid AuthSocialBindLoginReqVO reqVO) {
|
||||||
String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent());
|
String token = authService.socialBindLogin(reqVO);
|
||||||
return success(AuthLoginRespVO.builder().token(token).build());
|
return success(AuthLoginRespVO.builder().token(token).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package cn.iocoder.yudao.module.system.dal.mysql.auth;
|
package cn.iocoder.yudao.module.system.dal.mysql.auth;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
|
public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
|
||||||
|
|
||||||
default int deleteByUserIdAndUserType(Integer userId, Integer userType) {
|
default int deleteByRefreshToken(String refreshToken) {
|
||||||
return delete(new QueryWrapper<OAuth2RefreshTokenDO>()
|
return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>()
|
||||||
.eq("user_id", userId).eq("user_type", userType));
|
.eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.system.job.auth;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
|
|
||||||
import cn.iocoder.yudao.module.system.service.auth.UserSessionService;
|
|
||||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户 Session 超时 Job
|
|
||||||
*
|
|
||||||
* @author 願
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
@TenantJob
|
|
||||||
@Slf4j
|
|
||||||
public class UserSessionTimeoutJob implements JobHandler {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private UserSessionService userSessionService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String execute(String param) throws Exception {
|
|
||||||
// 执行过期
|
|
||||||
Long timeoutCount = userSessionService.deleteTimeoutSession();
|
|
||||||
// 返回结果,记录每次的超时数量
|
|
||||||
return String.format("移除在线会话数量为 %s 个", timeoutCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -17,11 +17,9 @@ public interface AdminAuthService {
|
|||||||
* 账号登录
|
* 账号登录
|
||||||
*
|
*
|
||||||
* @param reqVO 登录信息
|
* @param reqVO 登录信息
|
||||||
* @param userIp 用户 IP
|
|
||||||
* @param userAgent 用户 UA
|
|
||||||
* @return 身份令牌,使用 JWT 方式
|
* @return 身份令牌,使用 JWT 方式
|
||||||
*/
|
*/
|
||||||
String login(@Valid AuthLoginReqVO reqVO, String userIp, String userAgent);
|
String login(@Valid AuthLoginReqVO reqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于 token 退出登录
|
* 基于 token 退出登录
|
||||||
@ -41,21 +39,17 @@ public interface AdminAuthService {
|
|||||||
* 短信登录
|
* 短信登录
|
||||||
*
|
*
|
||||||
* @param reqVO 登录信息
|
* @param reqVO 登录信息
|
||||||
* @param userIp 用户 IP
|
|
||||||
* @param userAgent 用户 UA
|
|
||||||
* @return 身份令牌,使用 JWT 方式
|
* @return 身份令牌,使用 JWT 方式
|
||||||
*/
|
*/
|
||||||
String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) ;
|
String smsLogin(AuthSmsLoginReqVO reqVO) ;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社交快捷登录,使用 code 授权码
|
* 社交快捷登录,使用 code 授权码
|
||||||
*
|
*
|
||||||
* @param reqVO 登录信息
|
* @param reqVO 登录信息
|
||||||
* @param userIp 用户 IP
|
|
||||||
* @param userAgent 用户 UA
|
|
||||||
* @return 身份令牌,使用 JWT 方式
|
* @return 身份令牌,使用 JWT 方式
|
||||||
*/
|
*/
|
||||||
String socialQuickLogin(@Valid AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent);
|
String socialQuickLogin(@Valid AuthSocialQuickLoginReqVO reqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社交绑定登录,使用 code 授权码 + 账号密码
|
* 社交绑定登录,使用 code 授权码 + 账号密码
|
||||||
@ -65,6 +59,6 @@ public interface AdminAuthService {
|
|||||||
* @param userAgent 用户 UA
|
* @param userAgent 用户 UA
|
||||||
* @return 身份令牌,使用 JWT 方式
|
* @return 身份令牌,使用 JWT 方式
|
||||||
*/
|
*/
|
||||||
String socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent);
|
String socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
|||||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
|
||||||
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
|
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*;
|
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*;
|
||||||
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
|
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
||||||
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
|
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
|
||||||
@ -47,8 +47,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
@Resource
|
@Resource
|
||||||
private LoginLogService loginLogService;
|
private LoginLogService loginLogService;
|
||||||
@Resource
|
@Resource
|
||||||
private UserSessionService userSessionService;
|
|
||||||
@Resource
|
|
||||||
private OAuth2TokenService oauth2TokenService;
|
private OAuth2TokenService oauth2TokenService;
|
||||||
@Resource
|
@Resource
|
||||||
private SocialUserService socialUserService;
|
private SocialUserService socialUserService;
|
||||||
@ -60,16 +58,15 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
private SmsCodeApi smsCodeApi;
|
private SmsCodeApi smsCodeApi;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) {
|
public String login(AuthLoginReqVO reqVO) {
|
||||||
// 判断验证码是否正确
|
// 判断验证码是否正确
|
||||||
verifyCaptcha(reqVO);
|
verifyCaptcha(reqVO);
|
||||||
|
|
||||||
// 使用账号密码,进行登录
|
// 使用账号密码,进行登录
|
||||||
LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword());
|
AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword());
|
||||||
|
|
||||||
// 缓存登陆用户到 Redis 中,返回 Token 令牌
|
// 创建 Token 令牌,记录登录日志
|
||||||
return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(),
|
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
|
||||||
LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -83,9 +80,9 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) {
|
public String smsLogin(AuthSmsLoginReqVO reqVO) {
|
||||||
// 校验验证码
|
// 校验验证码
|
||||||
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), userIp));
|
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP()));
|
||||||
|
|
||||||
// 获得用户信息
|
// 获得用户信息
|
||||||
AdminUserDO user = userService.getUserByMobile(reqVO.getMobile());
|
AdminUserDO user = userService.getUserByMobile(reqVO.getMobile());
|
||||||
@ -93,12 +90,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
throw exception(USER_NOT_EXISTS);
|
throw exception(USER_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 LoginUser 对象
|
|
||||||
LoginUser loginUser = buildLoginUser(user);
|
|
||||||
|
|
||||||
// 缓存登陆用户到 Redis 中,返回 sessionId 编号
|
// 缓存登陆用户到 Redis 中,返回 sessionId 编号
|
||||||
return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(),
|
return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
|
||||||
LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@ -128,7 +121,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
LoginUser login0(String username, String password) {
|
AdminUserDO login0(String username, String password) {
|
||||||
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
|
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
|
||||||
// 校验账号是否存在
|
// 校验账号是否存在
|
||||||
AdminUserDO user = userService.getUserByUsername(username);
|
AdminUserDO user = userService.getUserByUsername(username);
|
||||||
@ -145,9 +138,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED);
|
createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED);
|
||||||
throw exception(AUTH_LOGIN_USER_DISABLED);
|
throw exception(AUTH_LOGIN_USER_DISABLED);
|
||||||
}
|
}
|
||||||
|
return user;
|
||||||
// 构建 User 对象
|
|
||||||
return buildLoginUser(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createLoginLog(Long userId, String username,
|
private void createLoginLog(Long userId, String username,
|
||||||
@ -170,7 +161,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String socialQuickLogin(AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) {
|
public String socialQuickLogin(AuthSocialQuickLoginReqVO reqVO) {
|
||||||
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
|
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
|
||||||
Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
|
Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
|
||||||
reqVO.getCode(), reqVO.getState());
|
reqVO.getCode(), reqVO.getState());
|
||||||
@ -178,56 +169,46 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
|
throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动登录
|
// 获得用户
|
||||||
AdminUserDO user = userService.getUser(userId);
|
AdminUserDO user = userService.getUser(userId);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw exception(USER_NOT_EXISTS);
|
throw exception(USER_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 LoginUser 对象
|
// 创建 Token 令牌,记录登录日志
|
||||||
LoginUser loginUser = buildLoginUser(user);
|
return createTokenAfterLoginSuccess(user.getId(), null, LoginLogTypeEnum.LOGIN_SOCIAL);
|
||||||
|
|
||||||
// 缓存登录用户到 Redis 中,返回 Token 令牌
|
|
||||||
return createUserSessionAfterLoginSuccess(loginUser, null,
|
|
||||||
LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String socialBindLogin(AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) {
|
public String socialBindLogin(AuthSocialBindLoginReqVO reqVO) {
|
||||||
// 使用账号密码,进行登录。
|
// 使用账号密码,进行登录。
|
||||||
LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword());
|
AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword());
|
||||||
|
|
||||||
// 绑定社交用户
|
// 绑定社交用户
|
||||||
socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
|
socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(user.getId(), getUserType().getValue(), reqVO));
|
||||||
|
|
||||||
// 缓存登录用户到 Redis 中,返回 Token 令牌
|
// 创建 Token 令牌,记录登录日志
|
||||||
return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(),
|
return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
|
||||||
LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String username,
|
private String createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
|
||||||
LoginLogTypeEnum logType, String userIp, String userAgent) {
|
|
||||||
// 插入登陆日志
|
// 插入登陆日志
|
||||||
createLoginLog(loginUser.getId(), username, logType, LoginResultEnum.SUCCESS);
|
createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
|
||||||
// 创建访问令牌
|
// 创建访问令牌
|
||||||
// TODO userIp、userAgent
|
|
||||||
// TODO clientId
|
// TODO clientId
|
||||||
return oauth2TokenService.createAccessToken(loginUser.getId(), getUserType().getValue(), 1L)
|
return oauth2TokenService.createAccessToken(userId, getUserType().getValue(), 1L)
|
||||||
.getAccessToken();
|
.getAccessToken();
|
||||||
// return userSessionService.createUserSession(loginUser, userIp, userAgent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logout(String token) {
|
public void logout(String token) {
|
||||||
// 查询用户信息
|
// 删除访问令牌
|
||||||
LoginUser loginUser = userSessionService.getLoginUser(token);
|
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token);
|
||||||
if (loginUser == null) {
|
if (accessTokenDO == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 删除 session
|
// 删除成功,则记录登出日志
|
||||||
userSessionService.deleteUserSession(token);
|
createLogoutLog(accessTokenDO.getUserId());
|
||||||
// 记录登出日志
|
|
||||||
createLogoutLog(loginUser.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createLogoutLog(Long userId) {
|
private void createLogoutLog(Long userId) {
|
||||||
@ -243,10 +224,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
loginLogService.createLoginLog(reqDTO);
|
loginLogService.createLoginLog(reqDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoginUser buildLoginUser(AdminUserDO user) {
|
|
||||||
return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getUsername(Long userId) {
|
private String getUsername(Long userId) {
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -59,8 +59,8 @@ public interface OAuth2TokenService {
|
|||||||
* 参考 DefaultTokenServices 的 revokeToken 方法
|
* 参考 DefaultTokenServices 的 revokeToken 方法
|
||||||
*
|
*
|
||||||
* @param accessToken 刷新令牌
|
* @param accessToken 刷新令牌
|
||||||
* @return 是否移除到
|
* @return 访问令牌的信息
|
||||||
*/
|
*/
|
||||||
boolean removeAccessToken(String accessToken);
|
OAuth2AccessTokenDO removeAccessToken(String accessToken);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -85,23 +85,19 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeAccessToken(String accessToken) {
|
public OAuth2AccessTokenDO removeAccessToken(String accessToken) {
|
||||||
return false;
|
// 删除访问令牌
|
||||||
|
OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken);
|
||||||
|
if (accessTokenDO == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
oauth2AccessTokenMapper.deleteById(accessTokenDO.getId());
|
||||||
|
oauth2AccessTokenRedisDAO.delete(accessToken);
|
||||||
|
// 删除刷新令牌
|
||||||
|
oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken());
|
||||||
|
return accessTokenDO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
|
||||||
// @Transactional
|
|
||||||
// public OAuth2AccessTokenRespDTO checkAccessToken(String accessToken) {
|
|
||||||
// OAuth2AccessTokenDO accessTokenDO = this.getOAuth2AccessToken(accessToken);
|
|
||||||
// if (accessTokenDO == null) { // 不存在
|
|
||||||
// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_NOT_FOUND);
|
|
||||||
// }
|
|
||||||
// if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
|
|
||||||
// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED);
|
|
||||||
// }
|
|
||||||
// // 返回访问令牌
|
|
||||||
// return OAuth2Convert.INSTANCE.convert(accessTokenDO);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
// @Transactional
|
// @Transactional
|
||||||
@ -124,20 +120,6 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
// OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
|
// OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
|
||||||
// // 返回访问令牌
|
// // 返回访问令牌
|
||||||
// return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
|
// return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// @Transactional
|
|
||||||
// public void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO) {
|
|
||||||
// // 删除 Access Token
|
|
||||||
// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByUserIdAndUserType(
|
|
||||||
// removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
|
|
||||||
// if (accessTokenDO != null) {
|
|
||||||
// this.deleteOAuth2AccessToken(accessTokenDO.getId());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 删除 Refresh Token
|
|
||||||
// oauth2RefreshTokenMapper.deleteByUserIdAndUserType(removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
|
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
|
||||||
@ -158,19 +140,6 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
return refreshToken;
|
return refreshToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 删除 accessToken 的 MySQL 与 Redis 的数据
|
|
||||||
// *
|
|
||||||
// * @param accessToken 访问令牌
|
|
||||||
// */
|
|
||||||
// private void deleteOAuth2AccessToken(String accessToken) {
|
|
||||||
// // 删除 MySQL
|
|
||||||
// oauth2AccessTokenMapper.deleteById(accessToken);
|
|
||||||
// // 删除 Redis
|
|
||||||
// oauth2AccessTokenRedisDAO.delete(accessToken);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
private static String generateAccessToken() {
|
private static String generateAccessToken() {
|
||||||
return IdUtil.fastSimpleUUID();
|
return IdUtil.fastSimpleUUID();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.auth;
|
package cn.iocoder.yudao.module.system.service.auth;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在线用户 Session Service 接口
|
* 在线用户 Session Service 接口
|
||||||
@ -20,31 +19,6 @@ public interface UserSessionService {
|
|||||||
*/
|
*/
|
||||||
PageResult<UserSessionDO> getUserSessionPage(UserSessionPageReqVO reqVO);
|
PageResult<UserSessionDO> getUserSessionPage(UserSessionPageReqVO reqVO);
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除超时的在线用户
|
|
||||||
*
|
|
||||||
* @return {@link Long } 移出的超时用户数量
|
|
||||||
**/
|
|
||||||
long deleteTimeoutSession();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建在线用户 Session
|
|
||||||
*
|
|
||||||
* @param loginUser 登录用户
|
|
||||||
* @param userIp 用户 IP
|
|
||||||
* @param userAgent 用户 UA
|
|
||||||
* @return Token 令牌
|
|
||||||
*/
|
|
||||||
String createUserSession(LoginUser loginUser, String userIp, String userAgent);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新在线用户 Session 的更新时间
|
|
||||||
*
|
|
||||||
* @param token 令牌
|
|
||||||
* @param loginUser 登录用户
|
|
||||||
*/
|
|
||||||
void refreshUserSession(String token, LoginUser loginUser);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除在线用户 Session
|
* 删除在线用户 Session
|
||||||
*
|
*
|
||||||
@ -59,19 +33,4 @@ public interface UserSessionService {
|
|||||||
*/
|
*/
|
||||||
void deleteUserSession(Long id);
|
void deleteUserSession(Long id);
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得 Token 对应的在线用户
|
|
||||||
*
|
|
||||||
* @param token 令牌
|
|
||||||
* @return 在线用户
|
|
||||||
*/
|
|
||||||
LoginUser getLoginUser(String token);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得 Session 超时时间,单位:毫秒
|
|
||||||
*
|
|
||||||
* @return 超时时间
|
|
||||||
*/
|
|
||||||
Long getSessionTimeoutMillis();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package cn.iocoder.yudao.module.system.service.auth;
|
package cn.iocoder.yudao.module.system.service.auth;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
||||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
|
||||||
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
|
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
|
||||||
@ -21,12 +19,9 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在线用户 Session Service 实现类
|
* 在线用户 Session Service 实现类
|
||||||
@ -64,29 +59,6 @@ public class UserSessionServiceImpl implements UserSessionService {
|
|||||||
return userSessionMapper.selectPage(reqVO, userIds);
|
return userSessionMapper.selectPage(reqVO, userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long deleteTimeoutSession() {
|
|
||||||
// 获取 db 里已经超时的用户列表
|
|
||||||
List<UserSessionDO> timeoutSessions = userSessionMapper.selectListBySessionTimoutLt();
|
|
||||||
if (CollUtil.isEmpty(timeoutSessions)) {
|
|
||||||
return 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 由于过期的用户一般不多,所以顺序遍历,进行清理
|
|
||||||
int count = 0;
|
|
||||||
for (UserSessionDO session : timeoutSessions) {
|
|
||||||
// 基于 Redis 二次判断,同时也保证 Redis Key 的立即过期,避免延迟导致浪费内存空间
|
|
||||||
if (loginUserRedisDAO.exists(session.getToken())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
userSessionMapper.deleteById(session.getId());
|
|
||||||
// 记录退出日志
|
|
||||||
createLogoutLog(session, LoginLogTypeEnum.LOGOUT_TIMEOUT);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createLogoutLog(UserSessionDO session, LoginLogTypeEnum type) {
|
private void createLogoutLog(UserSessionDO session, LoginLogTypeEnum type) {
|
||||||
LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
|
LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
|
||||||
reqDTO.setLogType(type.getType());
|
reqDTO.setLogType(type.getType());
|
||||||
@ -100,28 +72,6 @@ public class UserSessionServiceImpl implements UserSessionService {
|
|||||||
loginLogService.createLoginLog(reqDTO);
|
loginLogService.createLoginLog(reqDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
|
|
||||||
// 生成 Session 编号
|
|
||||||
String token = generateToken();
|
|
||||||
// 写入 Redis 缓存
|
|
||||||
loginUserRedisDAO.set(token, loginUser);
|
|
||||||
// 写入 DB 中
|
|
||||||
UserSessionDO userSession = UserSessionDO.builder().token(token)
|
|
||||||
.userId(loginUser.getId()).userType(loginUser.getUserType())
|
|
||||||
.userIp(userIp).userAgent(userAgent).username("")
|
|
||||||
.sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis())))
|
|
||||||
.build();
|
|
||||||
userSessionMapper.insert(userSession);
|
|
||||||
// 返回 Token 令牌
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refreshUserSession(String token, LoginUser loginUser) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteUserSession(String token) {
|
public void deleteUserSession(String token) {
|
||||||
// 删除 Redis 缓存
|
// 删除 Redis 缓存
|
||||||
@ -145,23 +95,4 @@ public class UserSessionServiceImpl implements UserSessionService {
|
|||||||
createLogoutLog(session, LoginLogTypeEnum.LOGOUT_DELETE);
|
createLogoutLog(session, LoginLogTypeEnum.LOGOUT_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public LoginUser getLoginUser(String token) {
|
|
||||||
return loginUserRedisDAO.get(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getSessionTimeoutMillis() {
|
|
||||||
return securityProperties.getSessionTimeout().toMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成 Token 令牌,目前采用 UUID 算法
|
|
||||||
*
|
|
||||||
* @return Session 编号
|
|
||||||
*/
|
|
||||||
private static String generateToken() {
|
|
||||||
return IdUtil.fastSimpleUUID();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
|
|||||||
when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
|
when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
LoginUser loginUser = authService.login0(username, password);
|
AdminUserDO loginUser = authService.login0(username, password);
|
||||||
// 校验
|
// 校验
|
||||||
assertPojoEquals(user, loginUser);
|
assertPojoEquals(user, loginUser);
|
||||||
}
|
}
|
||||||
@ -182,8 +182,6 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testLogin_success() {
|
public void testLogin_success() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
String userIp = randomString();
|
|
||||||
String userAgent = randomString();
|
|
||||||
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
|
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
|
||||||
o.setUsername("test_username").setPassword("test_password"));
|
o.setUsername("test_username").setPassword("test_password"));
|
||||||
|
|
||||||
@ -197,13 +195,14 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
|
|||||||
when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
|
when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
|
||||||
// mock 缓存登录用户到 Redis
|
// mock 缓存登录用户到 Redis
|
||||||
String token = randomString();
|
String token = randomString();
|
||||||
when(userSessionService.createUserSession(argThat(argument -> {
|
// when(userSessionService.createUserSession(argThat(argument -> {
|
||||||
AssertUtils.assertPojoEquals(user, argument);
|
// AssertUtils.assertPojoEquals(user, argument);
|
||||||
return true;
|
// return true;
|
||||||
}), eq(userIp), eq(userAgent))).thenReturn(token);
|
// }), eq(userIp), eq(userAgent))).thenReturn(token);
|
||||||
|
// TODO 芋艿:oauth2
|
||||||
|
|
||||||
// 调用, 并断言异常
|
// 调用, 并断言异常
|
||||||
String result = authService.login(reqVO, userIp, userAgent);
|
String result = authService.login(reqVO);
|
||||||
assertEquals(token, result);
|
assertEquals(token, result);
|
||||||
// 校验调用参数
|
// 校验调用参数
|
||||||
verify(loginLogService).createLoginLog(
|
verify(loginLogService).createLoginLog(
|
||||||
@ -219,7 +218,8 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
|
|||||||
String token = randomString();
|
String token = randomString();
|
||||||
LoginUser loginUser = randomPojo(LoginUser.class);
|
LoginUser loginUser = randomPojo(LoginUser.class);
|
||||||
// mock
|
// mock
|
||||||
when(userSessionService.getLoginUser(token)).thenReturn(loginUser);
|
// when(userSessionService.getLoginUser(token)).thenReturn(loginUser);
|
||||||
|
// TODO @芋艿:oauth2
|
||||||
// 调用
|
// 调用
|
||||||
authService.logout(token);
|
authService.logout(token);
|
||||||
// 校验调用参数
|
// 校验调用参数
|
||||||
|
@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.service.auth;
|
|||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
@ -14,8 +13,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
|||||||
import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper;
|
||||||
import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO;
|
import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO;
|
||||||
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
|
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
|
||||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
|
||||||
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
|
|
||||||
import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
|
import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
|
||||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@ -25,17 +22,15 @@ import org.springframework.context.annotation.Import;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
import static cn.hutool.core.util.RandomUtil.randomEle;
|
import static cn.hutool.core.util.RandomUtil.randomEle;
|
||||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
|
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,112 +95,6 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
|
|||||||
assertPojoEquals(dbSession, pageResult.getList().get(0));
|
assertPojoEquals(dbSession, pageResult.getList().get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClearSessionTimeout_none() {
|
|
||||||
// mock db 数据
|
|
||||||
UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> {
|
|
||||||
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
|
|
||||||
o.setSessionTimeout(addTime(Duration.ofDays(1)));
|
|
||||||
});
|
|
||||||
userSessionMapper.insert(userSession);
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
long count = userSessionService.deleteTimeoutSession();
|
|
||||||
// 断言
|
|
||||||
assertEquals(0, count);
|
|
||||||
assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // Redis 还存在的情况
|
|
||||||
public void testClearSessionTimeout_exists() {
|
|
||||||
// mock db 数据
|
|
||||||
UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> {
|
|
||||||
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
|
|
||||||
o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1));
|
|
||||||
});
|
|
||||||
userSessionMapper.insert(userSession);
|
|
||||||
// mock redis 数据
|
|
||||||
loginUserRedisDAO.set(userSession.getToken(), new LoginUser());
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
long count = userSessionService.deleteTimeoutSession();
|
|
||||||
// 断言
|
|
||||||
assertEquals(0, count);
|
|
||||||
assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClearSessionTimeout_success() {
|
|
||||||
// mock db 数据
|
|
||||||
UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> {
|
|
||||||
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
|
|
||||||
o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1));
|
|
||||||
});
|
|
||||||
userSessionMapper.insert(userSession);
|
|
||||||
|
|
||||||
// 清空超时数据
|
|
||||||
long count = userSessionService.deleteTimeoutSession();
|
|
||||||
// 校验
|
|
||||||
assertEquals(1, count);
|
|
||||||
assertNull(userSessionMapper.selectById(userSession.getId())); // 已删除
|
|
||||||
verify(loginLogService).createLoginLog(argThat(loginLog -> {
|
|
||||||
assertPojoEquals(userSession, loginLog);
|
|
||||||
assertEquals(LoginLogTypeEnum.LOGOUT_TIMEOUT.getType(), loginLog.getLogType());
|
|
||||||
assertEquals(LoginResultEnum.SUCCESS.getResult(), loginLog.getResult());
|
|
||||||
return true;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateUserSession_success() {
|
|
||||||
// 准备参数
|
|
||||||
String userIp = randomString();
|
|
||||||
String userAgent = randomString();
|
|
||||||
LoginUser loginUser = randomPojo(LoginUser.class, o -> {
|
|
||||||
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
|
|
||||||
o.setTenantId(0L); // 租户设置为 0,因为暂未启用多租户组件
|
|
||||||
});
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
String token = userSessionService.createUserSession(loginUser, userIp, userAgent);
|
|
||||||
// 校验 UserSessionDO 记录
|
|
||||||
UserSessionDO userSessionDO = userSessionMapper.selectOne(UserSessionDO::getToken, token);
|
|
||||||
assertPojoEquals(loginUser, userSessionDO, "id", "updateTime");
|
|
||||||
assertEquals(token, userSessionDO.getToken());
|
|
||||||
assertEquals(userIp, userSessionDO.getUserIp());
|
|
||||||
assertEquals(userAgent, userSessionDO.getUserAgent());
|
|
||||||
// 校验 LoginUser 缓存
|
|
||||||
LoginUser redisLoginUser = loginUserRedisDAO.get(token);
|
|
||||||
assertPojoEquals(loginUser, redisLoginUser, "username", "password");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateRefreshUserSession() {
|
|
||||||
// 准备参数
|
|
||||||
String token = randomString();
|
|
||||||
|
|
||||||
// mock redis 数据
|
|
||||||
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue()));
|
|
||||||
loginUserRedisDAO.set(token, loginUser);
|
|
||||||
// mock db 数据
|
|
||||||
UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> {
|
|
||||||
o.setUserType(randomEle(UserTypeEnum.values()).getValue());
|
|
||||||
o.setToken(token);
|
|
||||||
});
|
|
||||||
userSessionMapper.insert(userSession);
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
userSessionService.refreshUserSession(token, loginUser);
|
|
||||||
// 校验 LoginUser 缓存
|
|
||||||
LoginUser redisLoginUser = loginUserRedisDAO.get(token);
|
|
||||||
assertPojoEquals(redisLoginUser, loginUser, "username", "password");
|
|
||||||
// 校验 UserSessionDO 记录
|
|
||||||
UserSessionDO updateDO = userSessionMapper.selectOne(UserSessionDO::getToken, token);
|
|
||||||
// assertEquals(updateDO.getUsername(), loginUser.getUsername());
|
|
||||||
assertNotNull(userSession.getUpdateTime());
|
|
||||||
assertNotNull(userSession.getSessionTimeout());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteUserSession_Token() {
|
public void testDeleteUserSession_Token() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
|
Loading…
Reference in New Issue
Block a user