mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 04:08:43 +08:00 
			
		
		
		
	实现管理后台登出时,删除 oauth 令牌
This commit is contained in:
		@@ -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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -59,8 +59,8 @@ public interface OAuth2TokenService {
 | 
			
		||||
     * 参考 DefaultTokenServices 的 revokeToken 方法
 | 
			
		||||
     *
 | 
			
		||||
     * @param accessToken 刷新令牌
 | 
			
		||||
     * @return 是否移除到
 | 
			
		||||
     * @return 访问令牌的信息
 | 
			
		||||
     */
 | 
			
		||||
    boolean removeAccessToken(String accessToken);
 | 
			
		||||
    OAuth2AccessTokenDO removeAccessToken(String accessToken);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -124,20 +120,6 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
 | 
			
		||||
//        OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
 | 
			
		||||
//        // 返回访问令牌
 | 
			
		||||
//        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) {
 | 
			
		||||
@@ -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,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 接口
 | 
			
		||||
@@ -20,31 +19,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
 | 
			
		||||
     *
 | 
			
		||||
@@ -59,19 +33,4 @@ public interface UserSessionService {
 | 
			
		||||
     */
 | 
			
		||||
    void deleteUserSession(Long id);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得 Token 对应的在线用户
 | 
			
		||||
     *
 | 
			
		||||
     * @param token 令牌
 | 
			
		||||
     * @return 在线用户
 | 
			
		||||
     */
 | 
			
		||||
    LoginUser getLoginUser(String token);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得 Session 超时时间,单位:毫秒
 | 
			
		||||
     *
 | 
			
		||||
     * @return 超时时间
 | 
			
		||||
     */
 | 
			
		||||
    Long getSessionTimeoutMillis();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
@@ -100,28 +72,6 @@ public class UserSessionServiceImpl implements UserSessionService {
 | 
			
		||||
        loginLogService.createLoginLog(reqDTO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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 缓存
 | 
			
		||||
@@ -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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user