Browse Source

实现管理后台登出时,删除 oauth 令牌

YunaiV 2 years ago
parent
commit
5ea9cc3cd7
13 changed files with 90 additions and 395 deletions
  1. 24 13
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
  2. 0 1
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java
  3. 4 6
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
  4. 4 4
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java
  5. 0 32
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/auth/UserSessionTimeoutJob.java
  6. 4 10
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
  7. 27 50
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
  8. 2 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
  9. 11 42
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
  10. 1 42
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionService.java
  11. 0 69
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImpl.java
  12. 9 9
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java
  13. 4 115
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java

+ 24 - 13
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.security.core.filter;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
@@ -44,23 +45,14 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
         if (StrUtil.isNotEmpty(token)) {
             Integer userType = WebFrameworkUtils.getLoginUserType(request);
             try {
-                // 验证 token 有效性
-                OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token);
-                if (accessToken != null && ObjectUtil.notEqual(accessToken.getUserType(), userType)) { // 用户类型不匹配,无权限
-                    throw new AccessDeniedException("错误的用户类型");
-                }
-                LoginUser loginUser = null;
-                if (accessToken != null) { // 如果不为空,说明认证通过,则转换成登录用户
-                    loginUser = new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
-                            .setTenantId(accessToken.getTenantId());
-                }
-
-                // 模拟 Login 功能,方便日常开发调试
+                // 1.1 基于 token 构建登录用户
+                LoginUser loginUser = buildLoginUserByToken(token, userType);
+                // 1.2 模拟 Login 功能,方便日常开发调试
                 if (loginUser == null) {
                     loginUser = mockLoginUser(request, token, userType);
                 }
 
-                // 设置当前用户
+                // 2. 设置当前用户
                 if (loginUser != null) {
                     SecurityFrameworkUtils.setLoginUser(loginUser, request);
                 }
@@ -75,6 +67,25 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
         chain.doFilter(request, response);
     }
 
+    private LoginUser buildLoginUserByToken(String token, Integer userType) {
+        try {
+            OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token);
+            if (accessToken == null) {
+                return null;
+            }
+            // 用户类型不匹配,无权限
+            if (ObjectUtil.notEqual(accessToken.getUserType(), userType)) {
+                throw new AccessDeniedException("错误的用户类型");
+            }
+            // 构建登录用户
+            return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
+                    .setTenantId(accessToken.getTenantId());
+        } catch (ServiceException serviceException) {
+            // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
+            return null;
+        }
+    }
+
     /**
      * 模拟登录用户,方便日常开发调试
      *

+ 0 - 1
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java

@@ -16,7 +16,6 @@ public enum LoginLogTypeEnum {
     LOGIN_SMS(104), // 使用短信登陆
 
     LOGOUT_SELF(200),  // 自己主动登出
-    LOGOUT_TIMEOUT(201), // 超时登出
     LOGOUT_DELETE(202), // 强制退出
     ;
 

+ 4 - 6
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java

@@ -33,8 +33,6 @@ import java.util.List;
 import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
-import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent;
 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 import static java.util.Collections.singleton;
 
@@ -63,7 +61,7 @@ public class AuthController {
     @ApiOperation("使用账号密码登录")
     @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
     public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
-        String token = authService.login(reqVO, getClientIP(), getUserAgent());
+        String token = authService.login(reqVO);
         return success(AuthLoginRespVO.builder().token(token).build());
     }
 
@@ -116,7 +114,7 @@ public class AuthController {
     @ApiOperation("使用短信验证码登录")
     @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
     public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) {
-        String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
+        String token = authService.smsLogin(reqVO);
         // 返回结果
         return success(AuthLoginRespVO.builder().token(token).build());
     }
@@ -146,7 +144,7 @@ public class AuthController {
     @ApiOperation("社交快捷登录,使用 code 授权码")
     @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
     public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialQuickLoginReqVO reqVO) {
-        String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent());
+        String token = authService.socialQuickLogin(reqVO);
         return success(AuthLoginRespVO.builder().token(token).build());
     }
 
@@ -154,7 +152,7 @@ public class AuthController {
     @ApiOperation("社交绑定登录,使用 code 授权码 + 账号密码")
     @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
     public CommonResult<AuthLoginRespVO> socialBindLogin(@RequestBody @Valid AuthSocialBindLoginReqVO reqVO) {
-        String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent());
+        String token = authService.socialBindLogin(reqVO);
         return success(AuthLoginRespVO.builder().token(token).build());
     }
 

+ 4 - 4
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java

@@ -1,16 +1,16 @@
 package cn.iocoder.yudao.module.system.dal.mysql.auth;
 
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
 public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
 
-    default int deleteByUserIdAndUserType(Integer userId, Integer userType) {
-        return delete(new QueryWrapper<OAuth2RefreshTokenDO>()
-                .eq("user_id", userId).eq("user_type", userType));
+    default int deleteByRefreshToken(String refreshToken) {
+        return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>()
+                .eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken));
     }
 
 }

+ 0 - 32
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/auth/UserSessionTimeoutJob.java

@@ -1,32 +0,0 @@
-package cn.iocoder.yudao.module.system.job.auth;
-
-import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
-import cn.iocoder.yudao.module.system.service.auth.UserSessionService;
-import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.Resource;
-
-/**
- * 用户 Session 超时 Job
- *
- * @author 願
- */
-@Component
-@TenantJob
-@Slf4j
-public class UserSessionTimeoutJob implements JobHandler {
-
-    @Resource
-    private UserSessionService userSessionService;
-
-    @Override
-    public String execute(String param) throws Exception {
-        // 执行过期
-        Long timeoutCount = userSessionService.deleteTimeoutSession();
-        // 返回结果,记录每次的超时数量
-        return String.format("移除在线会话数量为 %s 个", timeoutCount);
-    }
-
-}

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

@@ -17,11 +17,9 @@ public interface AdminAuthService {
      * 账号登录
      *
      * @param reqVO 登录信息
-     * @param userIp 用户 IP
-     * @param userAgent 用户 UA
      * @return 身份令牌,使用 JWT 方式
      */
-    String login(@Valid AuthLoginReqVO reqVO, String userIp, String userAgent);
+    String login(@Valid AuthLoginReqVO reqVO);
 
     /**
      * 基于 token 退出登录
@@ -41,21 +39,17 @@ public interface AdminAuthService {
      * 短信登录
      *
      * @param reqVO 登录信息
-     * @param userIp 用户 IP
-     * @param userAgent 用户 UA
      * @return 身份令牌,使用 JWT 方式
      */
-    String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) ;
+    String smsLogin(AuthSmsLoginReqVO reqVO) ;
 
     /**
      * 社交快捷登录,使用 code 授权码
      *
      * @param reqVO 登录信息
-     * @param userIp 用户 IP
-     * @param userAgent 用户 UA
      * @return 身份令牌,使用 JWT 方式
      */
-    String socialQuickLogin(@Valid AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent);
+    String socialQuickLogin(@Valid AuthSocialQuickLoginReqVO reqVO);
 
     /**
      * 社交绑定登录,使用 code 授权码 + 账号密码
@@ -65,6 +59,6 @@ public interface AdminAuthService {
      * @param userAgent 用户 UA
      * @return 身份令牌,使用 JWT 方式
      */
-    String socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent);
+    String socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO);
 
 }

+ 27 - 50
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java

@@ -6,11 +6,11 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
-import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*;
 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
+import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
 import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
@@ -47,8 +47,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
     @Resource
     private LoginLogService loginLogService;
     @Resource
-    private UserSessionService userSessionService;
-    @Resource
     private OAuth2TokenService oauth2TokenService;
     @Resource
     private SocialUserService socialUserService;
@@ -60,16 +58,15 @@ public class AdminAuthServiceImpl implements AdminAuthService {
     private SmsCodeApi smsCodeApi;
 
     @Override
-    public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) {
+    public String login(AuthLoginReqVO reqVO) {
         // 判断验证码是否正确
         verifyCaptcha(reqVO);
 
         // 使用账号密码,进行登录
-        LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword());
+        AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword());
 
-        // 缓存登陆用户到 Redis 中,返回 Token 令牌
-        return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(),
-                LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent);
+        // 创建 Token 令牌,记录登录日志
+        return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME);
     }
 
     @Override
@@ -83,9 +80,9 @@ public class AdminAuthServiceImpl implements AdminAuthService {
     }
 
     @Override
-    public String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) {
+    public String smsLogin(AuthSmsLoginReqVO reqVO) {
         // 校验验证码
-        smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), userIp));
+        smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP()));
 
         // 获得用户信息
         AdminUserDO user = userService.getUserByMobile(reqVO.getMobile());
@@ -93,12 +90,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
             throw exception(USER_NOT_EXISTS);
         }
 
-        // 创建 LoginUser 对象
-        LoginUser loginUser = buildLoginUser(user);
-
         // 缓存登陆用户到 Redis 中,返回 sessionId 编号
-        return createUserSessionAfterLoginSuccess(loginUser, reqVO.getMobile(),
-                LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent);
+        return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
     }
 
     @VisibleForTesting
@@ -128,7 +121,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
     }
 
     @VisibleForTesting
-    LoginUser login0(String username, String password) {
+    AdminUserDO login0(String username, String password) {
         final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME;
         // 校验账号是否存在
         AdminUserDO user = userService.getUserByUsername(username);
@@ -145,9 +138,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
             createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED);
             throw exception(AUTH_LOGIN_USER_DISABLED);
         }
-
-        // 构建 User 对象
-        return buildLoginUser(user);
+        return user;
     }
 
     private void createLoginLog(Long userId, String username,
@@ -170,7 +161,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
     }
 
     @Override
-    public String socialQuickLogin(AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) {
+    public String socialQuickLogin(AuthSocialQuickLoginReqVO reqVO) {
         // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
         Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
                 reqVO.getCode(), reqVO.getState());
@@ -178,56 +169,46 @@ public class AdminAuthServiceImpl implements AdminAuthService {
             throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
         }
 
-        // 自动登录
+        // 获得用户
         AdminUserDO user = userService.getUser(userId);
         if (user == null) {
             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.getId(), null, LoginLogTypeEnum.LOGIN_SOCIAL);
     }
 
     @Override
-    public String socialBindLogin(AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) {
+    public String socialBindLogin(AuthSocialBindLoginReqVO reqVO) {
         // 使用账号密码,进行登录。
-        LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword());
+        AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword());
 
         // 绑定社交用户
-        socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
+        socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(user.getId(), getUserType().getValue(), reqVO));
 
-        // 缓存登录用户到 Redis 中,返回 Token 令牌
-        return createUserSessionAfterLoginSuccess(loginUser, reqVO.getUsername(),
-                LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
+        // 创建 Token 令牌,记录登录日志
+        return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
     }
 
-    private String createUserSessionAfterLoginSuccess(LoginUser loginUser, String username,
-                                                      LoginLogTypeEnum logType, String userIp, String userAgent) {
+    private String createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
         // 插入登陆日志
-        createLoginLog(loginUser.getId(), username, logType, LoginResultEnum.SUCCESS);
+        createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
         // 创建访问令牌
-        // TODO userIp、userAgent
         // TODO clientId
-        return oauth2TokenService.createAccessToken(loginUser.getId(), getUserType().getValue(), 1L)
+        return oauth2TokenService.createAccessToken(userId, getUserType().getValue(), 1L)
                 .getAccessToken();
-//        return userSessionService.createUserSession(loginUser, userIp, userAgent);
     }
 
     @Override
     public void logout(String token) {
-        // 查询用户信息
-        LoginUser loginUser = userSessionService.getLoginUser(token);
-        if (loginUser == null) {
+        // 删除访问令牌
+        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token);
+        if (accessTokenDO == null) {
             return;
         }
-        // 删除 session
-        userSessionService.deleteUserSession(token);
-        // 记录登出日志
-        createLogoutLog(loginUser.getId());
+        // 删除成功,则记录登出日志
+        createLogoutLog(accessTokenDO.getUserId());
     }
 
     private void createLogoutLog(Long userId) {
@@ -243,10 +224,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
         loginLogService.createLoginLog(reqDTO);
     }
 
-    private LoginUser buildLoginUser(AdminUserDO user) {
-        return AuthConvert.INSTANCE.convert(user).setUserType(getUserType().getValue());
-    }
-
     private String getUsername(Long userId) {
         if (userId == null) {
             return null;

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

@@ -59,8 +59,8 @@ public interface OAuth2TokenService {
      * 参考 DefaultTokenServices 的 revokeToken 方法
      *
      * @param accessToken 刷新令牌
-     * @return 是否移除到
+     * @return 访问令牌的信息
      */
-    boolean removeAccessToken(String accessToken);
+    OAuth2AccessTokenDO removeAccessToken(String accessToken);
 
 }

+ 11 - 42
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java

@@ -85,23 +85,19 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
     }
 
     @Override
-    public boolean removeAccessToken(String accessToken) {
-        return false;
+    public OAuth2AccessTokenDO removeAccessToken(String accessToken) {
+        // 删除访问令牌
+        OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken);
+        if (accessTokenDO == null) {
+            return null;
+        }
+        oauth2AccessTokenMapper.deleteById(accessTokenDO.getId());
+        oauth2AccessTokenRedisDAO.delete(accessToken);
+        // 删除刷新令牌
+        oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken());
+        return accessTokenDO;
     }
 
-//    @Override
-//    @Transactional
-//    public OAuth2AccessTokenRespDTO checkAccessToken(String accessToken) {
-//        OAuth2AccessTokenDO accessTokenDO = this.getOAuth2AccessToken(accessToken);
-//        if (accessTokenDO == null) { // 不存在
-//            throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_NOT_FOUND);
-//        }
-//        if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
-//            throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED);
-//        }
-//        // 返回访问令牌
-//        return OAuth2Convert.INSTANCE.convert(accessTokenDO);
-//    }
 
 //    @Override
 //    @Transactional
@@ -125,20 +121,6 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
 //        // 返回访问令牌
 //        return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
 //    }
-//
-//    @Override
-//    @Transactional
-//    public void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO) {
-//        // 删除 Access Token
-//        OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByUserIdAndUserType(
-//                removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
-//        if (accessTokenDO != null) {
-//            this.deleteOAuth2AccessToken(accessTokenDO.getId());
-//        }
-//
-//        // 删除 Refresh Token
-//        oauth2RefreshTokenMapper.deleteByUserIdAndUserType(removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
-//    }
 
     private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
         OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
@@ -158,19 +140,6 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
         return refreshToken;
     }
 
-
-//    /**
-//     * 删除 accessToken 的 MySQL 与 Redis 的数据
-//     *
-//     * @param accessToken 访问令牌
-//     */
-//    private void deleteOAuth2AccessToken(String accessToken) {
-//        // 删除 MySQL
-//        oauth2AccessTokenMapper.deleteById(accessToken);
-//        // 删除 Redis
-//        oauth2AccessTokenRedisDAO.delete(accessToken);
-//    }
-//
     private static String generateAccessToken() {
         return IdUtil.fastSimpleUUID();
     }

+ 1 - 42
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionService.java

@@ -1,9 +1,8 @@
 package cn.iocoder.yudao.module.system.service.auth;
 
-import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
 
 /**
  * 在线用户 Session Service 接口
@@ -21,31 +20,6 @@ public interface UserSessionService {
     PageResult<UserSessionDO> getUserSessionPage(UserSessionPageReqVO reqVO);
 
     /**
-     * 移除超时的在线用户
-     *
-     * @return {@link Long } 移出的超时用户数量
-     **/
-    long deleteTimeoutSession();
-
-    /**
-     * 创建在线用户 Session
-     *
-     * @param loginUser 登录用户
-     * @param userIp 用户 IP
-     * @param userAgent 用户 UA
-     * @return Token 令牌
-     */
-    String createUserSession(LoginUser loginUser, String userIp, String userAgent);
-
-    /**
-     * 刷新在线用户 Session 的更新时间
-     *
-     * @param token 令牌
-     * @param loginUser 登录用户
-     */
-    void refreshUserSession(String token, LoginUser loginUser);
-
-    /**
      * 删除在线用户 Session
      *
      * @param token token 令牌
@@ -59,19 +33,4 @@ public interface UserSessionService {
      */
     void deleteUserSession(Long id);
 
-    /**
-     * 获得 Token 对应的在线用户
-     *
-     * @param token 令牌
-     * @return 在线用户
-     */
-    LoginUser getLoginUser(String token);
-
-    /**
-     * 获得 Session 超时时间,单位:毫秒
-     *
-     * @return 超时时间
-     */
-    Long getSessionTimeoutMillis();
-
 }

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

@@ -1,12 +1,10 @@
 package cn.iocoder.yudao.module.system.service.auth;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
-import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO;
@@ -21,12 +19,9 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import java.time.Duration;
 import java.util.Collection;
-import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
 
 /**
  * 在线用户 Session Service 实现类
@@ -64,29 +59,6 @@ public class UserSessionServiceImpl implements UserSessionService {
         return userSessionMapper.selectPage(reqVO, userIds);
     }
 
-    @Override
-    public long deleteTimeoutSession() {
-        // 获取 db 里已经超时的用户列表
-        List<UserSessionDO> timeoutSessions = userSessionMapper.selectListBySessionTimoutLt();
-        if (CollUtil.isEmpty(timeoutSessions)) {
-            return 0L;
-        }
-
-        // 由于过期的用户一般不多,所以顺序遍历,进行清理
-        int count = 0;
-        for (UserSessionDO session : timeoutSessions) {
-            // 基于 Redis 二次判断,同时也保证 Redis Key 的立即过期,避免延迟导致浪费内存空间
-            if (loginUserRedisDAO.exists(session.getToken())) {
-                continue;
-            }
-            userSessionMapper.deleteById(session.getId());
-            // 记录退出日志
-            createLogoutLog(session, LoginLogTypeEnum.LOGOUT_TIMEOUT);
-            count++;
-        }
-        return count;
-    }
-
     private void createLogoutLog(UserSessionDO session, LoginLogTypeEnum type) {
         LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
         reqDTO.setLogType(type.getType());
@@ -101,28 +73,6 @@ public class UserSessionServiceImpl implements UserSessionService {
     }
 
     @Override
-    public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
-        // 生成 Session 编号
-        String token = generateToken();
-        // 写入 Redis 缓存
-        loginUserRedisDAO.set(token, loginUser);
-        // 写入 DB 中
-        UserSessionDO userSession = UserSessionDO.builder().token(token)
-                .userId(loginUser.getId()).userType(loginUser.getUserType())
-                .userIp(userIp).userAgent(userAgent).username("")
-                .sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis())))
-                .build();
-        userSessionMapper.insert(userSession);
-        // 返回 Token 令牌
-        return token;
-    }
-
-    @Override
-    public void refreshUserSession(String token, LoginUser loginUser) {
-
-    }
-
-    @Override
     public void deleteUserSession(String token) {
         // 删除 Redis 缓存
         loginUserRedisDAO.delete(token);
@@ -145,23 +95,4 @@ public class UserSessionServiceImpl implements UserSessionService {
         createLogoutLog(session, LoginLogTypeEnum.LOGOUT_DELETE);
     }
 
-    @Override
-    public LoginUser getLoginUser(String token) {
-        return loginUserRedisDAO.get(token);
-    }
-
-    @Override
-    public Long getSessionTimeoutMillis() {
-        return securityProperties.getSessionTimeout().toMillis();
-    }
-
-    /**
-     * 生成 Token 令牌,目前采用 UUID 算法
-     *
-     * @return Session 编号
-     */
-    private static String generateToken() {
-        return IdUtil.fastSimpleUUID();
-    }
-
 }

+ 9 - 9
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java

@@ -70,7 +70,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
         when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
 
         // 调用
-        LoginUser loginUser = authService.login0(username, password);
+        AdminUserDO loginUser = authService.login0(username, password);
         // 校验
         assertPojoEquals(user, loginUser);
     }
@@ -182,8 +182,6 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
     @Test
     public void testLogin_success() {
         // 准备参数
-        String userIp = randomString();
-        String userAgent = randomString();
         AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o ->
                 o.setUsername("test_username").setPassword("test_password"));
 
@@ -197,13 +195,14 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
         when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true);
         // mock 缓存登录用户到 Redis
         String token = randomString();
-        when(userSessionService.createUserSession(argThat(argument -> {
-            AssertUtils.assertPojoEquals(user, argument);
-            return true;
-        }), eq(userIp), eq(userAgent))).thenReturn(token);
+//        when(userSessionService.createUserSession(argThat(argument -> {
+//            AssertUtils.assertPojoEquals(user, argument);
+//            return true;
+//        }), eq(userIp), eq(userAgent))).thenReturn(token);
+        // TODO 芋艿:oauth2
 
         // 调用, 并断言异常
-        String result = authService.login(reqVO, userIp, userAgent);
+        String result = authService.login(reqVO);
         assertEquals(token, result);
         // 校验调用参数
         verify(loginLogService).createLoginLog(
@@ -219,7 +218,8 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
         String token = randomString();
         LoginUser loginUser = randomPojo(LoginUser.class);
         // mock
-        when(userSessionService.getLoginUser(token)).thenReturn(loginUser);
+//        when(userSessionService.getLoginUser(token)).thenReturn(loginUser);
+        // TODO @芋艿:oauth2
         // 调用
         authService.logout(token);
         // 校验调用参数

+ 4 - 115
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java

@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.service.auth;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.date.DateUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
@@ -14,8 +13,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
 import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper;
 import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO;
 import cn.iocoder.yudao.module.system.enums.common.SexEnum;
-import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
-import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
 import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
 import org.junit.jupiter.api.BeforeEach;
@@ -25,17 +22,15 @@ import org.springframework.context.annotation.Import;
 
 import javax.annotation.Resource;
 import java.time.Duration;
-import java.util.Calendar;
 
 import static cn.hutool.core.util.RandomUtil.randomEle;
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
-import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
 import static java.util.Collections.singletonList;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.argThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 /**
@@ -101,112 +96,6 @@ public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest {
     }
 
     @Test
-    public void testClearSessionTimeout_none() {
-        // mock db 数据
-        UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> {
-            o.setUserType(randomEle(UserTypeEnum.values()).getValue());
-            o.setSessionTimeout(addTime(Duration.ofDays(1)));
-        });
-        userSessionMapper.insert(userSession);
-
-        // 调用
-        long count = userSessionService.deleteTimeoutSession();
-        // 断言
-        assertEquals(0, count);
-        assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除
-    }
-
-    @Test // Redis 还存在的情况
-    public void testClearSessionTimeout_exists() {
-        // mock db 数据
-        UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> {
-            o.setUserType(randomEle(UserTypeEnum.values()).getValue());
-            o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1));
-        });
-        userSessionMapper.insert(userSession);
-        // mock redis 数据
-        loginUserRedisDAO.set(userSession.getToken(), new LoginUser());
-
-        // 调用
-        long count = userSessionService.deleteTimeoutSession();
-        // 断言
-        assertEquals(0, count);
-        assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除
-    }
-
-    @Test
-    public void testClearSessionTimeout_success() {
-        // mock db 数据
-        UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> {
-            o.setUserType(randomEle(UserTypeEnum.values()).getValue());
-            o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1));
-        });
-        userSessionMapper.insert(userSession);
-
-        // 清空超时数据
-        long count = userSessionService.deleteTimeoutSession();
-        // 校验
-        assertEquals(1, count);
-        assertNull(userSessionMapper.selectById(userSession.getId())); // 已删除
-        verify(loginLogService).createLoginLog(argThat(loginLog -> {
-            assertPojoEquals(userSession, loginLog);
-            assertEquals(LoginLogTypeEnum.LOGOUT_TIMEOUT.getType(), loginLog.getLogType());
-            assertEquals(LoginResultEnum.SUCCESS.getResult(), loginLog.getResult());
-            return true;
-        }));
-    }
-
-    @Test
-    public void testCreateUserSession_success() {
-        // 准备参数
-        String userIp = randomString();
-        String userAgent = randomString();
-        LoginUser loginUser = randomPojo(LoginUser.class, o -> {
-            o.setUserType(randomEle(UserTypeEnum.values()).getValue());
-            o.setTenantId(0L); // 租户设置为 0,因为暂未启用多租户组件
-        });
-
-        // 调用
-        String token = userSessionService.createUserSession(loginUser, userIp, userAgent);
-        // 校验 UserSessionDO 记录
-        UserSessionDO userSessionDO = userSessionMapper.selectOne(UserSessionDO::getToken, token);
-        assertPojoEquals(loginUser, userSessionDO, "id", "updateTime");
-        assertEquals(token, userSessionDO.getToken());
-        assertEquals(userIp, userSessionDO.getUserIp());
-        assertEquals(userAgent, userSessionDO.getUserAgent());
-        // 校验 LoginUser 缓存
-        LoginUser redisLoginUser = loginUserRedisDAO.get(token);
-        assertPojoEquals(loginUser, redisLoginUser, "username", "password");
-    }
-
-    @Test
-    public void testCreateRefreshUserSession() {
-        // 准备参数
-        String token = randomString();
-
-        // mock redis 数据
-        LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue()));
-        loginUserRedisDAO.set(token, loginUser);
-        // mock db 数据
-        UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> {
-            o.setUserType(randomEle(UserTypeEnum.values()).getValue());
-            o.setToken(token);
-        });
-        userSessionMapper.insert(userSession);
-
-        // 调用
-        userSessionService.refreshUserSession(token, loginUser);
-        // 校验 LoginUser 缓存
-        LoginUser redisLoginUser = loginUserRedisDAO.get(token);
-        assertPojoEquals(redisLoginUser, loginUser, "username", "password");
-        // 校验 UserSessionDO 记录
-        UserSessionDO updateDO = userSessionMapper.selectOne(UserSessionDO::getToken, token);
-//        assertEquals(updateDO.getUsername(), loginUser.getUsername());
-        assertNotNull(userSession.getUpdateTime());
-        assertNotNull(userSession.getSessionTimeout());
-    }
-
-    @Test
     public void testDeleteUserSession_Token() {
         // 准备参数
         String token = randomString();