feat: 收口知识库分享链路
- 新增 shareKey 单参数 URL 分享页与失效页 - 新增知识库分享后端鉴权、审计与迁移脚本 - 在访问令牌中增加知识库分享授权入口
This commit is contained in:
@@ -23,6 +23,9 @@ public class SysApiKey extends SysApiKeyBase {
|
||||
@Column(ignore = true)
|
||||
List<BigInteger> permissionIds;
|
||||
|
||||
@Column(ignore = true)
|
||||
private Boolean knowledgeShareEnabled;
|
||||
|
||||
@RelationOneToMany(selfField = "id", targetField = "apiKeyId", targetTable = "tb_sys_api_key_resource_mapping")
|
||||
private List<SysApiKeyResourceMapping> resourcePermissions;
|
||||
|
||||
@@ -41,4 +44,12 @@ public class SysApiKey extends SysApiKeyBase {
|
||||
public void setPermissionIds(List<BigInteger> permissionIds) {
|
||||
this.permissionIds = permissionIds;
|
||||
}
|
||||
|
||||
public Boolean getKnowledgeShareEnabled() {
|
||||
return knowledgeShareEnabled;
|
||||
}
|
||||
|
||||
public void setKnowledgeShareEnabled(Boolean knowledgeShareEnabled) {
|
||||
this.knowledgeShareEnabled = knowledgeShareEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,24 @@ public class SysApiKeyResourceMappingBase implements Serializable {
|
||||
@Column(comment = "请求接口资源访问id")
|
||||
private BigInteger apiKeyResourceId;
|
||||
|
||||
/**
|
||||
* 资源类型
|
||||
*/
|
||||
@Column(comment = "资源类型")
|
||||
private String resourceType;
|
||||
|
||||
/**
|
||||
* 资源目标ID
|
||||
*/
|
||||
@Column(comment = "资源目标ID")
|
||||
private BigInteger resourceTargetId;
|
||||
|
||||
/**
|
||||
* 动作范围
|
||||
*/
|
||||
@Column(comment = "动作范围")
|
||||
private String actionScope;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -53,4 +71,28 @@ public class SysApiKeyResourceMappingBase implements Serializable {
|
||||
this.apiKeyResourceId = apiKeyResourceId;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public BigInteger getResourceTargetId() {
|
||||
return resourceTargetId;
|
||||
}
|
||||
|
||||
public void setResourceTargetId(BigInteger resourceTargetId) {
|
||||
this.resourceTargetId = resourceTargetId;
|
||||
}
|
||||
|
||||
public String getActionScope() {
|
||||
return actionScope;
|
||||
}
|
||||
|
||||
public void setActionScope(String actionScope) {
|
||||
this.actionScope = actionScope;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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