mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 18:28:43 +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
	 YunaiV
					YunaiV