Commit 5cf68961e1a5d7b310e6e9d8b9308c88c254b0dd

Authored by YunaiV
1 parent 6ed62486

1. 修改在线会话的实现

2. 接入到会员管理 OAuth2.0
Showing 28 changed files with 356 additions and 150 deletions
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/SecurityProperties.java
... ... @@ -6,7 +6,6 @@ import org.springframework.validation.annotation.Validated;
6 6  
7 7 import javax.validation.constraints.NotEmpty;
8 8 import javax.validation.constraints.NotNull;
9   -import java.time.Duration;
10 9  
11 10 @ConfigurationProperties(prefix = "yudao.security")
12 11 @Validated
... ... @@ -18,18 +17,6 @@ public class SecurityProperties {
18 17 */
19 18 @NotEmpty(message = "Token Header 不能为空")
20 19 private String tokenHeader;
21   - /**
22   - * Token 过期时间
23   - */
24   - @NotNull(message = "Token 过期时间不能为空")
25   - private Duration tokenTimeout;
26   - /**
27   - * Session 过期时间
28   - *
29   - * 当 User 用户超过当前时间未操作,则 Session 会过期
30   - */
31   - @NotNull(message = "Session 过期时间不能为空")
32   - private Duration sessionTimeout;
33 20  
34 21 /**
35 22 * mock 模式的开关
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
... ... @@ -19,7 +19,7 @@ tenant-id: {{appTenentId}}
19 19 }
20 20  
21 21 ### 请求 /sms-login 接口 => 成功
22   -POST {{appApi}}/member/sms-login
  22 +POST {{appApi}}/member/auth/sms-login
23 23 Content-Type: application/json
24 24 tenant-id: {{appTenentId}}
25 25  
... ... @@ -29,7 +29,12 @@ tenant-id: {{appTenentId}}
29 29 }
30 30  
31 31 ### 请求 /logout 接口 => 成功
32   -POST {{appApi}}/member/logout
  32 +POST {{appApi}}/member/auth/logout
33 33 Content-Type: application/json
34 34 Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66
35 35 tenant-id: {{appTenentId}}
  36 +
  37 +### 请求 /auth/refresh-token 接口 => 成功
  38 +POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70
  39 +Content-Type: application/json
  40 +tenant-id: {{appTenentId}}
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.member.controller.app.auth;
2 2  
3 3 import cn.hutool.core.util.StrUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
5 6 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
6 7 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
7 8 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
... ... @@ -20,8 +21,6 @@ import javax.servlet.http.HttpServletRequest;
20 21 import javax.validation.Valid;
21 22  
22 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
23   -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
24   -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent;
25 24 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
26 25  
27 26 @Api(tags = "用户 APP - 认证")
... ... @@ -40,8 +39,7 @@ public class AppAuthController {
40 39 @PostMapping("/login")
41 40 @ApiOperation("使用手机 + 密码登录")
42 41 public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) {
43   - String token = authService.login(reqVO, getClientIP(), getUserAgent());
44   - return success(AppAuthLoginRespVO.builder().token(token).build());
  42 + return success(authService.login(reqVO));
45 43 }
46 44  
47 45 @PostMapping("/logout")
... ... @@ -54,12 +52,20 @@ public class AppAuthController {
54 52 return success(true);
55 53 }
56 54  
  55 + @PostMapping("/refresh-token")
  56 + @ApiOperation("刷新令牌")
  57 + @ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class)
  58 + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
  59 + public CommonResult<AppAuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
  60 + return success(authService.refreshToken(refreshToken));
  61 + }
  62 +
  63 + // ========== 短信登录相关 ==========
  64 +
57 65 @PostMapping("/sms-login")
58 66 @ApiOperation("使用手机 + 验证码登录")
59 67 public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
60   - String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
61   - // 返回结果
62   - return success(AppAuthLoginRespVO.builder().token(token).build());
  68 + return success(authService.smsLogin(reqVO));
63 69 }
64 70  
65 71 @PostMapping("/send-sms-code")
... ... @@ -100,16 +106,14 @@ public class AppAuthController {
100 106  
101 107 @PostMapping("/social-quick-login")
102 108 @ApiOperation(value = "社交快捷登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户")
103   - public CommonResult<AppAuthLoginRespVO> socialLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) {
104   - String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent());
105   - return success(AppAuthLoginRespVO.builder().token(token).build());
  109 + public CommonResult<AppAuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) {
  110 + return success(authService.socialQuickLogin(reqVO));
106 111 }
107 112  
108 113 @PostMapping("/social-bind-login")
109 114 @ApiOperation(value = "社交绑定登录,使用 手机号 + 手机验证码", notes = "适合未登录的用户,进行登录 + 绑定")
110   - public CommonResult<AppAuthLoginRespVO> socialLogin2(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) {
111   - String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent());
112   - return success(AppAuthLoginRespVO.builder().token(token).build());
  115 + public CommonResult<AppAuthLoginRespVO> socialBindLogin(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) {
  116 + return success(authService.socialBindLogin(reqVO));
113 117 }
114 118  
115 119 }
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java
... ... @@ -7,14 +7,25 @@ import lombok.Builder;
7 7 import lombok.Data;
8 8 import lombok.NoArgsConstructor;
9 9  
10   -@ApiModel("用户 APP - 手机密码登录 Response VO")
  10 +import java.util.Date;
  11 +
  12 +@ApiModel("用户 APP - 登录 Response VO")
11 13 @Data
12 14 @NoArgsConstructor
13 15 @AllArgsConstructor
14 16 @Builder
15 17 public class AppAuthLoginRespVO {
16 18  
17   - @ApiModelProperty(value = "token", required = true, example = "yudaoyuanma")
18   - private String token;
  19 + @ApiModelProperty(value = "用户编号", required = true, example = "1024")
  20 + private Long userId;
  21 +
  22 + @ApiModelProperty(value = "访问令牌", required = true, example = "happy")
  23 + private String accessToken;
  24 +
  25 + @ApiModelProperty(value = "刷新令牌", required = true, example = "nice")
  26 + private String refreshToken;
  27 +
  28 + @ApiModelProperty(value = "过期时间", required = true)
  29 + private Date expiresTime;
19 30  
20 31 }
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java
1 1 package cn.iocoder.yudao.module.member.convert.auth;
2 2  
3   -import cn.iocoder.yudao.framework.security.core.LoginUser;
4 3 import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
5 4 import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
6   -import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
  5 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
7 6 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
8 7 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
9 8 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
... ... @@ -17,8 +16,6 @@ public interface AuthConvert {
17 16  
18 17 AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
19 18  
20   - LoginUser convert(MemberUserDO bean);
21   -
22 19 SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO);
23 20 SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO);
24 21 SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO);
... ... @@ -27,4 +24,6 @@ public interface AuthConvert {
27 24 SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp);
28 25 SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp);
29 26  
  27 + AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean);
  28 +
30 29 }
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java
... ... @@ -17,11 +17,9 @@ public interface MemberAuthService {
17 17 * 手机 + 密码登录
18 18 *
19 19 * @param reqVO 登录信息
20   - * @param userIp 用户 IP
21   - * @param userAgent 用户 UA
22   - * @return 身份令牌,使用 JWT 方式
  20 + * @return 登录结果
23 21 */
24   - String login(@Valid AppAuthLoginReqVO reqVO, String userIp, String userAgent);
  22 + AppAuthLoginRespVO login(@Valid AppAuthLoginReqVO reqVO);
25 23  
26 24 /**
27 25 * 基于 token 退出登录
... ... @@ -34,31 +32,25 @@ public interface MemberAuthService {
34 32 * 手机 + 验证码登陆
35 33 *
36 34 * @param reqVO 登陆信息
37   - * @param userIp 用户 IP
38   - * @param userAgent 用户 UA
39   - * @return 身份令牌,使用 JWT 方式
  35 + * @return 登录结果
40 36 */
41   - String smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent);
  37 + AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO);
42 38  
43 39 /**
44 40 * 社交登录,使用 code 授权码
45 41 *
46 42 * @param reqVO 登录信息
47   - * @param userIp 用户 IP
48   - * @param userAgent 用户 UA
49   - * @return 身份令牌,使用 JWT 方式
  43 + * @return 登录结果
50 44 */
51   - String socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent);
  45 + AppAuthLoginRespVO socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO);
52 46  
53 47 /**
54 48 * 社交登录,使用 手机号 + 手机验证码
55 49 *
56 50 * @param reqVO 登录信息
57   - * @param userIp 用户 IP
58   - * @param userAgent 用户 UA
59   - * @return 身份令牌,使用 JWT 方式
  51 + * @return 登录结果
60 52 */
61   - String socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent);
  53 + AppAuthLoginRespVO socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO);
62 54  
63 55 /**
64 56 * 获得社交认证 URL
... ... @@ -90,4 +82,11 @@ public interface MemberAuthService {
90 82 */
91 83 void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO);
92 84  
  85 + /**
  86 + * 刷新访问令牌
  87 + *
  88 + * @param refreshToken 刷新令牌
  89 + * @return 登录结果
  90 + */
  91 + AppAuthLoginRespVO refreshToken(String refreshToken);
93 92 }
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
... ... @@ -6,17 +6,19 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
7 7 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
8 8 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
9   -import cn.iocoder.yudao.framework.security.core.LoginUser;
10 9 import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
11 10 import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
12 11 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
13 12 import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
14 13 import cn.iocoder.yudao.module.member.service.user.MemberUserService;
15   -import cn.iocoder.yudao.module.system.api.auth.UserSessionApi;
  14 +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
  15 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
  16 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
16 17 import cn.iocoder.yudao.module.system.api.logger.LoginLogApi;
17 18 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
18 19 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
19 20 import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
  21 +import cn.iocoder.yudao.module.system.enums.auth.OAuth2ClientIdEnum;
20 22 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
21 23 import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
22 24 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
... ... @@ -49,9 +51,9 @@ public class MemberAuthServiceImpl implements MemberAuthService {
49 51 @Resource
50 52 private LoginLogApi loginLogApi;
51 53 @Resource
52   - private UserSessionApi userSessionApi;
53   - @Resource
54 54 private SocialUserApi socialUserApi;
  55 + @Resource
  56 + private OAuth2TokenApi oauth2TokenApi;
55 57  
56 58 @Resource
57 59 private PasswordEncoder passwordEncoder;
... ... @@ -59,35 +61,31 @@ public class MemberAuthServiceImpl implements MemberAuthService {
59 61 private MemberUserMapper userMapper;
60 62  
61 63 @Override
62   - public String login(AppAuthLoginReqVO reqVO, String userIp, String userAgent) {
  64 + public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) {
63 65 // 使用手机 + 密码,进行登录。
64   - LoginUser loginUser = login0(reqVO.getMobile(), reqVO.getPassword());
  66 + MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword());
65 67  
66   - // 缓存登录用户到 Redis 中,返回 Token 令牌
67   - return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(),
68   - LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent);
  68 + // 创建 Token 令牌,记录登录日志
  69 + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
69 70 }
70 71  
71 72 @Override
72 73 @Transactional
73   - public String smsLogin(AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent) {
  74 + public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) {
74 75 // 校验验证码
  76 + String userIp = getClientIP();
75 77 smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp));
76 78  
77 79 // 获得获得注册用户
78 80 MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp);
79 81 Assert.notNull(user, "获取用户失败,结果为空");
80 82  
81   - // 执行登陆
82   - LoginUser loginUser = buildLoginUser(user);
83   -
84   - // 缓存登录用户到 Redis 中,返回 Token 令牌
85   - return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(),
86   - LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent);
  83 + // 创建 Token 令牌,记录登录日志
  84 + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS);
87 85 }
88 86  
89 87 @Override
90   - public String socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) {
  88 + public AppAuthLoginRespVO socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO) {
91 89 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
92 90 Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
93 91 reqVO.getCode(), reqVO.getState());
... ... @@ -101,33 +99,30 @@ public class MemberAuthServiceImpl implements MemberAuthService {
101 99 throw exception(USER_NOT_EXISTS);
102 100 }
103 101  
104   - // 创建 LoginUser 对象
105   - LoginUser loginUser = buildLoginUser(user);
106   -
107   - // 缓存登录用户到 Redis 中,返回 Token 令牌
108   - return createUserSessionAfterLoginSuccess(loginUser, null,
109   - LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
  102 + // 创建 Token 令牌,记录登录日志
  103 + return createTokenAfterLoginSuccess(user, null, LoginLogTypeEnum.LOGIN_SOCIAL);
110 104 }
111 105  
112 106 @Override
113   - public String socialBindLogin(AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) {
  107 + public AppAuthLoginRespVO socialBindLogin(AppAuthSocialBindLoginReqVO reqVO) {
114 108 // 使用手机号、手机验证码登录
115 109 AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder()
116 110 .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build();
117   - String token = this.smsLogin(loginReqVO, userIp, userAgent);
118   - LoginUser loginUser = userSessionApi.getLoginUser(token);
  111 + AppAuthLoginRespVO token = smsLogin(loginReqVO);
119 112  
120 113 // 绑定社交用户
121   - socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
  114 + socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(token.getUserId(), getUserType().getValue(), reqVO));
122 115 return token;
123 116 }
124 117  
125   - private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String mobile,
126   - LoginLogTypeEnum logType, String userIp, String userAgent) {
  118 + private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType) {
127 119 // 插入登陆日志
128   - createLoginLog(loginUser.getId(), mobile, logType, LoginResultEnum.SUCCESS);
129   - // 缓存登录用户到 Redis 中,返回 Token 令牌
130   - return userSessionApi.createUserSession(loginUser, userIp, userAgent);
  120 + createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
  121 + // 创建 Token 令牌
  122 + OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO()
  123 + .setUserId(user.getId()).setUserType(getUserType().getValue()).setClientId(OAuth2ClientIdEnum.DEFAULT.getId()));
  124 + // 构建返回结果
  125 + return AuthConvert.INSTANCE.convert(accessTokenRespDTO);
131 126 }
132 127  
133 128 @Override
... ... @@ -135,7 +130,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
135 130 return socialUserApi.getAuthorizeUrl(type, redirectUri);
136 131 }
137 132  
138   - private LoginUser login0(String mobile, String password) {
  133 + private MemberUserDO login0(String mobile, String password) {
139 134 final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE;
140 135 // 校验账号是否存在
141 136 MemberUserDO user = userService.getUserByMobile(mobile);
... ... @@ -152,9 +147,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
152 147 createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.USER_DISABLED);
153 148 throw exception(AUTH_LOGIN_USER_DISABLED);
154 149 }
155   -
156   - // 构建 User 对象
157   - return buildLoginUser(user);
  150 + return user;
158 151 }
159 152  
160 153 private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) {
... ... @@ -177,15 +170,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
177 170  
178 171 @Override
179 172 public void logout(String token) {
180   - // 查询用户信息
181   - LoginUser loginUser = userSessionApi.getLoginUser(token);
182   - if (loginUser == null) {
  173 + // 删除访问令牌
  174 + OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token);
  175 + if (accessTokenRespDTO == null) {
183 176 return;
184 177 }
185   - // 删除 session
186   - userSessionApi.deleteUserSession(token);
187   - // 记录登出日志
188   - createLogoutLog(loginUser.getId());
  178 + // 删除成功,则记录登出日志
  179 + createLogoutLog(accessTokenRespDTO.getUserId());
189 180 }
190 181  
191 182 @Override
... ... @@ -219,6 +210,12 @@ public class MemberAuthServiceImpl implements MemberAuthService {
219 210 smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
220 211 }
221 212  
  213 + @Override
  214 + public AppAuthLoginRespVO refreshToken(String refreshToken) {
  215 + OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId());
  216 + return AuthConvert.INSTANCE.convert(accessTokenDO);
  217 + }
  218 +
222 219 /**
223 220 * 校验旧密码
224 221 *
... ... @@ -260,10 +257,6 @@ public class MemberAuthServiceImpl implements MemberAuthService {
260 257 loginLogApi.createLoginLog(reqDTO);
261 258 }
262 259  
263   - private LoginUser buildLoginUser(MemberUserDO user) {
264   - return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue());
265   - }
266   -
267 260 private String getMobile(Long userId) {
268 261 if (userId == null) {
269 262 return null;
... ...
yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceTest.java
... ... @@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthUpdatePasswo
9 9 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
10 10 import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
11 11 import cn.iocoder.yudao.module.member.service.user.MemberUserService;
12   -import cn.iocoder.yudao.module.system.api.auth.UserSessionApi;
  12 +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
13 13 import cn.iocoder.yudao.module.system.api.logger.LoginLogApi;
14 14 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
15 15 import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
... ... @@ -46,7 +46,7 @@ public class MemberAuthServiceTest extends BaseDbAndRedisUnitTest {
46 46 @MockBean
47 47 private LoginLogApi loginLogApi;
48 48 @MockBean
49   - private UserSessionApi userSessionApi;
  49 + private OAuth2TokenApi oauth2TokenApi;
50 50 @MockBean
51 51 private SocialUserApi socialUserApi;
52 52 @MockBean
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApi.java
... ... @@ -13,10 +13,37 @@ import javax.validation.Valid;
13 13 */
14 14 public interface OAuth2TokenApi {
15 15  
  16 + /**
  17 + * 创建访问令牌
  18 + *
  19 + * @param reqDTO 访问令牌的创建信息
  20 + * @return 访问令牌的信息
  21 + */
16 22 OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO);
17 23  
  24 + /**
  25 + * 校验访问令牌
  26 + *
  27 + * @param accessToken 访问令牌
  28 + * @return 访问令牌的信息
  29 + */
18 30 OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken);
19 31  
20   -// void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO);
  32 + /**
  33 + * 移除访问令牌
  34 + *
  35 + * @param accessToken 访问令牌
  36 + * @return 访问令牌的信息
  37 + */
  38 + OAuth2AccessTokenRespDTO removeAccessToken(String accessToken);
  39 +
  40 + /**
  41 + * 刷新访问令牌
  42 + *
  43 + * @param refreshToken 刷新令牌
  44 + * @param clientId 客户端编号
  45 + * @return 访问令牌的信息
  46 + */
  47 + OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId);
21 48  
22 49 }
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.api.auth.dto;
3 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
5 5 import lombok.Data;
6   -import lombok.experimental.Accessors;
7 6  
8 7 import javax.validation.constraints.NotNull;
9 8 import java.io.Serializable;
... ... @@ -14,14 +13,13 @@ import java.io.Serializable;
14 13 * @author 芋道源码
15 14 */
16 15 @Data
17   -@Accessors(chain = true)
18 16 public class OAuth2AccessTokenCreateReqDTO implements Serializable {
19 17  
20 18 /**
21 19 * 用户编号
22 20 */
23 21 @NotNull(message = "用户编号不能为空")
24   - private Integer userId;
  22 + private Long userId;
25 23 /**
26 24 * 用户类型
27 25 */
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenRespDTO.java
... ... @@ -26,7 +26,7 @@ public class OAuth2AccessTokenRespDTO implements Serializable {
26 26 /**
27 27 * 用户编号
28 28 */
29   - private Integer userId;
  29 + private Long userId;
30 30 /**
31 31 * 用户类型
32 32 */
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java
... ... @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO
4 4 import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
5 5 import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
6 6 import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
7 8 import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService;
8 9 import org.springframework.stereotype.Service;
9 10  
... ... @@ -22,7 +23,9 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi {
22 23  
23 24 @Override
24 25 public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) {
25   - return null;
  26 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(
  27 + reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId());
  28 + return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
26 29 }
27 30  
28 31 @Override
... ... @@ -30,4 +33,16 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi {
30 33 return OAuth2TokenConvert.INSTANCE.convert(oauth2TokenService.checkAccessToken(accessToken));
31 34 }
32 35  
  36 + @Override
  37 + public OAuth2AccessTokenRespDTO removeAccessToken(String accessToken) {
  38 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(accessToken);
  39 + return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
  40 + }
  41 +
  42 + @Override
  43 + public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId) {
  44 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
  45 + return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
  46 + }
  47 +
33 48 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
... ... @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
11 11 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
12 12 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
13 13 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
  14 +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
14 15 import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
15 16 import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
16 17 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
... ... @@ -70,14 +71,15 @@ public class AuthController {
70 71 public CommonResult<Boolean> logout(HttpServletRequest request) {
71 72 String token = obtainAuthorization(request, securityProperties.getTokenHeader());
72 73 if (StrUtil.isNotBlank(token)) {
73   - authService.logout(token);
  74 + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
74 75 }
75 76 return success(true);
76 77 }
77 78  
78 79 @PostMapping("/refresh-token")
79 80 @ApiOperation("刷新令牌")
80   - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 TODO 接口文档
  81 + @ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class)
  82 + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
81 83 public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
82 84 return success(authService.refreshToken(refreshToken));
83 85 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2TokenController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.auth;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO;
  7 +import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
  9 +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
  10 +import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
  11 +import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService;
  12 +import io.swagger.annotations.Api;
  13 +import io.swagger.annotations.ApiImplicitParam;
  14 +import io.swagger.annotations.ApiOperation;
  15 +import org.springframework.security.access.prepost.PreAuthorize;
  16 +import org.springframework.web.bind.annotation.*;
  17 +
  18 +import javax.annotation.Resource;
  19 +import javax.validation.Valid;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +@Api(tags = "管理后台 - OAuth2.0 令牌")
  24 +@RestController
  25 +@RequestMapping("/system/oauth2-token")
  26 +public class OAuth2TokenController {
  27 +
  28 + @Resource
  29 + private OAuth2TokenService oauth2TokenService;
  30 + @Resource
  31 + private AdminAuthService authService;
  32 +
  33 + @GetMapping("/page")
  34 + @ApiOperation(value = "获得访问令牌分页", notes = "只返回有效期内的")
  35 + @PreAuthorize("@ss.hasPermission('system:oauth2-token:page')")
  36 + public CommonResult<PageResult<OAuth2AccessTokenRespVO>> getAccessTokenPage(@Valid OAuth2AccessTokenPageReqVO reqVO) {
  37 + PageResult<OAuth2AccessTokenDO> pageResult = oauth2TokenService.getAccessTokenPage(reqVO);
  38 + return success(OAuth2TokenConvert.INSTANCE.convert(pageResult));
  39 + }
  40 +
  41 + @DeleteMapping("/delete")
  42 + @ApiOperation("删除访问令牌")
  43 + @ApiImplicitParam(name = "accessToken", value = "访问令牌", required = true, dataTypeClass = String.class, example = "tudou")
  44 + @PreAuthorize("@ss.hasPermission('system:oauth2-token:delete')")
  45 + public CommonResult<Boolean> deleteAccessToken(@RequestParam("accessToken") String accessToken) {
  46 + authService.logout(accessToken, LoginLogTypeEnum.LOGOUT_DELETE.getType());
  47 + return success(true);
  48 + }
  49 +
  50 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +
  9 +@ApiModel("管理后台 - 访问令牌分页 Request VO")
  10 +@Data
  11 +@EqualsAndHashCode(callSuper = true)
  12 +public class OAuth2AccessTokenPageReqVO extends PageParam {
  13 +
  14 + @ApiModelProperty(value = "用户编号", required = true, example = "666")
  15 + private Long userId;
  16 +
  17 + @ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举")
  18 + private Integer userType;
  19 +
  20 + @ApiModelProperty(value = "客户端编号", required = true, example = "2")
  21 + private Long clientId;
  22 +
  23 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Data;
  7 +import lombok.NoArgsConstructor;
  8 +
  9 +import java.util.Date;
  10 +
  11 +@ApiModel("管理后台 - 访问令牌 Response VO")
  12 +@Data
  13 +@NoArgsConstructor
  14 +@AllArgsConstructor
  15 +public class OAuth2AccessTokenRespVO {
  16 +
  17 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  18 + private Long id;
  19 +
  20 + @ApiModelProperty(value = "访问令牌", required = true, example = "tudou")
  21 + private String accessToken;
  22 +
  23 + @ApiModelProperty(value = "刷新令牌", required = true, example = "nice")
  24 + private String refreshToken;
  25 +
  26 + @ApiModelProperty(value = "用户编号", required = true, example = "666")
  27 + private Long userId;
  28 +
  29 + @ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举")
  30 + private Integer userType;
  31 +
  32 + @ApiModelProperty(value = "客户端编号", required = true, example = "2")
  33 + private Long clientId;
  34 +
  35 + @ApiModelProperty(value = "创建时间", required = true)
  36 + private Date createTime;
  37 +
  38 + @ApiModelProperty(value = "过期时间", required = true)
  39 + private Date expiresTime;
  40 +
  41 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java
1 1 package cn.iocoder.yudao.module.system.convert.auth;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
  5 +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO;
4 7 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
5 8 import org.mapstruct.Mapper;
6 9 import org.mapstruct.factory.Mappers;
... ... @@ -12,4 +15,8 @@ public interface OAuth2TokenConvert {
12 15  
13 16 OAuth2AccessTokenCheckRespDTO convert(OAuth2AccessTokenDO bean);
14 17  
  18 + PageResult<OAuth2AccessTokenRespVO> convert(PageResult<OAuth2AccessTokenDO> page);
  19 +
  20 + OAuth2AccessTokenRespDTO convert2(OAuth2AccessTokenDO bean);
  21 +
15 22 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java
1 1 package cn.iocoder.yudao.module.system.dal.mysql.auth;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
4 7 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
5 8 import org.apache.ibatis.annotations.Mapper;
6 9  
  10 +import java.util.Date;
7 11 import java.util.List;
8 12  
9 13 @Mapper
... ... @@ -17,4 +21,13 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX&lt;OAuth2AccessTokenDO
17 21 return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
18 22 }
19 23  
  24 + default PageResult<OAuth2AccessTokenDO> selectPage(OAuth2AccessTokenPageReqVO reqVO) {
  25 + return selectPage(reqVO, new LambdaQueryWrapperX<OAuth2AccessTokenDO>()
  26 + .eqIfPresent(OAuth2AccessTokenDO::getUserId, reqVO.getUserId())
  27 + .eqIfPresent(OAuth2AccessTokenDO::getUserType, reqVO.getUserType())
  28 + .eqIfPresent(OAuth2AccessTokenDO::getClientId, reqVO.getClientId())
  29 + .gt(OAuth2AccessTokenDO::getExpiresTime, new Date())
  30 + .orderByDesc(OAuth2AccessTokenDO::getId));
  31 + }
  32 +
20 33 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
... ... @@ -25,8 +25,9 @@ public interface AdminAuthService {
25 25 * 基于 token 退出登录
26 26 *
27 27 * @param token token
  28 + * @param logType 登出类型
28 29 */
29   - void logout(String token);
  30 + void logout(String token, Integer logType);
30 31  
31 32 /**
32 33 * 短信验证码发送
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
... ... @@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
18 18 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
19 19 import cn.iocoder.yudao.module.system.service.common.CaptchaService;
20 20 import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
  21 +import cn.iocoder.yudao.module.system.service.member.MemberService;
21 22 import cn.iocoder.yudao.module.system.service.social.SocialUserService;
22 23 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
23 24 import com.google.common.annotations.VisibleForTesting;
... ... @@ -51,6 +52,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
51 52 private OAuth2TokenService oauth2TokenService;
52 53 @Resource
53 54 private SocialUserService socialUserService;
  55 + @Resource
  56 + private MemberService memberService;
54 57  
55 58 @Resource
56 59 private Validator validator;
... ... @@ -209,23 +212,27 @@ public class AdminAuthServiceImpl implements AdminAuthService {
209 212 }
210 213  
211 214 @Override
212   - public void logout(String token) {
  215 + public void logout(String token, Integer logType) {
213 216 // 删除访问令牌
214 217 OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token);
215 218 if (accessTokenDO == null) {
216 219 return;
217 220 }
218 221 // 删除成功,则记录登出日志
219   - createLogoutLog(accessTokenDO.getUserId());
  222 + createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType);
220 223 }
221 224  
222   - private void createLogoutLog(Long userId) {
  225 + private void createLogoutLog(Long userId, Integer userType, Integer logType) {
223 226 LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
224   - reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType());
  227 + reqDTO.setLogType(logType);
225 228 reqDTO.setTraceId(TracerUtils.getTraceId());
226 229 reqDTO.setUserId(userId);
227   - reqDTO.setUsername(getUsername(userId));
228   - reqDTO.setUserType(getUserType().getValue());
  230 + reqDTO.setUserType(userType);
  231 + if (ObjectUtil.notEqual(getUserType(), userType)) {
  232 + reqDTO.setUsername(getUsername(userId));
  233 + } else {
  234 + reqDTO.setUsername(memberService.getMemberUserMobile(userId));
  235 + }
229 236 reqDTO.setUserAgent(ServletUtils.getUserAgent());
230 237 reqDTO.setUserIp(ServletUtils.getClientIP());
231 238 reqDTO.setResult(LoginResultEnum.SUCCESS.getResult());
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
1 1 package cn.iocoder.yudao.module.system.service.auth;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
3 5 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
4 6  
5 7 /**
... ... @@ -64,4 +66,12 @@ public interface OAuth2TokenService {
64 66 */
65 67 OAuth2AccessTokenDO removeAccessToken(String accessToken);
66 68  
  69 + /**
  70 + * 获得访问令牌分页
  71 + *
  72 + * @param reqVO 请求
  73 + * @return 访问令牌分页
  74 + */
  75 + PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO);
  76 +
67 77 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
... ... @@ -4,8 +4,10 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.IdUtil;
5 5 import cn.hutool.core.util.ObjectUtil;
6 6 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 8 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
8 9 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  10 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
9 11 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
10 12 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
11 13 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
... ... @@ -125,6 +127,11 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
125 127 return accessTokenDO;
126 128 }
127 129  
  130 + @Override
  131 + public PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) {
  132 + return oauth2AccessTokenMapper.selectPage(reqVO);
  133 + }
  134 +
128 135 private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
129 136 OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
130 137 .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId())
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java
... ... @@ -221,7 +221,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
221 221 when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO);
222 222  
223 223 // 调用
224   - authService.logout(token);
  224 + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
225 225 // 校验调用参数
226 226 verify(loginLogService).createLoginLog(argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
227 227 && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
... ... @@ -234,7 +234,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
234 234 String token = randomString();
235 235  
236 236 // 调用
237   - authService.logout(token);
  237 + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
238 238 // 校验调用参数
239 239 verify(loginLogService, never()).createLoginLog(any());
240 240 }
... ...
yudao-server/pom.xml
... ... @@ -21,11 +21,11 @@
21 21 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
22 22  
23 23 <dependencies>
24   -<!-- <dependency>-->
25   -<!-- <groupId>cn.iocoder.boot</groupId>-->
26   -<!-- <artifactId>yudao-module-member-biz</artifactId>-->
27   -<!-- <version>${revision}</version>-->
28   -<!-- </dependency>-->
  24 + <dependency>
  25 + <groupId>cn.iocoder.boot</groupId>
  26 + <artifactId>yudao-module-member-biz</artifactId>
  27 + <version>${revision}</version>
  28 + </dependency>
29 29 <dependency>
30 30 <groupId>cn.iocoder.boot</groupId>
31 31 <artifactId>yudao-module-system-biz</artifactId>
... ...
yudao-server/src/main/resources/application-dev.yaml
... ... @@ -173,8 +173,6 @@ wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-sta
173 173 yudao:
174 174 security:
175 175 token-header: Authorization
176   - token-timeout: 1d
177   - session-timeout: 30m
178 176 mock-enable: true
179 177 mock-secret: test
180 178 xss:
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -193,8 +193,6 @@ yudao:
193 193 enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试
194 194 security:
195 195 token-header: Authorization
196   - token-timeout: 1d
197   - session-timeout: 1d
198 196 mock-enable: true
199 197 mock-secret: test
200 198 xss:
... ...
yudao-ui-admin/src/api/system/session.js renamed to yudao-ui-admin/src/api/system/oauth2/oauth2Token.js
1 1 import request from '@/utils/request'
2 2  
3   -// 查询在线用户列表
4   -export function list(query) {
  3 +// 获得访问令牌分页
  4 +export function getAccessTokenPage(query) {
5 5 return request({
6   - url: '/system/user-session/page',
  6 + url: '/system/oauth2-token/page',
7 7 method: 'get',
8 8 params: query
9 9 })
10 10 }
11 11  
12   -// 强退用户
13   -export function forceLogout(tokenId) {
  12 +// 删除访问令牌
  13 +export function deleteAccessToken(accessToken) {
14 14 return request({
15   - url: '/system/user-session/delete?id=' + tokenId,
  15 + url: '/system/oauth2-token/delete?accessToken=' + accessToken,
16 16 method: 'delete'
17 17 })
18 18 }
... ...
yudao-ui-admin/src/views/system/session/index.vue renamed to yudao-ui-admin/src/views/system/oauth2/token/index.vue
... ... @@ -3,11 +3,14 @@
3 3 <doc-alert title="用户体系" url="https://doc.iocoder.cn/user-center/" />
4 4 <!-- 搜索工作栏 -->
5 5 <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
6   - <el-form-item label="登录地址" prop="userIp">
7   - <el-input v-model="queryParams.userIp" placeholder="请输入登录地址" clearable @keyup.enter.native="handleQuery"/>
  6 + <el-form-item label="用户编号" prop="userId">
  7 + <el-input v-model="queryParams.userId" placeholder="请输入用户编号" clearable @keyup.enter.native="handleQuery"/>
8 8 </el-form-item>
9   - <el-form-item label="用户名称" prop="username">
10   - <el-input v-model="queryParams.username" placeholder="请输入用户名称" clearable @keyup.enter.native="handleQuery"/>
  9 + <el-form-item label="用户类型" prop="userType">
  10 + <el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable>
  11 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.USER_TYPE)"
  12 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  13 + </el-select>
11 14 </el-form-item>
12 15 <el-form-item>
13 16 <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
... ... @@ -16,20 +19,28 @@
16 19  
17 20 </el-form>
18 21 <el-table v-loading="loading" :data="list" style="width: 100%;">
19   - <el-table-column label="会话编号" align="center" prop="id" width="300" />
20   - <el-table-column label="登录名称" align="center" prop="username" width="100" />
21   - <el-table-column label="部门名称" align="center" prop="deptName" width="100" />
22   - <el-table-column label="登录地址" align="center" prop="userIp" width="100" />
23   - <el-table-column label="userAgent" align="center" prop="userAgent" :show-overflow-tooltip="true" />
24   - <el-table-column label="登录时间" align="center" prop="createTime" width="180">
  22 + <el-table-column label="访问令牌" align="center" prop="accessToken" width="300" />
  23 + <el-table-column label="刷新令牌" align="center" prop="refreshToken" width="300" />
  24 + <el-table-column label="用户编号" align="center" prop="userId" />
  25 + <el-table-column label="用户类型" align="center" prop="userType" width="100">
  26 + <template slot-scope="scope">
  27 + <dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType"/>
  28 + </template>
  29 + </el-table-column>
  30 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
25 31 <template slot-scope="scope">
26 32 <span>{{ parseTime(scope.row.createTime) }}</span>
27 33 </template>
28 34 </el-table-column>
  35 + <el-table-column label="过期时间" align="center" prop="expiresTime" width="180">
  36 + <template slot-scope="scope">
  37 + <span>{{ parseTime(scope.row.expiresTime) }}</span>
  38 + </template>
  39 + </el-table-column>
29 40 <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
30 41 <template slot-scope="scope">
31 42 <el-button size="mini" type="text" icon="el-icon-delete" @click="handleForceLogout(scope.row)"
32   - v-hasPermi="['system:user-session:delete']">强退</el-button>
  43 + v-hasPermi="['system:oauth2-token:delete']">强退</el-button>
33 44 </template>
34 45 </el-table-column>
35 46 </el-table>
... ... @@ -40,10 +51,10 @@
40 51 </template>
41 52  
42 53 <script>
43   -import { list, forceLogout } from "@/api/system/session";
  54 +import { getAccessTokenPage, deleteAccessToken } from "@/api/system/oauth2/oauth2Token";
44 55  
45 56 export default {
46   - name: "Online",
  57 + name: "OAuth2Token",
47 58 data() {
48 59 return {
49 60 // 遮罩层
... ... @@ -56,8 +67,8 @@ export default {
56 67 queryParams: {
57 68 pageNo: 1,
58 69 pageSize: 10,
59   - userIp: undefined,
60   - username: undefined
  70 + userId: undefined,
  71 + userType: undefined
61 72 }
62 73 };
63 74 },
... ... @@ -68,7 +79,7 @@ export default {
68 79 /** 查询登录日志列表 */
69 80 getList() {
70 81 this.loading = true;
71   - list(this.queryParams).then(response => {
  82 + getAccessTokenPage(this.queryParams).then(response => {
72 83 this.list = response.data.list;
73 84 this.total = response.data.total;
74 85 this.loading = false;
... ... @@ -86,8 +97,8 @@ export default {
86 97 },
87 98 /** 强退按钮操作 */
88 99 handleForceLogout(row) {
89   - this.$modal.confirm('是否确认强退名称为"' + row.username + '"的数据项?').then(function() {
90   - return forceLogout(row.id);
  100 + this.$modal.confirm('是否确认强退令牌为"' + row.accessToken + '"的数据项?').then(function() {
  101 + return deleteAccessToken(row.accessToken);
91 102 }).then(() => {
92 103 this.getList();
93 104 this.$modal.msgSuccess("强退成功");
... ...