【功能优化】SYSTEM:支持通过 refreshToken 认证,解决部分场景不方便刷新访问令牌场景

This commit is contained in:
YunaiV 2024-10-02 14:08:10 +08:00
parent d50844246c
commit c2937bd087
4 changed files with 43 additions and 4 deletions

View File

@ -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 {
/**
* 编号数据库字典

View File

@ -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);
}

View File

@ -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} 获取到昵称部门等信息
*

View File

@ -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 数据访问令牌