Browse Source

1. 修改在线会话的实现
2. 接入到会员管理 OAuth2.0

YunaiV 3 năm trước cách đây
mục cha
commit
5cf68961e1
29 tập tin đã thay đổi với 368 bổ sung162 xóa
  1. 0 13
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/SecurityProperties.java
  2. 7 2
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
  3. 17 13
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
  4. 14 3
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java
  5. 3 4
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java
  6. 15 16
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java
  7. 40 47
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
  8. 2 2
      yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceTest.java
  9. 28 1
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApi.java
  10. 1 3
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java
  11. 1 1
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenRespDTO.java
  12. 16 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java
  13. 4 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
  14. 50 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2TokenController.java
  15. 23 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenPageReqVO.java
  16. 41 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenRespVO.java
  17. 7 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java
  18. 13 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java
  19. 2 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
  20. 13 6
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
  21. 10 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
  22. 7 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
  23. 2 2
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java
  24. 5 5
      yudao-server/pom.xml
  25. 0 2
      yudao-server/src/main/resources/application-dev.yaml
  26. 0 2
      yudao-server/src/main/resources/application-local.yaml
  27. 18 0
      yudao-ui-admin/src/api/system/oauth2/oauth2Token.js
  28. 0 18
      yudao-ui-admin/src/api/system/session.js
  29. 29 18
      yudao-ui-admin/src/views/system/oauth2/token/index.vue

+ 0 - 13
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;
 
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
-import java.time.Duration;
 
 @ConfigurationProperties(prefix = "yudao.security")
 @Validated
@@ -18,18 +17,6 @@ public class SecurityProperties {
      */
     @NotEmpty(message = "Token Header 不能为空")
     private String tokenHeader;
-    /**
-     * Token 过期时间
-     */
-    @NotNull(message = "Token 过期时间不能为空")
-    private Duration tokenTimeout;
-    /**
-     * Session 过期时间
-     *
-     * 当 User 用户超过当前时间未操作,则 Session 会过期
-     */
-    @NotNull(message = "Session 过期时间不能为空")
-    private Duration sessionTimeout;
 
     /**
      * mock 模式的开关

+ 7 - 2
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}}
 }
 
 ### 请求 /sms-login 接口 => 成功
-POST {{appApi}}/member/sms-login
+POST {{appApi}}/member/auth/sms-login
 Content-Type: application/json
 tenant-id: {{appTenentId}}
 
@@ -29,7 +29,12 @@ tenant-id: {{appTenentId}}
 }
 
 ### 请求 /logout 接口 => 成功
-POST {{appApi}}/member/logout
+POST {{appApi}}/member/auth/logout
 Content-Type: application/json
 Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66
 tenant-id: {{appTenentId}}
+
+### 请求 /auth/refresh-token 接口 => 成功
+POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70
+Content-Type: application/json
+tenant-id: {{appTenentId}}

+ 17 - 13
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;
 
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
@@ -20,8 +21,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.validation.Valid;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
-import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent;
 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 
 @Api(tags = "用户 APP - 认证")
@@ -40,8 +39,7 @@ public class AppAuthController {
     @PostMapping("/login")
     @ApiOperation("使用手机 + 密码登录")
     public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) {
-        String token = authService.login(reqVO, getClientIP(), getUserAgent());
-        return success(AppAuthLoginRespVO.builder().token(token).build());
+        return success(authService.login(reqVO));
     }
 
     @PostMapping("/logout")
@@ -54,12 +52,20 @@ public class AppAuthController {
         return success(true);
     }
 
+    @PostMapping("/refresh-token")
+    @ApiOperation("刷新令牌")
+    @ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class)
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+    public CommonResult<AppAuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
+        return success(authService.refreshToken(refreshToken));
+    }
+
+    // ========== 短信登录相关 ==========
+
     @PostMapping("/sms-login")
     @ApiOperation("使用手机 + 验证码登录")
     public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
-        String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
-        // 返回结果
-        return success(AppAuthLoginRespVO.builder().token(token).build());
+        return success(authService.smsLogin(reqVO));
     }
 
     @PostMapping("/send-sms-code")
@@ -100,16 +106,14 @@ public class AppAuthController {
 
     @PostMapping("/social-quick-login")
     @ApiOperation(value = "社交快捷登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户")
-    public CommonResult<AppAuthLoginRespVO> socialLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) {
-        String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent());
-        return success(AppAuthLoginRespVO.builder().token(token).build());
+    public CommonResult<AppAuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) {
+        return success(authService.socialQuickLogin(reqVO));
     }
 
     @PostMapping("/social-bind-login")
     @ApiOperation(value = "社交绑定登录,使用 手机号 + 手机验证码", notes = "适合未登录的用户,进行登录 + 绑定")
-    public CommonResult<AppAuthLoginRespVO> socialLogin2(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) {
-        String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent());
-        return success(AppAuthLoginRespVO.builder().token(token).build());
+    public CommonResult<AppAuthLoginRespVO> socialBindLogin(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) {
+        return success(authService.socialBindLogin(reqVO));
     }
 
 }

+ 14 - 3
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;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-@ApiModel("用户 APP - 手机密码登录 Response VO")
+import java.util.Date;
+
+@ApiModel("用户 APP - 登录 Response VO")
 @Data
 @NoArgsConstructor
 @AllArgsConstructor
 @Builder
 public class AppAuthLoginRespVO {
 
-    @ApiModelProperty(value = "token", required = true, example = "yudaoyuanma")
-    private String token;
+    @ApiModelProperty(value = "用户编号", required = true, example = "1024")
+    private Long userId;
+
+    @ApiModelProperty(value = "访问令牌", required = true, example = "happy")
+    private String accessToken;
+
+    @ApiModelProperty(value = "刷新令牌", required = true, example = "nice")
+    private String refreshToken;
+
+    @ApiModelProperty(value = "过期时间", required = true)
+    private Date expiresTime;
 
 }

+ 3 - 4
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java

@@ -1,9 +1,8 @@
 package cn.iocoder.yudao.module.member.convert.auth;
 
-import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
 import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
-import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
@@ -17,8 +16,6 @@ public interface AuthConvert {
 
     AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
 
-    LoginUser convert(MemberUserDO bean);
-
     SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO);
     SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO);
     SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO);
@@ -27,4 +24,6 @@ public interface AuthConvert {
     SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp);
     SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp);
 
+    AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean);
+
 }

+ 15 - 16
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 {
      * 手机 + 密码登录
      *
      * @param reqVO 登录信息
-     * @param userIp 用户 IP
-     * @param userAgent 用户 UA
-     * @return 身份令牌,使用 JWT 方式
+     * @return 登录结果
      */
-    String login(@Valid AppAuthLoginReqVO reqVO, String userIp, String userAgent);
+    AppAuthLoginRespVO login(@Valid AppAuthLoginReqVO reqVO);
 
     /**
      * 基于 token 退出登录
@@ -34,31 +32,25 @@ public interface MemberAuthService {
      * 手机 + 验证码登陆
      *
      * @param reqVO 登陆信息
-     * @param userIp 用户 IP
-     * @param userAgent 用户 UA
-     * @return 身份令牌,使用 JWT 方式
+     * @return 登录结果
      */
-    String smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent);
+    AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO);
 
     /**
      * 社交登录,使用 code 授权码
      *
      * @param reqVO 登录信息
-     * @param userIp 用户 IP
-     * @param userAgent 用户 UA
-     * @return 身份令牌,使用 JWT 方式
+     * @return 登录结果
      */
-    String socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent);
+    AppAuthLoginRespVO socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO);
 
     /**
      * 社交登录,使用 手机号 + 手机验证码
      *
      * @param reqVO 登录信息
-     * @param userIp 用户 IP
-     * @param userAgent 用户 UA
-     * @return 身份令牌,使用 JWT 方式
+     * @return 登录结果
      */
-    String socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent);
+    AppAuthLoginRespVO socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO);
 
     /**
      * 获得社交认证 URL
@@ -90,4 +82,11 @@ public interface MemberAuthService {
      */
     void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO);
 
+    /**
+     * 刷新访问令牌
+     *
+     * @param refreshToken 刷新令牌
+     * @return 登录结果
+     */
+    AppAuthLoginRespVO refreshToken(String refreshToken);
 }

+ 40 - 47
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;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
-import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
 import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
 import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
 import cn.iocoder.yudao.module.member.service.user.MemberUserService;
-import cn.iocoder.yudao.module.system.api.auth.UserSessionApi;
+import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
 import cn.iocoder.yudao.module.system.api.logger.LoginLogApi;
 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
 import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
+import cn.iocoder.yudao.module.system.enums.auth.OAuth2ClientIdEnum;
 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
 import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
@@ -49,9 +51,9 @@ public class MemberAuthServiceImpl implements MemberAuthService {
     @Resource
     private LoginLogApi loginLogApi;
     @Resource
-    private UserSessionApi userSessionApi;
-    @Resource
     private SocialUserApi socialUserApi;
+    @Resource
+    private OAuth2TokenApi oauth2TokenApi;
 
     @Resource
     private PasswordEncoder passwordEncoder;
@@ -59,35 +61,31 @@ public class MemberAuthServiceImpl implements MemberAuthService {
     private MemberUserMapper userMapper;
 
     @Override
-    public String login(AppAuthLoginReqVO reqVO, String userIp, String userAgent) {
+    public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) {
         // 使用手机 + 密码,进行登录。
-        LoginUser loginUser = login0(reqVO.getMobile(), reqVO.getPassword());
+        MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword());
 
-        // 缓存登录用户到 Redis 中,返回 Token 令牌
-        return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(),
-                LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent);
+        // 创建 Token 令牌,记录登录日志
+        return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
     }
 
     @Override
     @Transactional
-    public String smsLogin(AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent) {
+    public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) {
         // 校验验证码
+        String userIp = getClientIP();
         smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp));
 
         // 获得获得注册用户
         MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp);
         Assert.notNull(user, "获取用户失败,结果为空");
 
-        // 执行登陆
-        LoginUser loginUser = buildLoginUser(user);
-
-        // 缓存登录用户到 Redis 中,返回 Token 令牌
-        return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(),
-                LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent);
+        // 创建 Token 令牌,记录登录日志
+        return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS);
     }
 
     @Override
-    public String socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) {
+    public AppAuthLoginRespVO socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO) {
         // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
         Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
                 reqVO.getCode(), reqVO.getState());
@@ -101,33 +99,30 @@ public class MemberAuthServiceImpl implements MemberAuthService {
             throw exception(USER_NOT_EXISTS);
         }
 
-        // 创建 LoginUser 对象
-        LoginUser loginUser = buildLoginUser(user);
-
-        // 缓存登录用户到 Redis 中,返回 Token 令牌
-        return createUserSessionAfterLoginSuccess(loginUser, null,
-                LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
+        // 创建 Token 令牌,记录登录日志
+        return createTokenAfterLoginSuccess(user, null, LoginLogTypeEnum.LOGIN_SOCIAL);
     }
 
     @Override
-    public String socialBindLogin(AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) {
+    public AppAuthLoginRespVO socialBindLogin(AppAuthSocialBindLoginReqVO reqVO) {
         // 使用手机号、手机验证码登录
         AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder()
                 .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build();
-        String token = this.smsLogin(loginReqVO, userIp, userAgent);
-        LoginUser loginUser = userSessionApi.getLoginUser(token);
+        AppAuthLoginRespVO token = smsLogin(loginReqVO);
 
         // 绑定社交用户
-        socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
+        socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(token.getUserId(), getUserType().getValue(), reqVO));
         return token;
     }
 
-    private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String mobile,
-                                                      LoginLogTypeEnum logType, String userIp, String userAgent) {
+    private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType) {
         // 插入登陆日志
-        createLoginLog(loginUser.getId(), mobile, logType, LoginResultEnum.SUCCESS);
-        // 缓存登录用户到 Redis 中,返回 Token 令牌
-        return userSessionApi.createUserSession(loginUser, userIp, userAgent);
+        createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
+        // 创建 Token 令牌
+        OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO()
+                .setUserId(user.getId()).setUserType(getUserType().getValue()).setClientId(OAuth2ClientIdEnum.DEFAULT.getId()));
+        // 构建返回结果
+        return AuthConvert.INSTANCE.convert(accessTokenRespDTO);
     }
 
     @Override
@@ -135,7 +130,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
         return socialUserApi.getAuthorizeUrl(type, redirectUri);
     }
 
-    private LoginUser login0(String mobile, String password) {
+    private MemberUserDO login0(String mobile, String password) {
         final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE;
         // 校验账号是否存在
         MemberUserDO user = userService.getUserByMobile(mobile);
@@ -152,9 +147,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
             createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.USER_DISABLED);
             throw exception(AUTH_LOGIN_USER_DISABLED);
         }
-
-        // 构建 User 对象
-        return buildLoginUser(user);
+        return user;
     }
 
     private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) {
@@ -177,15 +170,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
 
     @Override
     public void logout(String token) {
-        // 查询用户信息
-        LoginUser loginUser = userSessionApi.getLoginUser(token);
-        if (loginUser == null) {
+        // 删除访问令牌
+        OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token);
+        if (accessTokenRespDTO == null) {
             return;
         }
-        // 删除 session
-        userSessionApi.deleteUserSession(token);
-        // 记录登出日志
-        createLogoutLog(loginUser.getId());
+        // 删除成功,则记录登出日志
+        createLogoutLog(accessTokenRespDTO.getUserId());
     }
 
     @Override
@@ -219,6 +210,12 @@ public class MemberAuthServiceImpl implements MemberAuthService {
         smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
     }
 
+    @Override
+    public AppAuthLoginRespVO refreshToken(String refreshToken) {
+        OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId());
+        return AuthConvert.INSTANCE.convert(accessTokenDO);
+    }
+
     /**
      * 校验旧密码
      *
@@ -260,10 +257,6 @@ public class MemberAuthServiceImpl implements MemberAuthService {
         loginLogApi.createLoginLog(reqDTO);
     }
 
-    private LoginUser buildLoginUser(MemberUserDO user) {
-        return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue());
-    }
-
     private String getMobile(Long userId) {
         if (userId == null) {
             return null;

+ 2 - 2
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
 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
 import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
 import cn.iocoder.yudao.module.member.service.user.MemberUserService;
-import cn.iocoder.yudao.module.system.api.auth.UserSessionApi;
+import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi;
 import cn.iocoder.yudao.module.system.api.logger.LoginLogApi;
 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
 import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
@@ -46,7 +46,7 @@ public class MemberAuthServiceTest extends BaseDbAndRedisUnitTest {
     @MockBean
     private LoginLogApi loginLogApi;
     @MockBean
-    private UserSessionApi userSessionApi;
+    private OAuth2TokenApi oauth2TokenApi;
     @MockBean
     private SocialUserApi socialUserApi;
     @MockBean

+ 28 - 1
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;
  */
 public interface OAuth2TokenApi {
 
+    /**
+     * 创建访问令牌
+     *
+     * @param reqDTO 访问令牌的创建信息
+     * @return 访问令牌的信息
+     */
     OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO);
 
+    /**
+     * 校验访问令牌
+     *
+     * @param accessToken 访问令牌
+     * @return 访问令牌的信息
+     */
     OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken);
 
-//    void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO);
+    /**
+     * 移除访问令牌
+     *
+     * @param accessToken 访问令牌
+     * @return 访问令牌的信息
+     */
+    OAuth2AccessTokenRespDTO removeAccessToken(String accessToken);
+
+    /**
+     * 刷新访问令牌
+     *
+     * @param refreshToken 刷新令牌
+     * @param clientId 客户端编号
+     * @return 访问令牌的信息
+     */
+    OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId);
 
 }

+ 1 - 3
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;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
 import lombok.Data;
-import lombok.experimental.Accessors;
 
 import javax.validation.constraints.NotNull;
 import java.io.Serializable;
@@ -14,14 +13,13 @@ import java.io.Serializable;
  * @author 芋道源码
  */
 @Data
-@Accessors(chain = true)
 public class OAuth2AccessTokenCreateReqDTO implements Serializable {
 
     /**
      * 用户编号
      */
     @NotNull(message = "用户编号不能为空")
-    private Integer userId;
+    private Long userId;
     /**
      * 用户类型
      */

+ 1 - 1
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 {
     /**
      * 用户编号
      */
-    private Integer userId;
+    private Long userId;
     /**
      * 用户类型
      */

+ 16 - 1
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
 import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO;
 import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
 import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert;
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
 import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService;
 import org.springframework.stereotype.Service;
 
@@ -22,7 +23,9 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi {
 
     @Override
     public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) {
-        return null;
+        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(
+                reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId());
+        return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
     }
 
     @Override
@@ -30,4 +33,16 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi {
         return OAuth2TokenConvert.INSTANCE.convert(oauth2TokenService.checkAccessToken(accessToken));
     }
 
+    @Override
+    public OAuth2AccessTokenRespDTO removeAccessToken(String accessToken) {
+        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(accessToken);
+        return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
+    }
+
+    @Override
+    public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId) {
+        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
+        return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO);
+    }
+
 }

+ 4 - 2
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;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
 import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum;
 import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
@@ -70,14 +71,15 @@ public class AuthController {
     public CommonResult<Boolean> logout(HttpServletRequest request) {
         String token = obtainAuthorization(request, securityProperties.getTokenHeader());
         if (StrUtil.isNotBlank(token)) {
-            authService.logout(token);
+            authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
         }
         return success(true);
     }
 
     @PostMapping("/refresh-token")
     @ApiOperation("刷新令牌")
-    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 TODO 接口文档
+    @ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class)
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
     public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
         return success(authService.refreshToken(refreshToken));
     }

+ 50 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2TokenController.java

@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.system.controller.admin.auth;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO;
+import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert;
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
+import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
+import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
+import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Api(tags = "管理后台 - OAuth2.0 令牌")
+@RestController
+@RequestMapping("/system/oauth2-token")
+public class OAuth2TokenController {
+
+    @Resource
+    private OAuth2TokenService oauth2TokenService;
+    @Resource
+    private AdminAuthService authService;
+
+    @GetMapping("/page")
+    @ApiOperation(value = "获得访问令牌分页", notes = "只返回有效期内的")
+    @PreAuthorize("@ss.hasPermission('system:oauth2-token:page')")
+    public CommonResult<PageResult<OAuth2AccessTokenRespVO>> getAccessTokenPage(@Valid OAuth2AccessTokenPageReqVO reqVO) {
+        PageResult<OAuth2AccessTokenDO> pageResult = oauth2TokenService.getAccessTokenPage(reqVO);
+        return success(OAuth2TokenConvert.INSTANCE.convert(pageResult));
+    }
+
+    @DeleteMapping("/delete")
+    @ApiOperation("删除访问令牌")
+    @ApiImplicitParam(name = "accessToken", value = "访问令牌", required = true, dataTypeClass = String.class, example = "tudou")
+    @PreAuthorize("@ss.hasPermission('system:oauth2-token:delete')")
+    public CommonResult<Boolean> deleteAccessToken(@RequestParam("accessToken") String accessToken) {
+        authService.logout(accessToken, LoginLogTypeEnum.LOGOUT_DELETE.getType());
+        return success(true);
+    }
+
+}

+ 23 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenPageReqVO.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@ApiModel("管理后台 - 访问令牌分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class OAuth2AccessTokenPageReqVO extends PageParam {
+
+    @ApiModelProperty(value = "用户编号", required = true, example = "666")
+    private Long userId;
+
+    @ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举")
+    private Integer userType;
+
+    @ApiModelProperty(value = "客户端编号", required = true, example = "2")
+    private Long clientId;
+
+}

+ 41 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenRespVO.java

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@ApiModel("管理后台 - 访问令牌 Response VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class OAuth2AccessTokenRespVO {
+
+    @ApiModelProperty(value = "编号", required = true, example = "1024")
+    private Long id;
+
+    @ApiModelProperty(value = "访问令牌", required = true, example = "tudou")
+    private String accessToken;
+
+    @ApiModelProperty(value = "刷新令牌", required = true, example = "nice")
+    private String refreshToken;
+
+    @ApiModelProperty(value = "用户编号", required = true, example = "666")
+    private Long userId;
+
+    @ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举")
+    private Integer userType;
+
+    @ApiModelProperty(value = "客户端编号", required = true, example = "2")
+    private Long clientId;
+
+    @ApiModelProperty(value = "创建时间", required = true)
+    private Date createTime;
+
+    @ApiModelProperty(value = "过期时间", required = true)
+    private Date expiresTime;
+
+}

+ 7 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java

@@ -1,6 +1,9 @@
 package cn.iocoder.yudao.module.system.convert.auth;
 
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO;
+import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO;
+import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
@@ -12,4 +15,8 @@ public interface OAuth2TokenConvert {
 
     OAuth2AccessTokenCheckRespDTO convert(OAuth2AccessTokenDO bean);
 
+    PageResult<OAuth2AccessTokenRespVO> convert(PageResult<OAuth2AccessTokenDO> page);
+
+    OAuth2AccessTokenRespDTO convert2(OAuth2AccessTokenDO bean);
+
 }

+ 13 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java

@@ -1,9 +1,13 @@
 package cn.iocoder.yudao.module.system.dal.mysql.auth;
 
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.Date;
 import java.util.List;
 
 @Mapper
@@ -17,4 +21,13 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO
         return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken);
     }
 
+    default PageResult<OAuth2AccessTokenDO> selectPage(OAuth2AccessTokenPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<OAuth2AccessTokenDO>()
+                .eqIfPresent(OAuth2AccessTokenDO::getUserId, reqVO.getUserId())
+                .eqIfPresent(OAuth2AccessTokenDO::getUserType, reqVO.getUserType())
+                .eqIfPresent(OAuth2AccessTokenDO::getClientId, reqVO.getClientId())
+                .gt(OAuth2AccessTokenDO::getExpiresTime, new Date())
+                .orderByDesc(OAuth2AccessTokenDO::getId));
+    }
+
 }

+ 2 - 1
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 {
      * 基于 token 退出登录
      *
      * @param token token
+     * @param logType 登出类型
      */
-    void logout(String token);
+    void logout(String token, Integer logType);
 
     /**
      * 短信验证码发送

+ 13 - 6
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;
 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
 import cn.iocoder.yudao.module.system.service.common.CaptchaService;
 import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
+import cn.iocoder.yudao.module.system.service.member.MemberService;
 import cn.iocoder.yudao.module.system.service.social.SocialUserService;
 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
 import com.google.common.annotations.VisibleForTesting;
@@ -51,6 +52,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
     private OAuth2TokenService oauth2TokenService;
     @Resource
     private SocialUserService socialUserService;
+    @Resource
+    private MemberService memberService;
 
     @Resource
     private Validator validator;
@@ -209,23 +212,27 @@ public class AdminAuthServiceImpl implements AdminAuthService {
     }
 
     @Override
-    public void logout(String token) {
+    public void logout(String token, Integer logType) {
         // 删除访问令牌
         OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token);
         if (accessTokenDO == null) {
             return;
         }
         // 删除成功,则记录登出日志
-        createLogoutLog(accessTokenDO.getUserId());
+        createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType);
     }
 
-    private void createLogoutLog(Long userId) {
+    private void createLogoutLog(Long userId, Integer userType, Integer logType) {
         LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
-        reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType());
+        reqDTO.setLogType(logType);
         reqDTO.setTraceId(TracerUtils.getTraceId());
         reqDTO.setUserId(userId);
-        reqDTO.setUsername(getUsername(userId));
-        reqDTO.setUserType(getUserType().getValue());
+        reqDTO.setUserType(userType);
+        if (ObjectUtil.notEqual(getUserType(), userType)) {
+            reqDTO.setUsername(getUsername(userId));
+        } else {
+            reqDTO.setUsername(memberService.getMemberUserMobile(userId));
+        }
         reqDTO.setUserAgent(ServletUtils.getUserAgent());
         reqDTO.setUserIp(ServletUtils.getClientIP());
         reqDTO.setResult(LoginResultEnum.SUCCESS.getResult());

+ 10 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java

@@ -1,5 +1,7 @@
 package cn.iocoder.yudao.module.system.service.auth;
 
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
 
 /**
@@ -64,4 +66,12 @@ public interface OAuth2TokenService {
      */
     OAuth2AccessTokenDO removeAccessToken(String accessToken);
 
+    /**
+     * 获得访问令牌分页
+     *
+     * @param reqVO 请求
+     * @return 访问令牌分页
+     */
+    PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO);
+
 }

+ 7 - 0
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;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
@@ -125,6 +127,11 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
         return accessTokenDO;
     }
 
+    @Override
+    public PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) {
+        return oauth2AccessTokenMapper.selectPage(reqVO);
+    }
+
     private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
         OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
                 .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId())

+ 2 - 2
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 {
         when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO);
 
         // 调用
-        authService.logout(token);
+        authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
         // 校验调用参数
         verify(loginLogService).createLoginLog(argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
                     && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
@@ -234,7 +234,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
         String token = randomString();
 
         // 调用
-        authService.logout(token);
+        authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
         // 校验调用参数
         verify(loginLogService, never()).createLoginLog(any());
     }

+ 5 - 5
yudao-server/pom.xml

@@ -21,11 +21,11 @@
     <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
 
     <dependencies>
-<!--        <dependency>-->
-<!--            <groupId>cn.iocoder.boot</groupId>-->
-<!--            <artifactId>yudao-module-member-biz</artifactId>-->
-<!--            <version>${revision}</version>-->
-<!--        </dependency>-->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-member-biz</artifactId>
+            <version>${revision}</version>
+        </dependency>
         <dependency>
             <groupId>cn.iocoder.boot</groupId>
             <artifactId>yudao-module-system-biz</artifactId>

+ 0 - 2
yudao-server/src/main/resources/application-dev.yaml

@@ -173,8 +173,6 @@ wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-sta
 yudao:
   security:
     token-header: Authorization
-    token-timeout: 1d
-    session-timeout: 30m
     mock-enable: true
     mock-secret: test
   xss:

+ 0 - 2
yudao-server/src/main/resources/application-local.yaml

@@ -193,8 +193,6 @@ yudao:
     enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试
   security:
     token-header: Authorization
-    token-timeout: 1d
-    session-timeout: 1d
     mock-enable: true
     mock-secret: test
   xss:

+ 18 - 0
yudao-ui-admin/src/api/system/oauth2/oauth2Token.js

@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// 获得访问令牌分页
+export function getAccessTokenPage(query) {
+  return request({
+    url: '/system/oauth2-token/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 删除访问令牌
+export function deleteAccessToken(accessToken) {
+  return request({
+    url: '/system/oauth2-token/delete?accessToken=' + accessToken,
+    method: 'delete'
+  })
+}

+ 0 - 18
yudao-ui-admin/src/api/system/session.js

@@ -1,18 +0,0 @@
-import request from '@/utils/request'
-
-// 查询在线用户列表
-export function list(query) {
-  return request({
-    url: '/system/user-session/page',
-    method: 'get',
-    params: query
-  })
-}
-
-// 强退用户
-export function forceLogout(tokenId) {
-  return request({
-    url: '/system/user-session/delete?id=' + tokenId,
-    method: 'delete'
-  })
-}

+ 29 - 18
yudao-ui-admin/src/views/system/session/index.vue → yudao-ui-admin/src/views/system/oauth2/token/index.vue

@@ -3,11 +3,14 @@
     <doc-alert title="用户体系" url="https://doc.iocoder.cn/user-center/" />
     <!-- 搜索工作栏 -->
     <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
-      <el-form-item label="登录地址" prop="userIp">
-        <el-input v-model="queryParams.userIp" placeholder="请输入登录地址" clearable @keyup.enter.native="handleQuery"/>
+      <el-form-item label="用户编号" prop="userId">
+        <el-input v-model="queryParams.userId" placeholder="请输入用户编号" clearable @keyup.enter.native="handleQuery"/>
       </el-form-item>
-      <el-form-item label="用户名称" prop="username">
-        <el-input v-model="queryParams.username" placeholder="请输入用户名称" clearable @keyup.enter.native="handleQuery"/>
+      <el-form-item label="用户类型" prop="userType">
+        <el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable>
+          <el-option v-for="dict in this.getDictDatas(DICT_TYPE.USER_TYPE)"
+                     :key="dict.value" :label="dict.label" :value="dict.value"/>
+        </el-select>
       </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
@@ -16,20 +19,28 @@
 
     </el-form>
     <el-table v-loading="loading" :data="list" style="width: 100%;">
-      <el-table-column label="会话编号" align="center" prop="id" width="300" />
-      <el-table-column label="登录名称" align="center" prop="username" width="100" />
-      <el-table-column label="部门名称" align="center" prop="deptName" width="100" />
-      <el-table-column label="登录地址" align="center" prop="userIp" width="100" />
-      <el-table-column label="userAgent" align="center" prop="userAgent" :show-overflow-tooltip="true" />
-      <el-table-column label="登录时间" align="center" prop="createTime" width="180">
+      <el-table-column label="访问令牌" align="center" prop="accessToken" width="300" />
+      <el-table-column label="刷新令牌" align="center" prop="refreshToken" width="300" />
+      <el-table-column label="用户编号" align="center" prop="userId" />
+      <el-table-column label="用户类型" align="center" prop="userType" width="100">
+        <template slot-scope="scope">
+          <dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
         <template slot-scope="scope">
           <span>{{ parseTime(scope.row.createTime) }}</span>
         </template>
       </el-table-column>
+      <el-table-column label="过期时间" align="center" prop="expiresTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.expiresTime) }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button size="mini" type="text" icon="el-icon-delete" @click="handleForceLogout(scope.row)"
-            v-hasPermi="['system:user-session:delete']">强退</el-button>
+            v-hasPermi="['system:oauth2-token:delete']">强退</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -40,10 +51,10 @@
 </template>
 
 <script>
-import { list, forceLogout } from "@/api/system/session";
+import { getAccessTokenPage, deleteAccessToken } from "@/api/system/oauth2/oauth2Token";
 
 export default {
-  name: "Online",
+  name: "OAuth2Token",
   data() {
     return {
       // 遮罩层
@@ -56,8 +67,8 @@ export default {
       queryParams: {
         pageNo: 1,
         pageSize: 10,
-        userIp: undefined,
-        username: undefined
+        userId: undefined,
+        userType: undefined
       }
     };
   },
@@ -68,7 +79,7 @@ export default {
     /** 查询登录日志列表 */
     getList() {
       this.loading = true;
-      list(this.queryParams).then(response => {
+      getAccessTokenPage(this.queryParams).then(response => {
         this.list = response.data.list;
         this.total = response.data.total;
         this.loading = false;
@@ -86,8 +97,8 @@ export default {
     },
     /** 强退按钮操作 */
     handleForceLogout(row) {
-      this.$modal.confirm('是否确认强退名称为"' + row.username + '"的数据项?').then(function() {
-          return forceLogout(row.id);
+      this.$modal.confirm('是否确认强退令牌为"' + row.accessToken + '"的数据项?').then(function() {
+          return deleteAccessToken(row.accessToken);
         }).then(() => {
           this.getList();
           this.$modal.msgSuccess("强退成功");