feat: 收口知识库分享链路
- 新增 shareKey 单参数 URL 分享页与失效页 - 新增知识库分享后端鉴权、审计与迁移脚本 - 在访问令牌中增加知识库分享授权入口
This commit is contained in:
@@ -4,6 +4,8 @@ import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.system.entity.SysApiKey;
|
||||
import tech.easyflow.system.entity.SysApiKeyResourceMapping;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* apikey-请求接口表 服务层。
|
||||
*
|
||||
@@ -13,4 +15,21 @@ import tech.easyflow.system.entity.SysApiKeyResourceMapping;
|
||||
public interface SysApiKeyResourceMappingService extends IService<SysApiKeyResourceMapping> {
|
||||
|
||||
void authInterface(SysApiKey entity);
|
||||
|
||||
/**
|
||||
* 移除指定 apiKey 下某个资源目标的 scope 授权。
|
||||
*
|
||||
* @param apiKeyId apiKey ID
|
||||
* @param resourceType 资源类型
|
||||
* @param resourceTargetId 资源目标 ID
|
||||
*/
|
||||
void removeScopedMappings(BigInteger apiKeyId, String resourceType, BigInteger resourceTargetId);
|
||||
|
||||
/**
|
||||
* 按资源类型移除指定 apiKey 下的全部资源级授权。
|
||||
*
|
||||
* @param apiKeyId apiKey ID
|
||||
* @param resourceType 资源类型
|
||||
*/
|
||||
void removeScopedMappings(BigInteger apiKeyId, String resourceType);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package tech.easyflow.system.service;
|
||||
import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.system.entity.SysApiKey;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 服务层。
|
||||
*
|
||||
@@ -15,4 +17,15 @@ public interface SysApiKeyService extends IService<SysApiKey> {
|
||||
|
||||
SysApiKey getSysApiKey(String apiKey);
|
||||
|
||||
/**
|
||||
* 校验资源级作用域权限。
|
||||
*
|
||||
* @param apiKeyId apiKey 记录 ID
|
||||
* @param requestURI 请求 URI
|
||||
* @param resourceType 资源类型
|
||||
* @param resourceTargetId 资源目标 ID
|
||||
* @param actionScope 动作范围
|
||||
*/
|
||||
void checkResourceScope(BigInteger apiKeyId, String requestURI, String resourceType, BigInteger resourceTargetId, String actionScope);
|
||||
|
||||
}
|
||||
|
||||
@@ -46,7 +46,10 @@ public class SysApiKeyResourceMappingServiceImpl extends ServiceImpl<SysApiKeyRe
|
||||
return;
|
||||
}
|
||||
redisLockExecutor.executeWithLock(API_KEY_MAPPING_LOCK_PREFIX + apiKeyId, LOCK_WAIT_TIMEOUT, LOCK_LEASE_TIMEOUT, () -> {
|
||||
this.remove(QueryWrapper.create().eq(SysApiKeyResourceMapping::getApiKeyId, apiKeyId));
|
||||
QueryWrapper removeWrapper = QueryWrapper.create()
|
||||
.eq(SysApiKeyResourceMapping::getApiKeyId, apiKeyId)
|
||||
.isNull(SysApiKeyResourceMapping::getResourceType);
|
||||
this.remove(removeWrapper);
|
||||
if (entity.getPermissionIds() == null || entity.getPermissionIds().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -66,4 +69,29 @@ public class SysApiKeyResourceMappingServiceImpl extends ServiceImpl<SysApiKeyRe
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void removeScopedMappings(BigInteger apiKeyId, String resourceType, BigInteger resourceTargetId) {
|
||||
if (apiKeyId == null || resourceTargetId == null || resourceType == null || resourceType.isBlank()) {
|
||||
return;
|
||||
}
|
||||
QueryWrapper wrapper = QueryWrapper.create()
|
||||
.eq(SysApiKeyResourceMapping::getApiKeyId, apiKeyId)
|
||||
.eq(SysApiKeyResourceMapping::getResourceType, resourceType)
|
||||
.eq(SysApiKeyResourceMapping::getResourceTargetId, resourceTargetId);
|
||||
remove(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void removeScopedMappings(BigInteger apiKeyId, String resourceType) {
|
||||
if (apiKeyId == null || resourceType == null || resourceType.isBlank()) {
|
||||
return;
|
||||
}
|
||||
QueryWrapper wrapper = QueryWrapper.create()
|
||||
.eq(SysApiKeyResourceMapping::getApiKeyId, apiKeyId)
|
||||
.eq(SysApiKeyResourceMapping::getResourceType, resourceType);
|
||||
remove(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,4 +79,36 @@ public class SysApiKeyServiceImpl extends ServiceImpl<SysApiKeyMapper, SysApiKey
|
||||
return one;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkResourceScope(BigInteger apiKeyId, String requestURI, String resourceType, BigInteger resourceTargetId, String actionScope) {
|
||||
List<String> candidateRequestUris = getCandidateRequestUris(requestURI);
|
||||
QueryWrapper resourceWrapper = QueryWrapper.create();
|
||||
resourceWrapper.in(SysApiKeyResource::getRequestInterface, candidateRequestUris);
|
||||
List<SysApiKeyResource> resources = resourceService.list(resourceWrapper);
|
||||
if (resources == null || resources.isEmpty()) {
|
||||
throw new BusinessException("该接口不存在");
|
||||
}
|
||||
List<BigInteger> resourceIds = resources.stream()
|
||||
.map(SysApiKeyResource::getId)
|
||||
.toList();
|
||||
QueryWrapper exactScopeWrapper = QueryWrapper.create()
|
||||
.eq(SysApiKeyResourceMapping::getApiKeyId, apiKeyId)
|
||||
.in(SysApiKeyResourceMapping::getApiKeyResourceId, resourceIds)
|
||||
.eq(SysApiKeyResourceMapping::getResourceType, resourceType)
|
||||
.eq(SysApiKeyResourceMapping::getResourceTargetId, resourceTargetId)
|
||||
.eq(SysApiKeyResourceMapping::getActionScope, actionScope);
|
||||
if (mappingService.count(exactScopeWrapper) > 0) {
|
||||
return;
|
||||
}
|
||||
QueryWrapper globalScopeWrapper = QueryWrapper.create();
|
||||
globalScopeWrapper.eq(SysApiKeyResourceMapping::getApiKeyId, apiKeyId);
|
||||
globalScopeWrapper.in(SysApiKeyResourceMapping::getApiKeyResourceId, resourceIds);
|
||||
globalScopeWrapper.eq(SysApiKeyResourceMapping::getResourceType, resourceType);
|
||||
globalScopeWrapper.isNull(SysApiKeyResourceMapping::getResourceTargetId);
|
||||
globalScopeWrapper.eq(SysApiKeyResourceMapping::getActionScope, actionScope);
|
||||
if (mappingService.count(globalScopeWrapper) == 0) {
|
||||
throw new BusinessException("该apiKey无权限访问当前资源");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user