身份与权限:Bella OpenAPI 的认证授权体系
引言:企业级安全架构的重要性
在 AI 服务日益普及的今天,API 网关不仅需要提供丰富的功能,还必须建立严格的身份认证和访问控制机制,确保企业数据安全和合规使用。Bella OpenAPI 通过其多层次认证授权体系,成功解决了这一挑战。本文将基于对 Bella OpenAPI 源代码的深入分析,揭示其身份与权限管理的核心设计和实现细节。
多元化的认证机制
通过分析 Bella OpenAPI 的源代码,我们发现系统支持多种认证方式,满足不同使用场景的需求:
1. API Key 认证
API Key 认证是 Bella OpenAPI 最核心的认证机制,特别适用于系统间集成和自动化场景:
// LoginFilter.java
if(StringUtils.isNotBlank(properties.getAuthorizationHeader())) {
String auth =
httpRequest.getHeader(properties.getAuthorizationHeader());
if(StringUtils.isNotBlank(auth)) {
chain.doFilter(request, response);
return;
}
}
// ApikeyService.java
public ApikeyInfo verifyAuth(String auth) {
String ak;
if(auth.startsWith("Bearer ")) {
ak = auth.substring(7);
} else {
ak = auth;
}
String sha = EncryptUtils.sha256(ak);
ApikeyInfo info = queryBySha(sha, true);
// ...验证逻辑
return info;
}
系统使用 Bearer Token 格式传递 API Key,主要用于 API 调用的场景,并通过 SHA-256 哈希存储,确保即使在数据库泄露的情况下原始密钥也不会被暴露。
2. OAuth 认证和 CAS 支持
对于 web 交互场景,系统支持 OAuth 2.0 认证流程,集成了多个身份提供商,同时支持接入企业的 CAS 服务:
// ProviderConditional.java
@ConditionalOnOAuthEnable
@Conditional(ConditionalOnGoogleAuthEnable.GoogleAuthEnableCondition.class)
public @interface ConditionalOnGoogleAuthEnable {
// Google OAuth 条件判定
}
@ConditionalOnOAuthEnable
@Conditional(ConditionalOnGithubAuthEnable.GithubAuthEnableCondition.class)
public @interface ConditionalOnGithubAuthEnable {
// Github OAuth 条件判定
}
3. 父子关系设计
通过这种父子关系设计,企业级用户可以创建具有不同权限范围的子密钥,分配给不同的团队或外部合作伙伴,确保每个密钥只能访问必要的资源。
// ApikeyService.java
@Transactional
public String createByParentCode(ApikeyCreateOp op) {
ApikeyInfo apikey = EndpointContext.getApikey();
if(!apikey.getCode().equals(op.getParentCode())) {
throw new ChannelException.AuthorizationException("没有操作权限");
}
// 验证配额和安全级别
Assert.isTrue(op.getMonthQuota() == null ||
op.getMonthQuota().doubleValue() <=
apikey.getMonthQuota().doubleValue(), "配额超出 ak 的最大配额");
Assert.isTrue(op.getSafetyLevel() <= apikey.getSafetyLevel(),
"安全等级超出 ak 的最高等级");
// 创建子密钥
// ...
// 验证权限范围
if(CollectionUtils.isNotEmpty(op.getPaths())) {
boolean match = op.getPaths().stream().allMatch(url ->
apikey.getRolePath().getIncluded().stream().anyMatch(pattern ->
MatchUtils.matchUrl(pattern, url))
&&
apikey.getRolePath().getExcluded().stream().noneMatch(pattern ->
MatchUtils.matchUrl(pattern, url)));
Assert.isTrue(match, "超出 ak 的权限范围");
// ...
}
return ak;
}