Commit 5ea9cc3cd796db0eede69e792d842b7754ffde34
1 parent
4f52d136
实现管理后台登出时,删除 oauth 令牌
Showing
13 changed files
with
90 additions
and
395 deletions
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.security.core.filter; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.ObjectUtil; |
| 4 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.exception.ServiceException; | |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 6 | 7 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 7 | 8 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; |
| ... | ... | @@ -44,23 +45,14 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { |
| 44 | 45 | if (StrUtil.isNotEmpty(token)) { |
| 45 | 46 | Integer userType = WebFrameworkUtils.getLoginUserType(request); |
| 46 | 47 | try { |
| 47 | - // 验证 token 有效性 | |
| 48 | - OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token); | |
| 49 | - if (accessToken != null && ObjectUtil.notEqual(accessToken.getUserType(), userType)) { // 用户类型不匹配,无权限 | |
| 50 | - throw new AccessDeniedException("错误的用户类型"); | |
| 51 | - } | |
| 52 | - LoginUser loginUser = null; | |
| 53 | - if (accessToken != null) { // 如果不为空,说明认证通过,则转换成登录用户 | |
| 54 | - loginUser = new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) | |
| 55 | - .setTenantId(accessToken.getTenantId()); | |
| 56 | - } | |
| 57 | - | |
| 58 | - // 模拟 Login 功能,方便日常开发调试 | |
| 48 | + // 1.1 基于 token 构建登录用户 | |
| 49 | + LoginUser loginUser = buildLoginUserByToken(token, userType); | |
| 50 | + // 1.2 模拟 Login 功能,方便日常开发调试 | |
| 59 | 51 | if (loginUser == null) { |
| 60 | 52 | loginUser = mockLoginUser(request, token, userType); |
| 61 | 53 | } |
| 62 | 54 | |
| 63 | - // 设置当前用户 | |
| 55 | + // 2. 设置当前用户 | |
| 64 | 56 | if (loginUser != null) { |
| 65 | 57 | SecurityFrameworkUtils.setLoginUser(loginUser, request); |
| 66 | 58 | } |
| ... | ... | @@ -75,6 +67,25 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { |
| 75 | 67 | chain.doFilter(request, response); |
| 76 | 68 | } |
| 77 | 69 | |
| 70 | + private LoginUser buildLoginUserByToken(String token, Integer userType) { | |
| 71 | + try { | |
| 72 | + OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token); | |
| 73 | + if (accessToken == null) { | |
| 74 | + return null; | |
| 75 | + } | |
| 76 | + // 用户类型不匹配,无权限 | |
| 77 | + if (ObjectUtil.notEqual(accessToken.getUserType(), userType)) { | |
| 78 | + throw new AccessDeniedException("错误的用户类型"); | |
| 79 | + } | |
| 80 | + // 构建登录用户 | |
| 81 | + return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) | |
| 82 | + .setTenantId(accessToken.getTenantId()); | |
| 83 | + } catch (ServiceException serviceException) { | |
| 84 | + // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可 | |
| 85 | + return null; | |
| 86 | + } | |
| 87 | + } | |
| 88 | + | |
| 78 | 89 | /** |
| 79 | 90 | * 模拟登录用户,方便日常开发调试 |
| 80 | 91 | * | ... | ... |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
| ... | ... | @@ -33,8 +33,6 @@ import java.util.List; |
| 33 | 33 | import java.util.Set; |
| 34 | 34 | |
| 35 | 35 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 36 | -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | |
| 37 | -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent; | |
| 38 | 36 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 39 | 37 | import static java.util.Collections.singleton; |
| 40 | 38 | |
| ... | ... | @@ -63,7 +61,7 @@ public class AuthController { |
| 63 | 61 | @ApiOperation("使用账号密码登录") |
| 64 | 62 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 |
| 65 | 63 | public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) { |
| 66 | - String token = authService.login(reqVO, getClientIP(), getUserAgent()); | |
| 64 | + String token = authService.login(reqVO); | |
| 67 | 65 | return success(AuthLoginRespVO.builder().token(token).build()); |
| 68 | 66 | } |
| 69 | 67 | |
| ... | ... | @@ -116,7 +114,7 @@ public class AuthController { |
| 116 | 114 | @ApiOperation("使用短信验证码登录") |
| 117 | 115 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 |
| 118 | 116 | public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) { |
| 119 | - String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent()); | |
| 117 | + String token = authService.smsLogin(reqVO); | |
| 120 | 118 | // 返回结果 |
| 121 | 119 | return success(AuthLoginRespVO.builder().token(token).build()); |
| 122 | 120 | } |
| ... | ... | @@ -146,7 +144,7 @@ public class AuthController { |
| 146 | 144 | @ApiOperation("社交快捷登录,使用 code 授权码") |
| 147 | 145 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 |
| 148 | 146 | public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialQuickLoginReqVO reqVO) { |
| 149 | - String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent()); | |
| 147 | + String token = authService.socialQuickLogin(reqVO); | |
| 150 | 148 | return success(AuthLoginRespVO.builder().token(token).build()); |
| 151 | 149 | } |
| 152 | 150 | |
| ... | ... | @@ -154,7 +152,7 @@ public class AuthController { |
| 154 | 152 | @ApiOperation("社交绑定登录,使用 code 授权码 + 账号密码") |
| 155 | 153 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 |
| 156 | 154 | public CommonResult<AuthLoginRespVO> socialBindLogin(@RequestBody @Valid AuthSocialBindLoginReqVO reqVO) { |
| 157 | - String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent()); | |
| 155 | + String token = authService.socialBindLogin(reqVO); | |
| 158 | 156 | return success(AuthLoginRespVO.builder().token(token).build()); |
| 159 | 157 | } |
| 160 | 158 | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java
| 1 | 1 | package cn.iocoder.yudao.module.system.dal.mysql.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 3 | 4 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO; |
| 4 | -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |
| 5 | 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| 6 | 6 | import org.apache.ibatis.annotations.Mapper; |
| 7 | 7 | |
| 8 | 8 | @Mapper |
| 9 | 9 | public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> { |
| 10 | 10 | |
| 11 | - default int deleteByUserIdAndUserType(Integer userId, Integer userType) { | |
| 12 | - return delete(new QueryWrapper<OAuth2RefreshTokenDO>() | |
| 13 | - .eq("user_id", userId).eq("user_type", userType)); | |
| 11 | + default int deleteByRefreshToken(String refreshToken) { | |
| 12 | + return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>() | |
| 13 | + .eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken)); | |
| 14 | 14 | } |
| 15 | 15 | |
| 16 | 16 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/auth/UserSessionTimeoutJob.java deleted
| 1 | -package cn.iocoder.yudao.module.system.job.auth; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | |
| 4 | -import cn.iocoder.yudao.module.system.service.auth.UserSessionService; | |
| 5 | -import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; | |
| 6 | -import lombok.extern.slf4j.Slf4j; | |
| 7 | -import org.springframework.stereotype.Component; | |
| 8 | - | |
| 9 | -import javax.annotation.Resource; | |
| 10 | - | |
| 11 | -/** | |
| 12 | - * 用户 Session 超时 Job | |
| 13 | - * | |
| 14 | - * @author 願 | |
| 15 | - */ | |
| 16 | -@Component | |
| 17 | -@TenantJob | |
| 18 | -@Slf4j | |
| 19 | -public class UserSessionTimeoutJob implements JobHandler { | |
| 20 | - | |
| 21 | - @Resource | |
| 22 | - private UserSessionService userSessionService; | |
| 23 | - | |
| 24 | - @Override | |
| 25 | - public String execute(String param) throws Exception { | |
| 26 | - // 执行过期 | |
| 27 | - Long timeoutCount = userSessionService.deleteTimeoutSession(); | |
| 28 | - // 返回结果,记录每次的超时数量 | |
| 29 | - return String.format("移除在线会话数量为 %s 个", timeoutCount); | |
| 30 | - } | |
| 31 | - | |
| 32 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
| ... | ... | @@ -17,11 +17,9 @@ public interface AdminAuthService { |
| 17 | 17 | * 账号登录 |
| 18 | 18 | * |
| 19 | 19 | * @param reqVO 登录信息 |
| 20 | - * @param userIp 用户 IP | |
| 21 | - * @param userAgent 用户 UA | |
| 22 | 20 | * @return 身份令牌,使用 JWT 方式 |
| 23 | 21 | */ |
| 24 | - String login(@Valid AuthLoginReqVO reqVO, String userIp, String userAgent); | |
| 22 | + String login(@Valid AuthLoginReqVO reqVO); | |
| 25 | 23 | |
| 26 | 24 | /** |
| 27 | 25 | * 基于 token 退出登录 |
| ... | ... | @@ -41,21 +39,17 @@ public interface AdminAuthService { |
| 41 | 39 | * 短信登录 |
| 42 | 40 | * |
| 43 | 41 | * @param reqVO 登录信息 |
| 44 | - * @param userIp 用户 IP | |
| 45 | - * @param userAgent 用户 UA | |
| 46 | 42 | * @return 身份令牌,使用 JWT 方式 |
| 47 | 43 | */ |
| 48 | - String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) ; | |
| 44 | + String smsLogin(AuthSmsLoginReqVO reqVO) ; | |
| 49 | 45 | |
| 50 | 46 | /** |
| 51 | 47 | * 社交快捷登录,使用 code 授权码 |
| 52 | 48 | * |
| 53 | 49 | * @param reqVO 登录信息 |
| 54 | - * @param userIp 用户 IP | |
| 55 | - * @param userAgent 用户 UA | |
| 56 | 50 | * @return 身份令牌,使用 JWT 方式 |
| 57 | 51 | */ |
| 58 | - String socialQuickLogin(@Valid AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent); | |
| 52 | + String socialQuickLogin(@Valid AuthSocialQuickLoginReqVO reqVO); | |
| 59 | 53 | |
| 60 | 54 | /** |
| 61 | 55 | * 社交绑定登录,使用 code 授权码 + 账号密码 |
| ... | ... | @@ -65,6 +59,6 @@ public interface AdminAuthService { |
| 65 | 59 | * @param userAgent 用户 UA |
| 66 | 60 | * @return 身份令牌,使用 JWT 方式 |
| 67 | 61 | */ |
| 68 | - String socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent); | |
| 62 | + String socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO); | |
| 69 | 63 | |
| 70 | 64 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
| ... | ... | @@ -6,11 +6,11 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; |
| 9 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | |
| 10 | 9 | import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; |
| 11 | 10 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
| 12 | 11 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; |
| 13 | 12 | import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; |
| 13 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | |
| 14 | 14 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 15 | 15 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; |
| 16 | 16 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; |
| ... | ... | @@ -47,8 +47,6 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 47 | 47 | @Resource |
| 48 | 48 | private LoginLogService loginLogService; |
| 49 | 49 | @Resource |
| 50 | - private UserSessionService userSessionService; | |
| 51 | - @Resource | |
| 52 | 50 | private OAuth2TokenService oauth2TokenService; |
| 53 | 51 | @Resource |
| 54 | 52 | private SocialUserService socialUserService; |
| ... | ... | @@ -60,16 +58,15 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 60 | 58 | private SmsCodeApi smsCodeApi; |
| 61 | 59 | |
| 62 | 60 | @Override |
| 63 | - public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) { | |
| 61 | + public String login(AuthLoginReqVO reqVO) { | |
| 64 | 62 | // 判断验证码是否正确 |
| 65 | 63 | verifyCaptcha(reqVO); |
| 66 | 64 | |
| 67 | 65 | // 使用账号密码,进行登录 |
| 68 | - LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword()); | |
| 66 | + AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword()); | |
| 69 | 67 | |
| 70 | - // 缓存登陆用户到 Redis 中,返回 Token 令牌 | |
| 71 | - return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(), | |
| 72 | - LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | |
| 68 | + // 创建 Token 令牌,记录登录日志 | |
| 69 | + return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); | |
| 73 | 70 | } |
| 74 | 71 | |
| 75 | 72 | @Override |
| ... | ... | @@ -83,9 +80,9 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 83 | 80 | } |
| 84 | 81 | |
| 85 | 82 | @Override |
| 86 | - public String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) { | |
| 83 | + public String smsLogin(AuthSmsLoginReqVO reqVO) { | |
| 87 | 84 | // 校验验证码 |
| 88 | - smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), userIp)); | |
| 85 | + smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP())); | |
| 89 | 86 | |
| 90 | 87 | // 获得用户信息 |
| 91 | 88 | AdminUserDO user = userService.getUserByMobile(reqVO.getMobile()); |
| ... | ... | @@ -93,12 +90,8 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 93 | 90 | throw exception(USER_NOT_EXISTS); |
| 94 | 91 | } |
| 95 | 92 | |
| 96 | - // 创建 LoginUser 对象 | |
| 97 | - LoginUser loginUser = buildLoginUser(user); | |
| 98 | - | |
| 99 | 93 | // 缓存登陆用户到 Redis 中,返回 sessionId 编号 |
| 100 | - return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(), | |
| 101 | - LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent); | |
| 94 | + return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); | |
| 102 | 95 | } |
| 103 | 96 | |
| 104 | 97 | @VisibleForTesting |
| ... | ... | @@ -128,7 +121,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 128 | 121 | } |
| 129 | 122 | |
| 130 | 123 | @VisibleForTesting |
| 131 | - LoginUser login0(String username, String password) { | |
| 124 | + AdminUserDO login0(String username, String password) { | |
| 132 | 125 | final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; |
| 133 | 126 | // 校验账号是否存在 |
| 134 | 127 | AdminUserDO user = userService.getUserByUsername(username); |
| ... | ... | @@ -145,9 +138,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 145 | 138 | createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED); |
| 146 | 139 | throw exception(AUTH_LOGIN_USER_DISABLED); |
| 147 | 140 | } |
| 148 | - | |
| 149 | - // 构建 User 对象 | |
| 150 | - return buildLoginUser(user); | |
| 141 | + return user; | |
| 151 | 142 | } |
| 152 | 143 | |
| 153 | 144 | private void createLoginLog(Long userId, String username, |
| ... | ... | @@ -170,7 +161,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 170 | 161 | } |
| 171 | 162 | |
| 172 | 163 | @Override |
| 173 | - public String socialQuickLogin(AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) { | |
| 164 | + public String socialQuickLogin(AuthSocialQuickLoginReqVO reqVO) { | |
| 174 | 165 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 |
| 175 | 166 | Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), |
| 176 | 167 | reqVO.getCode(), reqVO.getState()); |
| ... | ... | @@ -178,56 +169,46 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 178 | 169 | throw exception(AUTH_THIRD_LOGIN_NOT_BIND); |
| 179 | 170 | } |
| 180 | 171 | |
| 181 | - // 自动登录 | |
| 172 | + // 获得用户 | |
| 182 | 173 | AdminUserDO user = userService.getUser(userId); |
| 183 | 174 | if (user == null) { |
| 184 | 175 | throw exception(USER_NOT_EXISTS); |
| 185 | 176 | } |
| 186 | 177 | |
| 187 | - // 创建 LoginUser 对象 | |
| 188 | - LoginUser loginUser = buildLoginUser(user); | |
| 189 | - | |
| 190 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 191 | - return createUserSessionAfterLoginSuccess(loginUser, null, | |
| 192 | - LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | |
| 178 | + // 创建 Token 令牌,记录登录日志 | |
| 179 | + return createTokenAfterLoginSuccess(user.getId(), null, LoginLogTypeEnum.LOGIN_SOCIAL); | |
| 193 | 180 | } |
| 194 | 181 | |
| 195 | 182 | @Override |
| 196 | - public String socialBindLogin(AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) { | |
| 183 | + public String socialBindLogin(AuthSocialBindLoginReqVO reqVO) { | |
| 197 | 184 | // 使用账号密码,进行登录。 |
| 198 | - LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword()); | |
| 185 | + AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword()); | |
| 199 | 186 | |
| 200 | 187 | // 绑定社交用户 |
| 201 | - socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); | |
| 188 | + socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(user.getId(), getUserType().getValue(), reqVO)); | |
| 202 | 189 | |
| 203 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | |
| 204 | - return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(), | |
| 205 | - LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | |
| 190 | + // 创建 Token 令牌,记录登录日志 | |
| 191 | + return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); | |
| 206 | 192 | } |
| 207 | 193 | |
| 208 | - private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String username, | |
| 209 | - LoginLogTypeEnum logType, String userIp, String userAgent) { | |
| 194 | + private String createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) { | |
| 210 | 195 | // 插入登陆日志 |
| 211 | - createLoginLog(loginUser.getId(), username, logType, LoginResultEnum.SUCCESS); | |
| 196 | + createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS); | |
| 212 | 197 | // 创建访问令牌 |
| 213 | - // TODO userIp、userAgent | |
| 214 | 198 | // TODO clientId |
| 215 | - return oauth2TokenService.createAccessToken(loginUser.getId(), getUserType().getValue(), 1L) | |
| 199 | + return oauth2TokenService.createAccessToken(userId, getUserType().getValue(), 1L) | |
| 216 | 200 | .getAccessToken(); |
| 217 | -// return userSessionService.createUserSession(loginUser, userIp, userAgent); | |
| 218 | 201 | } |
| 219 | 202 | |
| 220 | 203 | @Override |
| 221 | 204 | public void logout(String token) { |
| 222 | - // 查询用户信息 | |
| 223 | - LoginUser loginUser = userSessionService.getLoginUser(token); | |
| 224 | - if (loginUser == null) { | |
| 205 | + // 删除访问令牌 | |
| 206 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token); | |
| 207 | + if (accessTokenDO == null) { | |
| 225 | 208 | return; |
| 226 | 209 | } |
| 227 | - // 删除 session | |
| 228 | - userSessionService.deleteUserSession(token); | |
| 229 | - // 记录登出日志 | |
| 230 | - createLogoutLog(loginUser.getId()); | |
| 210 | + // 删除成功,则记录登出日志 | |
| 211 | + createLogoutLog(accessTokenDO.getUserId()); | |
| 231 | 212 | } |
| 232 | 213 | |
| 233 | 214 | private void createLogoutLog(Long userId) { |
| ... | ... | @@ -243,10 +224,6 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 243 | 224 | loginLogService.createLoginLog(reqDTO); |
| 244 | 225 | } |
| 245 | 226 | |
| 246 | - private LoginUser buildLoginUser(AdminUserDO user) { | |
| 247 | - return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue()); | |
| 248 | - } | |
| 249 | - | |
| 250 | 227 | private String getUsername(Long userId) { |
| 251 | 228 | if (userId == null) { |
| 252 | 229 | return null; | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
| ... | ... | @@ -59,8 +59,8 @@ public interface OAuth2TokenService { |
| 59 | 59 | * 参考 DefaultTokenServices 的 revokeToken 方法 |
| 60 | 60 | * |
| 61 | 61 | * @param accessToken 刷新令牌 |
| 62 | - * @return 是否移除到 | |
| 62 | + * @return 访问令牌的信息 | |
| 63 | 63 | */ |
| 64 | - boolean removeAccessToken(String accessToken); | |
| 64 | + OAuth2AccessTokenDO removeAccessToken(String accessToken); | |
| 65 | 65 | |
| 66 | 66 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
| ... | ... | @@ -85,23 +85,19 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService { |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | 87 | @Override |
| 88 | - public boolean removeAccessToken(String accessToken) { | |
| 89 | - return false; | |
| 88 | + public OAuth2AccessTokenDO removeAccessToken(String accessToken) { | |
| 89 | + // 删除访问令牌 | |
| 90 | + OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken); | |
| 91 | + if (accessTokenDO == null) { | |
| 92 | + return null; | |
| 93 | + } | |
| 94 | + oauth2AccessTokenMapper.deleteById(accessTokenDO.getId()); | |
| 95 | + oauth2AccessTokenRedisDAO.delete(accessToken); | |
| 96 | + // 删除刷新令牌 | |
| 97 | + oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken()); | |
| 98 | + return accessTokenDO; | |
| 90 | 99 | } |
| 91 | 100 | |
| 92 | -// @Override | |
| 93 | -// @Transactional | |
| 94 | -// public OAuth2AccessTokenRespDTO checkAccessToken(String accessToken) { | |
| 95 | -// OAuth2AccessTokenDO accessTokenDO = this.getOAuth2AccessToken(accessToken); | |
| 96 | -// if (accessTokenDO == null) { // 不存在 | |
| 97 | -// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_NOT_FOUND); | |
| 98 | -// } | |
| 99 | -// if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期 | |
| 100 | -// throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED); | |
| 101 | -// } | |
| 102 | -// // 返回访问令牌 | |
| 103 | -// return OAuth2Convert.INSTANCE.convert(accessTokenDO); | |
| 104 | -// } | |
| 105 | 101 | |
| 106 | 102 | // @Override |
| 107 | 103 | // @Transactional |
| ... | ... | @@ -125,20 +121,6 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService { |
| 125 | 121 | // // 返回访问令牌 |
| 126 | 122 | // return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO); |
| 127 | 123 | // } |
| 128 | -// | |
| 129 | -// @Override | |
| 130 | -// @Transactional | |
| 131 | -// public void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO) { | |
| 132 | -// // 删除 Access Token | |
| 133 | -// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByUserIdAndUserType( | |
| 134 | -// removeTokenDTO.getUserId(), removeTokenDTO.getUserType()); | |
| 135 | -// if (accessTokenDO != null) { | |
| 136 | -// this.deleteOAuth2AccessToken(accessTokenDO.getId()); | |
| 137 | -// } | |
| 138 | -// | |
| 139 | -// // 删除 Refresh Token | |
| 140 | -// oauth2RefreshTokenMapper.deleteByUserIdAndUserType(removeTokenDTO.getUserId(), removeTokenDTO.getUserType()); | |
| 141 | -// } | |
| 142 | 124 | |
| 143 | 125 | private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) { |
| 144 | 126 | OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken()) |
| ... | ... | @@ -158,19 +140,6 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService { |
| 158 | 140 | return refreshToken; |
| 159 | 141 | } |
| 160 | 142 | |
| 161 | - | |
| 162 | -// /** | |
| 163 | -// * 删除 accessToken 的 MySQL 与 Redis 的数据 | |
| 164 | -// * | |
| 165 | -// * @param accessToken 访问令牌 | |
| 166 | -// */ | |
| 167 | -// private void deleteOAuth2AccessToken(String accessToken) { | |
| 168 | -// // 删除 MySQL | |
| 169 | -// oauth2AccessTokenMapper.deleteById(accessToken); | |
| 170 | -// // 删除 Redis | |
| 171 | -// oauth2AccessTokenRedisDAO.delete(accessToken); | |
| 172 | -// } | |
| 173 | -// | |
| 174 | 143 | private static String generateAccessToken() { |
| 175 | 144 | return IdUtil.fastSimpleUUID(); |
| 176 | 145 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionService.java
| 1 | 1 | package cn.iocoder.yudao.module.system.service.auth; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 4 | 4 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; |
| 5 | 5 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; |
| 6 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 7 | 6 | |
| 8 | 7 | /** |
| 9 | 8 | * 在线用户 Session Service 接口 |
| ... | ... | @@ -21,31 +20,6 @@ public interface UserSessionService { |
| 21 | 20 | PageResult<UserSessionDO> getUserSessionPage(UserSessionPageReqVO reqVO); |
| 22 | 21 | |
| 23 | 22 | /** |
| 24 | - * 移除超时的在线用户 | |
| 25 | - * | |
| 26 | - * @return {@link Long } 移出的超时用户数量 | |
| 27 | - **/ | |
| 28 | - long deleteTimeoutSession(); | |
| 29 | - | |
| 30 | - /** | |
| 31 | - * 创建在线用户 Session | |
| 32 | - * | |
| 33 | - * @param loginUser 登录用户 | |
| 34 | - * @param userIp 用户 IP | |
| 35 | - * @param userAgent 用户 UA | |
| 36 | - * @return Token 令牌 | |
| 37 | - */ | |
| 38 | - String createUserSession(LoginUser loginUser, String userIp, String userAgent); | |
| 39 | - | |
| 40 | - /** | |
| 41 | - * 刷新在线用户 Session 的更新时间 | |
| 42 | - * | |
| 43 | - * @param token 令牌 | |
| 44 | - * @param loginUser 登录用户 | |
| 45 | - */ | |
| 46 | - void refreshUserSession(String token, LoginUser loginUser); | |
| 47 | - | |
| 48 | - /** | |
| 49 | 23 | * 删除在线用户 Session |
| 50 | 24 | * |
| 51 | 25 | * @param token token 令牌 |
| ... | ... | @@ -59,19 +33,4 @@ public interface UserSessionService { |
| 59 | 33 | */ |
| 60 | 34 | void deleteUserSession(Long id); |
| 61 | 35 | |
| 62 | - /** | |
| 63 | - * 获得 Token 对应的在线用户 | |
| 64 | - * | |
| 65 | - * @param token 令牌 | |
| 66 | - * @return 在线用户 | |
| 67 | - */ | |
| 68 | - LoginUser getLoginUser(String token); | |
| 69 | - | |
| 70 | - /** | |
| 71 | - * 获得 Session 超时时间,单位:毫秒 | |
| 72 | - * | |
| 73 | - * @return 超时时间 | |
| 74 | - */ | |
| 75 | - Long getSessionTimeoutMillis(); | |
| 76 | - | |
| 77 | 36 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.system.service.auth; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.util.IdUtil; | |
| 5 | 4 | import cn.hutool.core.util.StrUtil; |
| 6 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | 6 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; |
| 8 | 7 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; |
| 9 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | |
| 10 | 8 | import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; |
| 11 | 9 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; |
| 12 | 10 | import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; |
| ... | ... | @@ -21,12 +19,9 @@ import lombok.extern.slf4j.Slf4j; |
| 21 | 19 | import org.springframework.stereotype.Service; |
| 22 | 20 | |
| 23 | 21 | import javax.annotation.Resource; |
| 24 | -import java.time.Duration; | |
| 25 | 22 | import java.util.Collection; |
| 26 | -import java.util.List; | |
| 27 | 23 | |
| 28 | 24 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 29 | -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; | |
| 30 | 25 | |
| 31 | 26 | /** |
| 32 | 27 | * 在线用户 Session Service 实现类 |
| ... | ... | @@ -64,29 +59,6 @@ public class UserSessionServiceImpl implements UserSessionService { |
| 64 | 59 | return userSessionMapper.selectPage(reqVO, userIds); |
| 65 | 60 | } |
| 66 | 61 | |
| 67 | - @Override | |
| 68 | - public long deleteTimeoutSession() { | |
| 69 | - // 获取 db 里已经超时的用户列表 | |
| 70 | - List<UserSessionDO> timeoutSessions = userSessionMapper.selectListBySessionTimoutLt(); | |
| 71 | - if (CollUtil.isEmpty(timeoutSessions)) { | |
| 72 | - return 0L; | |
| 73 | - } | |
| 74 | - | |
| 75 | - // 由于过期的用户一般不多,所以顺序遍历,进行清理 | |
| 76 | - int count = 0; | |
| 77 | - for (UserSessionDO session : timeoutSessions) { | |
| 78 | - // 基于 Redis 二次判断,同时也保证 Redis Key 的立即过期,避免延迟导致浪费内存空间 | |
| 79 | - if (loginUserRedisDAO.exists(session.getToken())) { | |
| 80 | - continue; | |
| 81 | - } | |
| 82 | - userSessionMapper.deleteById(session.getId()); | |
| 83 | - // 记录退出日志 | |
| 84 | - createLogoutLog(session, LoginLogTypeEnum.LOGOUT_TIMEOUT); | |
| 85 | - count++; | |
| 86 | - } | |
| 87 | - return count; | |
| 88 | - } | |
| 89 | - | |
| 90 | 62 | private void createLogoutLog(UserSessionDO session, LoginLogTypeEnum type) { |
| 91 | 63 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 92 | 64 | reqDTO.setLogType(type.getType()); |
| ... | ... | @@ -101,28 +73,6 @@ public class UserSessionServiceImpl implements UserSessionService { |
| 101 | 73 | } |
| 102 | 74 | |
| 103 | 75 | @Override |
| 104 | - public String createUserSession(LoginUser loginUser, String userIp, String userAgent) { | |
| 105 | - // 生成 Session 编号 | |
| 106 | - String token = generateToken(); | |
| 107 | - // 写入 Redis 缓存 | |
| 108 | - loginUserRedisDAO.set(token, loginUser); | |
| 109 | - // 写入 DB 中 | |
| 110 | - UserSessionDO userSession = UserSessionDO.builder().token(token) | |
| 111 | - .userId(loginUser.getId()).userType(loginUser.getUserType()) | |
| 112 | - .userIp(userIp).userAgent(userAgent).username("") | |
| 113 | - .sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))) | |
| 114 | - .build(); | |
| 115 | - userSessionMapper.insert(userSession); | |
| 116 | - // 返回 Token 令牌 | |
| 117 | - return token; | |
| 118 | - } | |
| 119 | - | |
| 120 | - @Override | |
| 121 | - public void refreshUserSession(String token, LoginUser loginUser) { | |
| 122 | - | |
| 123 | - } | |
| 124 | - | |
| 125 | - @Override | |
| 126 | 76 | public void deleteUserSession(String token) { |
| 127 | 77 | // 删除 Redis 缓存 |
| 128 | 78 | loginUserRedisDAO.delete(token); |
| ... | ... | @@ -145,23 +95,4 @@ public class UserSessionServiceImpl implements UserSessionService { |
| 145 | 95 | createLogoutLog(session, LoginLogTypeEnum.LOGOUT_DELETE); |
| 146 | 96 | } |
| 147 | 97 | |
| 148 | - @Override | |
| 149 | - public LoginUser getLoginUser(String token) { | |
| 150 | - return loginUserRedisDAO.get(token); | |
| 151 | - } | |
| 152 | - | |
| 153 | - @Override | |
| 154 | - public Long getSessionTimeoutMillis() { | |
| 155 | - return securityProperties.getSessionTimeout().toMillis(); | |
| 156 | - } | |
| 157 | - | |
| 158 | - /** | |
| 159 | - * 生成 Token 令牌,目前采用 UUID 算法 | |
| 160 | - * | |
| 161 | - * @return Session 编号 | |
| 162 | - */ | |
| 163 | - private static String generateToken() { | |
| 164 | - return IdUtil.fastSimpleUUID(); | |
| 165 | - } | |
| 166 | - | |
| 167 | 98 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java
| ... | ... | @@ -70,7 +70,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 70 | 70 | when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); |
| 71 | 71 | |
| 72 | 72 | // 调用 |
| 73 | - LoginUser loginUser = authService.login0(username, password); | |
| 73 | + AdminUserDO loginUser = authService.login0(username, password); | |
| 74 | 74 | // 校验 |
| 75 | 75 | assertPojoEquals(user, loginUser); |
| 76 | 76 | } |
| ... | ... | @@ -182,8 +182,6 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 182 | 182 | @Test |
| 183 | 183 | public void testLogin_success() { |
| 184 | 184 | // 准备参数 |
| 185 | - String userIp = randomString(); | |
| 186 | - String userAgent = randomString(); | |
| 187 | 185 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o -> |
| 188 | 186 | o.setUsername("test_username").setPassword("test_password")); |
| 189 | 187 | |
| ... | ... | @@ -197,13 +195,14 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 197 | 195 | when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true); |
| 198 | 196 | // mock 缓存登录用户到 Redis |
| 199 | 197 | String token = randomString(); |
| 200 | - when(userSessionService.createUserSession(argThat(argument -> { | |
| 201 | - AssertUtils.assertPojoEquals(user, argument); | |
| 202 | - return true; | |
| 203 | - }), eq(userIp), eq(userAgent))).thenReturn(token); | |
| 198 | +// when(userSessionService.createUserSession(argThat(argument -> { | |
| 199 | +// AssertUtils.assertPojoEquals(user, argument); | |
| 200 | +// return true; | |
| 201 | +// }), eq(userIp), eq(userAgent))).thenReturn(token); | |
| 202 | + // TODO 芋艿:oauth2 | |
| 204 | 203 | |
| 205 | 204 | // 调用, 并断言异常 |
| 206 | - String result = authService.login(reqVO, userIp, userAgent); | |
| 205 | + String result = authService.login(reqVO); | |
| 207 | 206 | assertEquals(token, result); |
| 208 | 207 | // 校验调用参数 |
| 209 | 208 | verify(loginLogService).createLoginLog( |
| ... | ... | @@ -219,7 +218,8 @@ public class AuthServiceImplTest extends BaseDbUnitTest { |
| 219 | 218 | String token = randomString(); |
| 220 | 219 | LoginUser loginUser = randomPojo(LoginUser.class); |
| 221 | 220 | // mock |
| 222 | - when(userSessionService.getLoginUser(token)).thenReturn(loginUser); | |
| 221 | +// when(userSessionService.getLoginUser(token)).thenReturn(loginUser); | |
| 222 | + // TODO @芋艿:oauth2 | |
| 223 | 223 | // 调用 |
| 224 | 224 | authService.logout(token); |
| 225 | 225 | // 校验调用参数 | ... | ... |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java
| ... | ... | @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.service.auth; |
| 3 | 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | -import cn.iocoder.yudao.framework.common.util.date.DateUtils; | |
| 7 | 6 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 8 | 7 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; |
| 9 | 8 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| ... | ... | @@ -14,8 +13,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 14 | 13 | import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper; |
| 15 | 14 | import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO; |
| 16 | 15 | import cn.iocoder.yudao.module.system.enums.common.SexEnum; |
| 17 | -import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | |
| 18 | -import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; | |
| 19 | 16 | import cn.iocoder.yudao.module.system.service.logger.LoginLogService; |
| 20 | 17 | import cn.iocoder.yudao.module.system.service.user.AdminUserService; |
| 21 | 18 | import org.junit.jupiter.api.BeforeEach; |
| ... | ... | @@ -25,17 +22,15 @@ import org.springframework.context.annotation.Import; |
| 25 | 22 | |
| 26 | 23 | import javax.annotation.Resource; |
| 27 | 24 | import java.time.Duration; |
| 28 | -import java.util.Calendar; | |
| 29 | 25 | |
| 30 | 26 | import static cn.hutool.core.util.RandomUtil.randomEle; |
| 31 | -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; | |
| 32 | 27 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 33 | -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | |
| 28 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | |
| 29 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; | |
| 34 | 30 | import static java.util.Collections.singletonList; |
| 35 | -import static org.junit.jupiter.api.Assertions.*; | |
| 36 | -import static org.mockito.ArgumentMatchers.argThat; | |
| 31 | +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 32 | +import static org.junit.jupiter.api.Assertions.assertNull; | |
| 37 | 33 | import static org.mockito.ArgumentMatchers.eq; |
| 38 | -import static org.mockito.Mockito.verify; | |
| 39 | 34 | import static org.mockito.Mockito.when; |
| 40 | 35 | |
| 41 | 36 | /** |
| ... | ... | @@ -101,112 +96,6 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { |
| 101 | 96 | } |
| 102 | 97 | |
| 103 | 98 | @Test |
| 104 | - public void testClearSessionTimeout_none() { | |
| 105 | - // mock db 数据 | |
| 106 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | |
| 107 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | |
| 108 | - o.setSessionTimeout(addTime(Duration.ofDays(1))); | |
| 109 | - }); | |
| 110 | - userSessionMapper.insert(userSession); | |
| 111 | - | |
| 112 | - // 调用 | |
| 113 | - long count = userSessionService.deleteTimeoutSession(); | |
| 114 | - // 断言 | |
| 115 | - assertEquals(0, count); | |
| 116 | - assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除 | |
| 117 | - } | |
| 118 | - | |
| 119 | - @Test // Redis 还存在的情况 | |
| 120 | - public void testClearSessionTimeout_exists() { | |
| 121 | - // mock db 数据 | |
| 122 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | |
| 123 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | |
| 124 | - o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1)); | |
| 125 | - }); | |
| 126 | - userSessionMapper.insert(userSession); | |
| 127 | - // mock redis 数据 | |
| 128 | - loginUserRedisDAO.set(userSession.getToken(), new LoginUser()); | |
| 129 | - | |
| 130 | - // 调用 | |
| 131 | - long count = userSessionService.deleteTimeoutSession(); | |
| 132 | - // 断言 | |
| 133 | - assertEquals(0, count); | |
| 134 | - assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除 | |
| 135 | - } | |
| 136 | - | |
| 137 | - @Test | |
| 138 | - public void testClearSessionTimeout_success() { | |
| 139 | - // mock db 数据 | |
| 140 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | |
| 141 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | |
| 142 | - o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1)); | |
| 143 | - }); | |
| 144 | - userSessionMapper.insert(userSession); | |
| 145 | - | |
| 146 | - // 清空超时数据 | |
| 147 | - long count = userSessionService.deleteTimeoutSession(); | |
| 148 | - // 校验 | |
| 149 | - assertEquals(1, count); | |
| 150 | - assertNull(userSessionMapper.selectById(userSession.getId())); // 已删除 | |
| 151 | - verify(loginLogService).createLoginLog(argThat(loginLog -> { | |
| 152 | - assertPojoEquals(userSession, loginLog); | |
| 153 | - assertEquals(LoginLogTypeEnum.LOGOUT_TIMEOUT.getType(), loginLog.getLogType()); | |
| 154 | - assertEquals(LoginResultEnum.SUCCESS.getResult(), loginLog.getResult()); | |
| 155 | - return true; | |
| 156 | - })); | |
| 157 | - } | |
| 158 | - | |
| 159 | - @Test | |
| 160 | - public void testCreateUserSession_success() { | |
| 161 | - // 准备参数 | |
| 162 | - String userIp = randomString(); | |
| 163 | - String userAgent = randomString(); | |
| 164 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> { | |
| 165 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | |
| 166 | - o.setTenantId(0L); // 租户设置为 0,因为暂未启用多租户组件 | |
| 167 | - }); | |
| 168 | - | |
| 169 | - // 调用 | |
| 170 | - String token = userSessionService.createUserSession(loginUser, userIp, userAgent); | |
| 171 | - // 校验 UserSessionDO 记录 | |
| 172 | - UserSessionDO userSessionDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); | |
| 173 | - assertPojoEquals(loginUser, userSessionDO, "id", "updateTime"); | |
| 174 | - assertEquals(token, userSessionDO.getToken()); | |
| 175 | - assertEquals(userIp, userSessionDO.getUserIp()); | |
| 176 | - assertEquals(userAgent, userSessionDO.getUserAgent()); | |
| 177 | - // 校验 LoginUser 缓存 | |
| 178 | - LoginUser redisLoginUser = loginUserRedisDAO.get(token); | |
| 179 | - assertPojoEquals(loginUser, redisLoginUser, "username", "password"); | |
| 180 | - } | |
| 181 | - | |
| 182 | - @Test | |
| 183 | - public void testCreateRefreshUserSession() { | |
| 184 | - // 准备参数 | |
| 185 | - String token = randomString(); | |
| 186 | - | |
| 187 | - // mock redis 数据 | |
| 188 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue())); | |
| 189 | - loginUserRedisDAO.set(token, loginUser); | |
| 190 | - // mock db 数据 | |
| 191 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | |
| 192 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | |
| 193 | - o.setToken(token); | |
| 194 | - }); | |
| 195 | - userSessionMapper.insert(userSession); | |
| 196 | - | |
| 197 | - // 调用 | |
| 198 | - userSessionService.refreshUserSession(token, loginUser); | |
| 199 | - // 校验 LoginUser 缓存 | |
| 200 | - LoginUser redisLoginUser = loginUserRedisDAO.get(token); | |
| 201 | - assertPojoEquals(redisLoginUser, loginUser, "username", "password"); | |
| 202 | - // 校验 UserSessionDO 记录 | |
| 203 | - UserSessionDO updateDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); | |
| 204 | -// assertEquals(updateDO.getUsername(), loginUser.getUsername()); | |
| 205 | - assertNotNull(userSession.getUpdateTime()); | |
| 206 | - assertNotNull(userSession.getSessionTimeout()); | |
| 207 | - } | |
| 208 | - | |
| 209 | - @Test | |
| 210 | 99 | public void testDeleteUserSession_Token() { |
| 211 | 100 | // 准备参数 |
| 212 | 101 | String token = randomString(); | ... | ... |