Commit 5ea9cc3cd796db0eede69e792d842b7754ffde34

Authored by YunaiV
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
... ... @@ -16,7 +16,6 @@ public enum LoginLogTypeEnum {
16 16 LOGIN_SMS(104), // 使用短信登陆
17 17  
18 18 LOGOUT_SELF(200), // 自己主动登出
19   - LOGOUT_TIMEOUT(201), // 超时登出
20 19 LOGOUT_DELETE(202), // 强制退出
21 20 ;
22 21  
... ...
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();
... ...