mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 12:18:42 +08:00 
			
		
		
		
	【功能优化】SYSTEM:支持通过 refreshToken 认证,解决部分场景不方便刷新访问令牌场景
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
			
		||||
package cn.iocoder.yudao.module.system.dal.dataobject.oauth2;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 | 
			
		||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.KeySequence;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableField;
 | 
			
		||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
			
		||||
@@ -24,7 +24,7 @@ import java.util.List;
 | 
			
		||||
@Data
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class OAuth2RefreshTokenDO extends BaseDO {
 | 
			
		||||
public class OAuth2RefreshTokenDO extends TenantBaseDO {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 编号,数据库字典
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.oauth2;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 | 
			
		||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
 | 
			
		||||
@@ -13,6 +14,7 @@ public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshToken
 | 
			
		||||
                .eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @TenantIgnore // 获取 token 的时候,需要忽略租户编号。原因是:一些场景下,可能不会传递 tenant-id 请求头,例如说文件上传、积木报表等等
 | 
			
		||||
    default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) {
 | 
			
		||||
        return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,10 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 | 
			
		||||
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.common.util.object.BeanUtils;
 | 
			
		||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
 | 
			
		||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 | 
			
		||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 | 
			
		||||
import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
 | 
			
		||||
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
 | 
			
		||||
@@ -105,10 +107,21 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
 | 
			
		||||
            return accessTokenDO;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 获取不到,从 MySQL 中获取
 | 
			
		||||
        // 获取不到,从 MySQL 中获取访问令牌
 | 
			
		||||
        accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken);
 | 
			
		||||
        if (accessTokenDO != null && DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
 | 
			
		||||
            accessTokenDO = null;
 | 
			
		||||
        }
 | 
			
		||||
        // 特殊:从 MySQL 中获取刷新令牌。原因:解决部分场景不方便刷新访问令牌场景
 | 
			
		||||
        // 例如说,积木报表只允许传递 token,不允许传递 refresh_token,导致无法刷新访问令牌
 | 
			
		||||
        // 再例如说,前端 WebSocket 的 token 直接跟在 url 上,无法传递 refresh_token
 | 
			
		||||
        OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(accessToken);
 | 
			
		||||
        if (refreshTokenDO != null && !DateUtils.isExpired(refreshTokenDO.getExpiresTime())) {
 | 
			
		||||
            accessTokenDO = convertToAccessToken(refreshTokenDO);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 如果在 MySQL 存在,则往 Redis 中写入
 | 
			
		||||
        if (accessTokenDO != null && !DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
 | 
			
		||||
        if (accessTokenDO != null) {
 | 
			
		||||
            oauth2AccessTokenRedisDAO.set(accessTokenDO);
 | 
			
		||||
        }
 | 
			
		||||
        return accessTokenDO;
 | 
			
		||||
@@ -169,6 +182,14 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
 | 
			
		||||
        return refreshToken;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private OAuth2AccessTokenDO convertToAccessToken(OAuth2RefreshTokenDO refreshTokenDO) {
 | 
			
		||||
        OAuth2AccessTokenDO accessTokenDO = BeanUtils.toBean(refreshTokenDO, OAuth2AccessTokenDO.class)
 | 
			
		||||
                .setAccessToken(refreshTokenDO.getRefreshToken());
 | 
			
		||||
        TenantUtils.execute(refreshTokenDO.getTenantId(),
 | 
			
		||||
                        () -> accessTokenDO.setUserInfo(buildUserInfo(refreshTokenDO.getUserId(), refreshTokenDO.getUserType())));
 | 
			
		||||
        return accessTokenDO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 加载用户信息,方便 {@link cn.iocoder.yudao.framework.security.core.LoginUser} 获取到昵称、部门等信息
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -231,6 +231,22 @@ public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest {
 | 
			
		||||
                new ErrorCode(401, "访问令牌已过期"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testCheckAccessToken_refreshToken() {
 | 
			
		||||
        // mock 数据(访问令牌)
 | 
			
		||||
        OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class)
 | 
			
		||||
                .setExpiresTime(LocalDateTime.now().plusDays(1));
 | 
			
		||||
        oauth2RefreshTokenMapper.insert(refreshTokenDO);
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        String accessToken = refreshTokenDO.getRefreshToken();
 | 
			
		||||
 | 
			
		||||
        // 调研,并断言
 | 
			
		||||
        OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken);
 | 
			
		||||
        // 断言
 | 
			
		||||
        assertPojoEquals(refreshTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted",
 | 
			
		||||
                "creator", "updater");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testCheckAccessToken_success() {
 | 
			
		||||
        // mock 数据(访问令牌)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user