Skip to content

贡献者指南

欢迎来到 CoSec -- 一个面向 JVM 的基于 RBAC 和策略的多租户响应式安全框架。本指南旨在帮助你从零开始快速成长为合格的贡献者,无论你来自 Python、JavaScript 还是其他 JVM 语言背景。


第一部分:基础篇

面向 Python 和 JavaScript 工程师的 Kotlin

Kotlin 是一种运行在 JVM 上的静态类型语言。如果你写过 Python 或 JavaScript,许多 Kotlin 概念会觉得很熟悉,但类型系统和空安全需要一些调整。本节为你提供对照参考,让你能流畅地阅读 CoSec 代码。

变量和类型

概念PythonJavaScriptKotlin
不可变变量x = 1(惯例:大写)const x = 1val x = 1
可变变量x = 1let x = 1var x = 1
类型注解x: int = 1x: number = 1(TS)val x: Int = 1
字符串模板f"Hello {name}"`Hello ${name}`"Hello $name"
可空类型Optional[str]string | null(TS)String?
非空断言x!(TS)x!(TS)x!!
安全调用x?.method()(TS)x?.method()
Elvis(默认值)x or defaultx ?? defaultx ?: default

函数和 Lambda

概念PythonJavaScriptKotlin
函数def add(a, b):function add(a, b) {fun add(a: Int, b: Int): Int
Lambdalambda x: x + 1(x) => x + 1{ x -> x + 1 }
单表达式def double(x): return x * 2const double = (x) => x * 2fun double(x: Int) = x * 2
默认参数def f(x=1):function f(x=1) {fun f(x: Int = 1)
命名参数f(name="alice")f({ name: "alice" })f(name = "alice")

类和接口

概念PythonJavaScriptKotlin
class Foo:class Foo { }class Foo { }
接口(抽象基类)(非标准)interface Foo { }
数据类@dataclass(手动)data class Foo(val x: Int)
实现接口class Bar(Foo):class Bar extends Fooclass Bar : Foo
枚举Enum 模块enum(TS)enum class Foo { A, B }
伴生对象@classmethodstaticcompanion object { }
扩展函数(猴子补丁)prototype.methodfun String.first() = this[0]

集合

概念PythonJavaScriptKotlin
列表[1, 2, 3][1, 2, 3]listOf(1, 2, 3)
可变列表[][]mutableListOf<Int>()
字典/映射{"a": 1}{ a: 1 }mapOf("a" to 1)
集合{1, 2}new Set([1, 2])setOf(1, 2)
过滤[x for x in l if x > 0]l.filter(x => x > 0)l.filter { it > 0 }
映射(转换)[f(x) for x in l]l.map(x => f(x))l.map { f(it) }
扁平映射[y for x in l for y in f(x)]l.flatMap(x => f(x))l.flatMap { f(it) }
折叠/归约functools.reduce(op, l, init)l.reduce((a, b) => a + b)l.fold(0) { acc, x -> acc + x }
it 关键字单参数 Lambda 的隐式参数名

可空类型和智能转换

Kotlin 在编译器层面强制执行空安全。String 不能持有 null;只有 String? 可以。

kotlin
val name: String = "Alice"   // 不能为 null
val maybe: String? = null    // 可以为 null

// 空检查后的智能转换
if (maybe != null) {
    println(maybe.length)    // 编译器知道 maybe 在这里非空
}

你会在 CoSec 中看到 ?.(安全调用)、?:(Elvis)和 !!(非空断言)的广泛使用。requireNotNull() 函数也很常见 -- 当值为 null 时它会抛出 IllegalArgumentExceptionPermissionVerifier.kt:69)。

CoSec 中的关键 Kotlin 惯用写法

  1. fun interface(SAM 接口):CoSec 使用 fun interface 定义单抽象方法接口,可以用 Lambda 实例化。例如,PermissionVerifier 就是一个 fun interfacePermissionVerifier.kt:29)。

  2. companion object:Kotlin 对 static 成员的替代方案。广泛用于常量和工厂方法(例如 CoSecPrincipal.ROOT_ID,见 CoSecPrincipal.kt:80)。

  3. 扩展函数:在不修改原有类型的情况下为其添加函数。例如,CoSecPrincipal.isRoot 是一个扩展属性(CoSecPrincipal.kt:94)。

  4. 类型别名:复杂类型的简写。typealias PolicyId = StringPolicy.kt:22)和 typealias RoleId = StringRoleCapable.kt:19)。

  5. 委托(by:Kotlin 支持类委托和属性委托。cosec-core 中的 Delegated.kt 提供了委托工具(Delegated.kt)。

  6. 密封类和枚举:用于固定值集合。Effect 是一个枚举,包含 ALLOWDENY 两个值(Effect.kt:28)。

从第一性原理理解 Spring Boot 和 Project Reactor

CoSec 构建于 Spring Boot 4 和 Project Reactor 之上。理解响应式编程范式对于贡献代码至关重要。

什么是响应式编程?

响应式编程是一种异步、事件驱动的范式,数据通过流传递。你不需要在等待结果时阻塞线程,而是声明当数据到达时应该发生什么

用 Python 的术语来说,可以类比生成器(yield)加上 asyncio。用 JavaScript 的术语来说,可以类比 PromiseObservable(RxJS)。

Mono 和 Flux

Project Reactor 提供两个核心类型:

  • Mono<T>:最多发射一个元素然后完成的流。等价于 JavaScript 中的 Promise<T> 或 Python 中的 Future<T>
  • Flux<T>:发射零个或多个元素然后完成的流。等价于 RxJS 中的 Observable<T>

CoSec 广泛使用 Mono,因为认证和授权通常产生单一结果:

kotlin
// 认证返回 Mono<CoSecPrincipal>(一个结果或空)
fun authenticate(credentials: Credentials): Mono<out CoSecPrincipal>
// 来源:cosec-api/.../Authentication.kt:44

// 授权返回 Mono<AuthorizeResult>(一个结果)
fun authorize(request: Request, context: SecurityContext): Mono<AuthorizeResult>
// 来源:cosec-api/.../Authorization.kt:43

响应式管道

响应式流通过操作符形成管道。以下是 CoSec 中最常见的操作符:

操作符Python 等价JavaScript 等价用途
mapmap(fn, iterable)promise.then(fn)转换值
flatMapasync for x in gen: yield xpromise.then(asyncFn)异步转换
filterfilter(fn, iterable)无(在 .then 中用 if保留匹配项
switchIfEmptyif not result: fallback()promise.catch(fallback)为空时提供替代
onErrorResumetry/exceptpromise.catch(handler)响应式错误处理
toMono()Future(result)Promise.resolve(result)将值包装为 Mono

来自 SimpleAuthorization.authorize() 的真实示例(SimpleAuthorization.kt:213):

kotlin
override fun authorize(request: Request, context: SecurityContext): Mono<AuthorizeResult> {
    val verifyResult = verifyRoot(context)
    if (verifyResult == VerifyResult.ALLOW) {
        return AuthorizeResult.ALLOW.toMono()        // 将值包装为 Mono
    }
    return blacklistChecker
        .check(request, context)                      // Mono<Boolean>
        .flatMap { allowed ->                         // 异步转换
            if (!allowed) {
                return@flatMap AuthorizeResult.EXPLICIT_DENY.toMono()
            }
            verify(request, context)                  // 链接到下一步
        }
}

使用 StepVerifier 测试响应式代码

Reactor 提供了 StepVerifier 来声明式地测试响应式流。你将在每个测试中看到这种模式:

kotlin
authorization.authorize(request, securityContext)
    .test()                                        // 将 Mono 转换为 StepVerifier
    .expectNext(AuthorizeResult.ALLOW)             // 断言发射的值
    .verifyComplete()                              // 断言流完成
// 来源:cosec-core/.../SimpleAuthorizationTest.kt:52-54

Spring Boot 自动配置

Spring Boot 自动配置是一种约定优于配置的机制。使用 @AutoConfiguration 注解的类在满足特定条件时会自动注册 Bean(Spring 管理的对象)。

在 CoSec 中,CoSecAutoConfiguration 是入口点(CoSecAutoConfiguration.kt:37):

kotlin
@ConditionalOnCoSecEnabled                    // 仅当 cosec.enabled=true 时激活
@AutoConfiguration(before = [JacksonAutoConfiguration::class])
@EnableConfigurationProperties(CoSecProperties::class)
class CoSecAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    fun coSecModule(): CoSecModule = CoSecModule()
    // ...
}

条件注解控制功能何时被激活:

  • @ConditionalOnCoSecEnabled -- 检查 cosec.enabled 属性(ConditionalOnCoSecEnabled.kt:23
  • @ConditionalOnJwtEnabled -- 激活 JWT 支持
  • @ConditionalOnAuthorizationEnabled -- 激活授权
  • @ConditionalOnGatewayEnabled -- 激活 Spring Cloud Gateway 集成

MatcherFactoryRegister Bean(MatcherFactoryRegister.kt:24)是一个 SmartLifecycle,它从 Spring 上下文中发现所有 ActionMatcherFactoryConditionMatcherFactory Bean 并注册到 SPI 提供者中。


第二部分:架构和领域模型

全局视图

CoSec 是一个多模块 Gradle 项目。所有公共 API 都在 cosec-api 中定义为接口;所有实现都在 cosec-core 或集成模块中。这种分离意味着你只需阅读 cosec-api 就能理解契约。

mermaid
graph TB
    subgraph "集成层"
        GW["cosec-gateway<br>Spring Cloud Gateway<br>GlobalFilter"]
        WF["cosec-webflux<br>响应式 WebFilter"]
        WM["cosec-webmvc<br>Servlet Filter"]
        ST["cosec-spring-boot-starter<br>自动配置"]
    end

    subgraph "核心层"
        CORE["cosec-core<br>策略评估<br>认证<br>授权"]
    end

    subgraph "API 层"
        API["cosec-api<br>仅接口<br>无依赖"]
    end

    subgraph "扩展模块"
        JWT["cosec-jwt"]
        SOCIAL["cosec-social"]
        CACHE["cosec-cocache"]
        IP["cosec-ip2region"]
        OTEL["cosec-opentelemetry"]
        OPENAPI["cosec-openapi"]
    end

    GW --> CORE
    WF --> CORE
    WM --> CORE
    ST --> GW
    ST --> WF
    ST --> WM
    CORE --> API
    JWT --> CORE
    SOCIAL --> CORE
    CACHE --> CORE
    IP --> CORE
    OTEL --> CORE
    OPENAPI --> CORE

    style API fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CORE fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style ST fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style GW fill:#2d333b,stroke:#30363d,color:#e6edf3
    style WF fill:#2d333b,stroke:#30363d,color:#e6edf3
    style WM fill:#2d333b,stroke:#30363d,color:#e6edf3
    style JWT fill:#161b22,stroke:#30363d,color:#e6edf3
    style SOCIAL fill:#161b22,stroke:#30363d,color:#e6edf3
    style CACHE fill:#161b22,stroke:#30363d,color:#e6edf3
    style IP fill:#161b22,stroke:#30363d,color:#e6edf3
    style OTEL fill:#161b22,stroke:#30363d,color:#e6edf3
    style OPENAPI fill:#161b22,stroke:#30363d,color:#e6edf3

模块依赖关系图

模块依赖顺序在 settings.gradle.ktssettings.gradle.kts)中定义,构建逻辑在 build.gradle.ktsbuild.gradle.kts)中:

模块依赖是否发布用途
cosec-api核心接口,零框架依赖
cosec-corecosec-api策略评估、认证/授权实现
cosec-jwtcosec-coreJWT 令牌处理
cosec-socialcosec-core通过 JustAuth 实现 OAuth 社交登录
cosec-cocachecosec-coreRedis 缓存策略/权限
cosec-ip2regioncosec-coreIP 地理位置信息增强
cosec-opentelemetrycosec-coreOpenTelemetry 链路追踪
cosec-openapicosec-coreSwagger/OpenAPI 集成
cosec-webfluxcosec-core响应式 WebFilter 集成
cosec-webmvccosec-coreServlet Filter 集成
cosec-gatewaycosec-webfluxSpring Cloud Gateway GlobalFilter
cosec-spring-boot-starter以上所有自动配置聚合器
cosec-gateway-servercosec-spring-boot-starter独立网关应用
cosec-dependencies是(BOM)版本管理
cosec-bom是(BOM)物料清单

cosec-gateway-server 是唯一不可发布的模块,在 build.gradle.kts 中配置(build.gradle.kts:33):

kotlin
val serverProjects = setOf(
    project(":cosec-gateway-server"),
)

领域模型

CoSec 的领域模型受 AWS IAM 启发。理解核心类型之间的关系至关重要。

mermaid
classDiagram
    class CoSecPrincipal {
        +String id
        +Map attributes
        +boolean anonymous
        +boolean authenticated
        +isRoot: boolean
        +ROOT_ID: String$
        +ANONYMOUS_ID: String$
    }

    class RoleCapable {
        +Set~RoleId~ roles
    }

    class PolicyCapable {
        +Set~String~ policies
    }

    class TenantPrincipal {
        +String tenantId
    }

    class SecurityContext {
        +CoSecPrincipal principal
        +String tenantId
        +MutableMap attributes
        +getAttributeValue(key) V
        +setAttributeValue(key, value) SecurityContext
    }

    class Request {
        +String path
        +String method
        +String remoteIp
        +String appId
        +String spaceId
        +String deviceId
        +String requestId
        +getHeader(key) String
        +getQuery(key) String
    }

    class Policy {
        +String id
        +String name
        +String category
        +String description
        +PolicyType type
        +String tenantId
        +ConditionMatcher condition
        +List~Statement~ statements
        +verify(Request, SecurityContext) VerifyResult
    }

    class Statement {
        +String name
        +Effect effect
        +ActionMatcher action
        +ConditionMatcher condition
        +verify(Request, SecurityContext) VerifyResult
    }

    class Effect {
        <<enumeration>>
        ALLOW
        DENY
    }

    class ActionMatcher {
        <<interface>>
        +match(Request, SecurityContext) boolean
    }

    class ConditionMatcher {
        <<interface>>
        +match(Request, SecurityContext) boolean
    }

    class Authentication {
        <<interface>>
        +authenticate(Credentials) Mono~CoSecPrincipal~
    }

    class Authorization {
        <<interface>>
        +authorize(Request, SecurityContext) Mono~AuthorizeResult~
    }

    class AuthorizeResult {
        +boolean authorized
        +String reason
        +ALLOW: AuthorizeResult$
        +EXPLICIT_DENY: AuthorizeResult$
        +IMPLICIT_DENY: AuthorizeResult$
    }

    CoSecPrincipal --|> RoleCapable : implements
    CoSecPrincipal --|> PolicyCapable : implements
    TenantPrincipal --|> CoSecPrincipal : extends
    TenantPrincipal --|> TenantCapable : implements
    SecurityContext o-- CoSecPrincipal : holds
    Policy o-- Statement : contains many
    Statement --> Effect : has
    Statement --> ActionMatcher : has
    Statement --> ConditionMatcher : has
    Policy --> ConditionMatcher : has
    Authorization ..> Request : evaluates
    Authorization ..> SecurityContext : uses
    Authorization ..> AuthorizeResult : returns

    style CoSecPrincipal fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Policy fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Statement fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style SecurityContext fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Authorization fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Authentication fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Request fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style ActionMatcher fill:#161b22,stroke:#30363d,color:#e6edf3
    style ConditionMatcher fill:#161b22,stroke:#30363d,color:#e6edf3
    style Effect fill:#161b22,stroke:#30363d,color:#e6edf3
    style AuthorizeResult fill:#161b22,stroke:#30363d,color:#e6edf3
    style RoleCapable fill:#161b22,stroke:#30363d,color:#e6edf3
    style PolicyCapable fill:#161b22,stroke:#30363d,color:#e6edf3
    style TenantPrincipal fill:#161b22,stroke:#30363d,color:#e6edf3

核心接口详解

CoSecPrincipalCoSecPrincipal.kt:35)-- 核心身份类型。继承 java.security.PrincipalPolicyCapableRoleCapable。关键属性:

  • id -- 唯一标识符(第 42 行
  • anonymous -- 当 id == ANONYMOUS_ID"(0)")时为 true(第 61 行
  • authenticated -- anonymous 的反义(第 68 行
  • ROOT_ID -- 默认为 "cosec",可通过系统属性 cosec.root 配置(第 80 行
  • ANONYMOUS_ID -- "(0)"第 87 行
  • isRoot -- 扩展属性,检查 ROOT_ID == id第 94 行

TenantPrincipalTenantPrincipal.kt:26)-- 在 CoSecPrincipal 基础上实现 TenantCapable,增加 tenantId。策略按租户隔离。

PolicyPolicy.kt:45)-- Statement 的命名集合,带可选的 ConditionMatcher。其 verify() 方法(第 76 行)实现了拒绝优先的评估算法:

  1. 检查策略级条件。如果未通过,返回 IMPLICIT_DENY
  2. 评估所有 DENY 语句。如果任一匹配,返回 EXPLICIT_DENY
  3. 评估所有 ALLOW 语句。如果任一匹配,返回 ALLOW
  4. 返回 IMPLICIT_DENY

StatementStatement.kt:37)-- 单条权限规则,包含 EffectActionMatcherConditionMatcher。其 verify() 方法(第 60 行)首先检查 Action 匹配器,然后检查条件匹配器,最后将 Effect 映射为 VerifyResult

AuthenticationAuthentication.kt:32)-- 泛型接口,参数化凭证类型 C 和主体类型 P。返回 Mono<out P>

AuthorizationAuthorization.kt:35)-- fun interface(SAM),接受 RequestSecurityContext,返回 Mono<AuthorizeResult>

RequestRequest.kt:36)-- 表示传入的 HTTP 请求,包含 pathmethodremoteIpappIdspaceIddeviceIdrequestId

SecurityContextSecurityContext.kt:34)-- 持有 CoSecPrincipal、租户信息和可变 attributes,用于在授权过程中在组件间传递数据。

租户类型

CoSec 支持三种租户类型,在 Tenant 接口中定义(Tenant.kt:22):

租户类型ID 常量描述
平台(Platform)"(platform)"根平台租户,管理所有其他租户
默认(Default)"(0)"非多租户场景的默认租户
用户(User)任意其他 ID客户特定租户

授权数据流

下图展示了请求如何流经 CoSec 授权管道:

mermaid
flowchart TD
    REQ["传入请求<br>GET /api/users/123"] --> FILTER["集成过滤器<br>(WebFilter / GlobalFilter / Servlet)"]
    FILTER --> PARSE["解析请求<br>RequestParser"]
    PARSE --> CTX["解析安全上下文<br>SecurityContextParser<br>(JWT / Token)"]
    CTX --> AUTHZ["Authorization.authorize()"]
    AUTHZ --> ROOT{"是 Root<br>用户?"}
    ROOT -- "是" --> ALLOW["ALLOW"]
    ROOT -- "否" --> BL{"黑名单<br>检查"}
    BL -- "已阻止" --> DENY["EXPLICIT_DENY"]
    BL -- "通过" --> GP["全局策略<br>(DENY 优先,然后 ALLOW)"]
    GP -- "找到匹配" --> RESULT["返回结果"]
    GP -- "未匹配" --> PP["用户策略<br>(DENY 优先,然后 ALLOW)"]
    PP -- "找到匹配" --> RESULT
    PP -- "未匹配" --> RP["基于角色的权限<br>(DENY 优先,然后 ALLOW)"]
    RP -- "找到匹配" --> RESULT
    RP -- "未匹配" --> IMPLICIT["IMPLICIT_DENY"]
    RESULT --> FILTER
    ALLOW --> FILTER
    DENY --> FILTER
    IMPLICIT --> FILTER
    FILTER --> RESP{"已授权?"}
    RESP -- "是" --> PASS["传递给过滤器链<br>中的下一个"]
    RESP -- "否(匿名)" --> UNAUTH["HTTP 401<br>未认证"]
    RESP -- "否(已认证)" --> FORBID["HTTP 403<br>禁止访问"]

    style REQ fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style FILTER fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style AUTHZ fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style ALLOW fill:#161b22,stroke:#30363d,color:#e6edf3
    style DENY fill:#161b22,stroke:#30363d,color:#e6edf3
    style IMPLICIT fill:#161b22,stroke:#30363d,color:#e6edf3
    style PASS fill:#161b22,stroke:#30363d,color:#e6edf3
    style UNAUTH fill:#161b22,stroke:#30363d,color:#e6edf3
    style FORBID fill:#161b22,stroke:#30363d,color:#e6edf3
    style GP fill:#2d333b,stroke:#30363d,color:#e6edf3
    style PP fill:#2d333b,stroke:#30363d,color:#e6edf3
    style RP fill:#2d333b,stroke:#30363d,color:#e6edf3

授权流程详解(SimpleAuthorization)

SimpleAuthorizationSimpleAuthorization.kt:48)是核心授权实现。其 authorize() 方法(第 213 行)严格按照以下顺序执行:

  1. Root 用户检查第 217 行)-- 如果 context.principal.isRoot,立即返回 ALLOW。Root 用户绕过所有检查。

  2. 黑名单检查第 221 行)-- 如果 BlacklistCheckerBlacklistChecker.kt:29)阻止请求,返回 EXPLICIT_DENY。默认为 NoOp,放行所有请求。

  3. 验证管道第 194 行)-- 使用 switchIfEmpty 的响应式链:

    • verifyGlobalPolicies第 156 行)-- 评估来自 PolicyRepository.getGlobalPolicy() 的全局策略。
    • verifyPrincipalPolicies第 166 行)-- 评估分配给主体的策略。
    • verifyAppRolePermission第 180 行)-- 评估基于角色的权限。
    • 如果没有匹配,返回 IMPLICIT_DENY

每个策略验证都使用 evaluateDenyFirst 辅助方法(第 61 行),它始终在 ALLOW 语句之前处理 DENY 语句。

集成层:请求如何进入 CoSec

CoSec 与三种 Spring 请求处理模型集成。三者都遵循相同的模式:解析请求、提取安全上下文、执行授权,然后要么放行要么拒绝。

mermaid
flowchart LR
    subgraph "请求源"
        A1["HTTP 请求"]
    end

    subgraph "Spring Cloud Gateway"
        B1["AuthorizationGatewayFilter<br>实现 GlobalFilter"]
    end

    subgraph "Spring WebFlux"
        B2["ReactiveAuthorizationFilter<br>实现 WebFilter"]
    end

    subgraph "Spring WebMVC"
        B3["AuthorizationFilter<br>(Servlet)"]
    end

    subgraph "共享逻辑"
        C1["ReactiveSecurityFilter<br>filterInternal()"]
        C2["SecurityContextParser"]
        C3["Authorization.authorize()"]
    end

    A1 --> B1
    A1 --> B2
    A1 --> B3
    B1 --> C1
    B2 --> C1
    B3 --> C3
    C1 --> C2
    C2 --> C3

    style B1 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style B2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style B3 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style C1 fill:#161b22,stroke:#30363d,color:#e6edf3
    style C2 fill:#161b22,stroke:#30363d,color:#e6edf3
    style C3 fill:#161b22,stroke:#30363d,color:#e6edf3

WebFluxcosec-webflux):ReactiveAuthorizationFilterReactiveAuthorizationFilter.kt:36)实现 WebFilterOrdered。它继承 ReactiveSecurityFilter,后者包含共享的过滤逻辑(ReactiveSecurityFilter.kt:57)。过滤器顺序为 1000第 49 行)。

Gatewaycosec-gateway):AuthorizationGatewayFilterAuthorizationGatewayFilter.kt:31)实现 Spring Cloud Gateway 的 GlobalFilter。它也继承 ReactiveSecurityFilter。过滤器顺序为 HIGHEST_PRECEDENCE + 10第 42 行)。

安全过滤器逻辑ReactiveSecurityFilter.filterInternal() 方法(ReactiveSecurityFilter.kt:66)处理完整的请求生命周期:

  • 使用 RequestParser 解析请求(第 70 行
  • 使用 SecurityContextParser 提取安全上下文(第 73 行
  • 遇到 TokenVerificationException 时,回退到匿名上下文(第 75 行
  • 调用 authorization.authorize()第 85 行
  • 如果已授权,将 principal 设置到 exchange 并继续过滤器链(第 89 行
  • 如果未授权且为匿名用户:HTTP 401(第 99 行
  • 如果未授权且已认证:HTTP 403(第 101 行
  • 处理 TooManyRequestsException:HTTP 429(第 106 行

SPI 扩展点

CoSec 使用 Java 的 Service Provider Interface(SPI)实现可扩展性。自定义匹配器在运行时通过 ServiceLoader 发现。

mermaid
flowchart TD
    subgraph "ServiceLoader 发现"
        SPI1["META-INF/services/<br>me.ahoo.cosec.policy.action<br>.ActionMatcherFactory"]
        SPI2["META-INF/services/<br>me.ahoo.cosec.policy.condition<br>.ConditionMatcherFactory"]
    end

    subgraph "ActionMatcherFactory 工厂"
        A1["AllActionMatcherFactory<br>type: all"]
        A2["PathActionMatcherFactory<br>type: path"]
        A3["CompositeActionMatcherFactory<br>type: composite"]
    end

    subgraph "ConditionMatcherFactory 工厂"
        C1["AllConditionMatcherFactory"]
        C2["AuthenticatedConditionMatcherFactory"]
        C3["InRoleConditionMatcherFactory"]
        C4["InTenantConditionMatcherFactory"]
        C5["SpelConditionMatcherFactory"]
        C6["OgnlConditionMatcherFactory"]
        C7["PathConditionMatcherFactory"]
        C8["RateLimiterConditionMatcherFactory"]
        C9["Eq / Contains / StartsWith<br>/ EndsWith / In / Regular"]
        C10["BoolConditionMatcherFactory"]
        C11["GroupedRateLimiterConditionMatcherFactory"]
    end

    subgraph "注册"
        PROV["ActionMatcherFactoryProvider<br>(SPI + Spring Bean)"]
        CPRV["ConditionMatcherFactoryProvider<br>(SPI + Spring Bean)"]
    end

    SPI1 --> A1
    SPI1 --> A2
    SPI1 --> A3
    SPI2 --> C1
    SPI2 --> C2
    SPI2 --> C3
    SPI2 --> C4
    SPI2 --> C5
    SPI2 --> C6
    SPI2 --> C7
    SPI2 --> C8
    SPI2 --> C9
    SPI2 --> C10
    SPI2 --> C11

    A1 --> PROV
    A2 --> PROV
    A3 --> PROV
    C1 --> CPRV
    C2 --> CPRV
    C3 --> CPRV
    C4 --> CPRV
    C5 --> CPRV
    C6 --> CPRV
    C7 --> CPRV
    C8 --> CPRV
    C9 --> CPRV
    C10 --> CPRV
    C11 --> CPRV

    style SPI1 fill:#161b22,stroke:#30363d,color:#e6edf3
    style SPI2 fill:#161b22,stroke:#30363d,color:#e6edf3
    style PROV fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CPRV fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

SPI 的工作原理

  1. SPI 文件声明内置工厂实现。Action 匹配器 SPI 文件(ActionMatcherFactory)列出:

    • me.ahoo.cosec.policy.action.AllActionMatcherFactory
    • me.ahoo.cosec.policy.action.PathActionMatcherFactory
    • me.ahoo.cosec.policy.action.CompositeActionMatcherFactory

    条件匹配器 SPI 文件(ConditionMatcherFactory)列出 16 个工厂。

  2. Provider 类在启动时加载 SPI 工厂。ActionMatcherFactoryProviderActionMatcherFactoryProvider.kt:20)在其 init 块中使用 ServiceLoader.load() 发现并按类型名称注册工厂。

  3. Spring 集成也通过 MatcherFactoryRegisterMatcherFactoryRegister.kt:24)注册工厂,它从 Spring ApplicationContext 中发现所有 ActionMatcherFactoryConditionMatcherFactory Bean。

添加自定义匹配器

要添加新的 Action 或条件匹配器:

  1. 实现工厂接口(ActionMatcherFactoryConditionMatcherFactory)-- 例如 ActionMatcherFactory.kt:30
  2. 如果是独立的,在 META-INF/services/me.ahoo.cosec.policy.action.ActionMatcherFactory 创建 SPI 文件列出你的类。
  3. 如果使用 Spring Boot,将你的工厂注册为 @BeanMatcherFactoryRegister 会自动发现它。

策略评估流程

DefaultPolicyEvaluatorDefaultPolicyEvaluator.kt:25)提供策略验证(不是授权)。它创建一个模拟的 EvaluateRequest第 64 行)并运行所有条件、Action 和语句,以便在策略投入生产前捕获配置错误。

认证管道

mermaid
sequenceDiagram
    participant C as 客户端
    participant F as 安全过滤器
    participant SP as SecurityContextParser
    participant A as CompositeAuthentication
    participant AP as AuthenticationProvider
    participant Auth as 具体 Authentication
    participant P as CoSecPrincipal

    C->>F: HTTP 请求 + 凭证
    F->>SP: parse(request)
    SP->>A: authenticate(credentials)
    A->>AP: getRequired(credentialsType)
    AP-->>A: Authentication 实例
    A->>Auth: authenticate(credentials)
    Auth-->>A: Mono<CoSecPrincipal>
    A-->>SP: Mono<CoSecPrincipal>
    SP-->>F: SecurityContext(principal)
    F->>F: authorize(request, context)

CompositeAuthenticationCompositeAuthentication.kt:22)使用 AuthenticationProvider 注册表,根据凭证类型委托给相应的 Authentication 实现。

序列化

CoSec 使用 Jackson 进行策略、权限和配置的 JSON 序列化。CoSecModuleCoSecModule)注册了领域类型的自定义序列化器,包括:

  • JsonPolicySerializer -- 序列化 Policy 对象
  • JsonStatementSerializer -- 序列化 Statement 对象
  • JsonActionMatcherSerializer -- 序列化 ActionMatcher 对象
  • JsonConditionMatcherSerializer -- 序列化 ConditionMatcher 对象
  • JsonEffectSerializer -- 序列化 Effect 枚举
  • JsonAppPermissionSerializer -- 序列化 AppPermission 对象

SPI 注册的 tools.jackson.databind.JacksonModule 文件确保模块被自动发现(JacksonModule SPI)。


第三部分:开发工作流

开发环境设置

前置要求

工具版本原因
JDK17+build.gradle.kts 中的 jvmToolchain(17) 要求(build.gradle.kts:83
Gradle已包含 Wrapper(./gradlew无需单独安装
Git任何最新版本版本控制
IDE推荐 IntelliJ IDEA最佳 Kotlin 支持和 Gradle 集成

克隆和构建

bash
# 克隆仓库
git clone https://github.com/Ahoo-Wang/CoSec.git
cd CoSec

# 构建所有模块(编译 + 测试)
./gradlew build

# 不运行测试的构建(初始设置更快)
./gradlew build -x test

首次构建会下载依赖,可能需要几分钟。所有依赖版本集中在 gradle/libs.versions.tomllibs.versions.toml)中管理。

验证你的环境

bash
# 运行所有测试(在干净的检出上应该全部通过)
./gradlew test

# 运行 Detekt 静态分析
./gradlew detekt

# 生成代码覆盖率报告
./gradlew :code-coverage-report:codeCoverageReport

关键构建命令参考

命令功能
./gradlew build编译、运行测试、运行 Detekt、构建 JAR
./gradlew test运行所有模块的测试
./gradlew :cosec-core:test运行单个模块的测试
./gradlew :cosec-core:test --tests "me.ahoo.cosec.policy.DefaultPolicyEvaluatorTest"运行单个测试类
./gradlew :cosec-core:test --tests "me.ahoo.cosec.authorization.SimpleAuthorizationTest.authorizeWhenPrincipalIsRoot"运行单个测试方法
./gradlew detekt运行所有模块的静态分析
./gradlew :cosec-core:jmh运行 JMH 基准测试
./gradlew :cosec-core:jmh -PjmhIncludes=*.SomeBenchmark运行特定 JMH 基准测试

项目结构约定

每个库模块遵循以下结构:

cosec-<module>/
  build.gradle.kts           # 模块构建配置
  src/
    main/
      kotlin/me/ahoo/cosec/  # 生产代码
        <feature>/
          *.kt
      resources/
        META-INF/services/    # SPI 注册文件
    test/
      kotlin/me/ahoo/cosec/  # 测试代码(镜像 main 结构)
        <feature>/
          *Test.kt
      resources/              # 测试夹具

包命名

基础包为 me.ahoo.cosec。子包映射到领域概念:

用途
me.ahoo.cosec.api公共接口(在 cosec-api 中)
me.ahoo.cosec.api.principalPrincipal、角色、策略能力接口
me.ahoo.cosec.api.policyPolicy、Statement、Effect、匹配器
me.ahoo.cosec.api.authorizationAuthorization、AuthorizeResult
me.ahoo.cosec.api.authenticationAuthentication、Credentials
me.ahoo.cosec.api.contextSecurityContext、Attributes
me.ahoo.cosec.api.context.requestRequest、AppIdCapable、DeviceIdCapable
me.ahoo.cosec.api.tenantTenant、TenantCapable
me.ahoo.cosec.api.permissionPermission、AppPermission、RolePermission
me.ahoo.cosec.api.tokenToken、AccessToken、CompositeToken
me.ahoo.cosec.policy策略评估、数据类
me.ahoo.cosec.policy.actionActionMatcher 实现
me.ahoo.cosec.policy.conditionConditionMatcher 实现
me.ahoo.cosec.policy.condition.part基于路径的条件匹配器
me.ahoo.cosec.policy.condition.context基于上下文的条件匹配器
me.ahoo.cosec.policy.condition.limiter限流条件匹配器
me.ahoo.cosec.authorizationAuthorization 实现
me.ahoo.cosec.authenticationAuthentication 实现
me.ahoo.cosec.contextSecurityContext 实现
me.ahoo.cosec.principalPrincipal 实现
me.ahoo.cosec.serializationJackson 序列化器
me.ahoo.cosec.tokenToken 处理
me.ahoo.cosec.blacklist黑名单检查
me.ahoo.cosec.tenantTenant 实现
me.ahoo.cosec.permissionPermission 数据类
me.ahoo.cosec.webfluxWebFlux 集成
me.ahoo.cosec.gatewaySpring Cloud Gateway 集成

你的第一次贡献

寻找任务

  1. 查看 GitHub Issues 中标记为 good first issuehelp wanted 的问题。
  2. 在代码库中搜索 TODO 注释。
  3. 查看测试覆盖率报告中未测试的代码路径。

典型工作流

  1. Fork 和分支:在 GitHub 上 Fork 仓库,然后创建功能分支:

    bash
    git checkout -b feature/my-new-feature
  2. 编写代码:遵循下面描述的约定。

  3. 编写测试:每个新功能或 Bug 修复都必须包含测试。

  4. 本地运行检查

    bash
    ./gradlew build        # 编译 + 测试 + Detekt
    ./gradlew detekt       # 仅静态分析
    ./gradlew test         # 仅测试
  5. 提交和推送:使用清晰的提交信息,遵循 Conventional Commits 格式:

    feat(policy): add regex action matcher
    fix(authorization): handle empty policy list correctly
    test(core): add tests for RateLimiterConditionMatcher
  6. 发起 Pull Request:将分支推送到你的 Fork 并对 main 分支发起 PR。

添加新条件匹配器:分步示例

假设你要添加一个 TimeRangeConditionMatcher,只允许在特定时间段内访问。

步骤 1:创建匹配器类,放在 cosec-core/src/main/kotlin/me/ahoo/cosec/policy/condition/part/TimeRangeConditionMatcher.kt

kotlin
package me.ahoo.cosec.policy.condition.part

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request
import me.ahoo.cosec.api.policy.ConditionMatcher
import me.ahoo.cosec.policy.condition.AbstractConditionMatcher

class TimeRangeConditionMatcher(configuration: Configuration) :
    AbstractConditionMatcher(TYPE, configuration) {

    private val startHour: Int = configuration.get("startHour")?.asInt() ?: 0
    private val endHour: Int = configuration.get("endHour")?.asInt() ?: 23

    override fun internalMatch(request: Request, securityContext: SecurityContext): Boolean {
        val currentHour = java.time.LocalTime.now().hour
        return currentHour in startHour..endHour
    }

    companion object {
        const val TYPE = "timeRange"
    }
}

步骤 2:创建工厂类

kotlin
class TimeRangeConditionMatcherFactory : ConditionMatcherFactory {
    override val type: String = TimeRangeConditionMatcher.TYPE

    override fun create(configuration: Configuration): ConditionMatcher =
        TimeRangeConditionMatcher(configuration)
}

步骤 3:通过 SPI 注册 -- 添加到 cosec-core/src/main/resources/META-INF/services/me.ahoo.cosec.policy.condition.ConditionMatcherFactory

me.ahoo.cosec.policy.condition.part.TimeRangeConditionMatcherFactory

步骤 4:编写测试,放在 cosec-core/src/test/kotlin/me/ahoo/cosec/policy/condition/part/TimeRangeConditionMatcherTest.kt

kotlin
package me.ahoo.cosec.policy.condition.part

import me.ahoo.cosec.configuration.JsonConfiguration
import me.ahoo.cosec.context.SimpleSecurityContext
import me.ahoo.cosec.policy.EvaluateRequest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow

class TimeRangeConditionMatcherTest {
    @Test
    fun `match within range`() {
        val config = """{"type":"timeRange","startHour":0,"endHour":23}"""
            .let { JsonConfiguration.Companion.asConfiguration(it) }
        val matcher = TimeRangeConditionMatcher(config)
        val request = EvaluateRequest()
        val context = SimpleSecurityContext.anonymous()
        assertDoesNotThrow { matcher.match(request, context) }
    }
}

步骤 5:注册 Jackson 序列化器(如果匹配器有自定义序列化格式)。在 me.ahoo.cosec.serialization 中添加序列化器类,并在 CoSecModule 中注册。

测试指南

CoSec 采用分层测试方法,在每个层级使用特定的库。

测试库

用途坐标
JUnit 5测试框架org.junit.jupiter(版本见 libs.versions.toml:18
MockKKotlin Mock 库io.mockk:mockklibs.versions.toml:47
FluentAssert流式断言me.ahoo.test:fluent-assert-corelibs.versions.toml:45
Hamcrest匹配器库org.hamcrest:hamcrestlibs.versions.toml:46
Reactor Test用于 Mono/Flux 的 StepVerifier通过 Spring BOM 引入

编写授权逻辑的测试

SimpleAuthorization 的测试(SimpleAuthorizationTest.kt:41)是一个很好的参考。关键模式:

1. 使用 MockK 进行 Mock

kotlin
val policyRepository = mockk<PolicyRepository>()
val permissionRepository = mockk<AppRolePermissionRepository>()
val request = mockk<Request>()
val securityContext = mockk<SecurityContext> {
    every { principal.id } returns CoSecPrincipal.ROOT_ID
}

2. 使用 StepVerifier 测试响应式流

kotlin
authorization.authorize(request, securityContext)
    .test()                                    // 将 Mono 转换为 StepVerifier
    .expectNext(AuthorizeResult.ALLOW)         // 断言发射的值
    .verifyComplete()                          // 断言流完成

3. 使用数据类构建测试夹具

kotlin
val statement = StatementData(
    effect = Effect.ALLOW,
    action = AllActionMatcher.INSTANCE,
)
val policy = PolicyData(
    id = "test-policy",
    category = "test",
    name = "Test Policy",
    description = "A test policy",
    type = PolicyType.GLOBAL,
    tenantId = Tenant.DEFAULT_TENANT_ID,
    statements = listOf(statement),
)

StatementDataStatementData.kt:31)和 PolicyDataPolicyData.kt:35)是用于构建测试对象的数据类实现。

测试自动配置

cosec-spring-boot-starter 中的测试使用 Spring Boot 的测试上下文框架。参见 CoSecAutoConfigurationTest 中的示例(CoSecAutoConfigurationTest.kt)。

测试策略评估

DefaultPolicyEvaluatorTestDefaultPolicyEvaluatorTest.kt)演示了如何测试策略评估。你可以使用数据类构建策略并验证其评估结果是否正确。

调试技巧

启用调试日志

CoSec 使用 io.github.oshai:kotlin-logging-jvm 进行日志记录(libs.versions.toml:37)。SimpleAuthorization 在 DEBUG 级别记录详细的决策推理:

Verify [Request(...)] [Context(...)] matched Policy[policyId] Statement[0][statementName] - [ALLOW].

要在 config/logback.xml 中启用调试日志:

xml
<logger name="me.ahoo.cosec" level="DEBUG"/>

常见调试场景

1. "ActionMatcherFactory[type] not found":这意味着你在策略 JSON 中引用了一个未注册的匹配器类型。检查工厂是否列在 SPI 文件中,以及 ActionMatcherFactoryProvider 是否已加载它(ActionMatcherFactoryProvider.kt:46)。

2. 意外的 IMPLICIT_DENY:最常见的原因是策略条件不匹配。使用调试日志跟踪正在评估的策略和语句。检查策略的顶层 condition 是否为 AllConditionMatcher(匹配一切)或是否与你的请求上下文匹配。

3. TokenVerificationException 被吞没ReactiveSecurityFilter 捕获 TokenVerificationException 并回退到匿名上下文(ReactiveSecurityFilter.kt:75)。如果你看到意外的 403 响应,检查 Token 解析是否静默失败。

4. StepVerifier 超时:如果 Mono 从未完成,StepVerifier.verifyComplete() 会挂起。使用 .verify(Duration) 设置超时,并检查所有响应式链是否产生值。

5. Detekt 失败:推送前运行 ./gradlew detekt。配置在 config/detekt/detekt.ymldetekt.yml)。构建中默认启用自动纠正(build.gradle.kts:51)。

代码风格和约定

Kotlin 约定

  • 接口在 cosec-api 中,实现在 cosec-core 中:这是最重要的约定。永远不要将实现类放在 cosec-api 中。
  • 全程响应式:核心接口返回 Mono<T>。永远不要在生产代码中阻塞 Mono
  • -Xjsr305=strict-Xjvm-default=all-compatibility:这些编译器标志在 build.gradle.kts:86 中设置。all-compatibility 标志为 Java 互操作性在接口中生成默认方法体。
  • 数据类用于 DTO:对表示数据的对象使用 data class(例如 PolicyDataStatementData)。对有行为的对象使用普通类。
  • 空安全:优先使用非空类型。仅当 null 是有效值时使用 ?。对必需值使用 requireNotNull()

命名约定

元素约定示例
接口PascalCase,无前缀/后缀PolicyActionMatcher
实现Simple 或描述性前缀SimpleAuthorizationPathActionMatcher
数据类名词 + Data 后缀PolicyDataStatementData
工厂名词 + Factory 后缀PathActionMatcherFactory
提供者名词 + Provider 后缀ActionMatcherFactoryProvider
测试类类名 + TestSimpleAuthorizationTest
测试方法camelCase,描述性authorizeWhenPrincipalIsRoot
常量SCREAMING_SNAKE_CASEROOT_IDANONYMOUS_ID

Detekt 配置

Detekt 规则在 config/detekt/detekt.ymldetekt.yml)中配置。值得注意的放宽规则:

  • LongParameterListNestedBlockDepthTooManyFunctions 已禁用
  • MaxLineLength 设置为 200
  • ReturnCount 已禁用
  • MagicNumber 已禁用
  • WildcardImport 排除常用测试导入(java.util.*org.hamcrest.*org.junit.jupiter.api.*

常见陷阱

1. 在生产代码中阻塞 Mono

永远不要在响应式线程(Netty 事件循环)上运行的代码中调用 .block()。这会导致死锁。请使用响应式操作符(mapflatMapswitchIfEmpty)替代。

2. 忘记 SPI 注册

如果你添加了新的 ActionMatcherFactoryConditionMatcherFactory 但忘记将其添加到 SPI 文件中,它将不会被发现。始终更新相应的 META-INF/services/ 文件。

3. 错误的测试导入

项目使用 FluentAssert(me.ahoo.test:fluent-assert-core)进行断言。请导入 me.ahoo.test.asserts.assert -- 而不是 AssertJ 的 assertThat(),后者在 Kotlin 中冗长且非空安全。这是通过约定强制执行的。

4. 忘记 JVM 工具链

所有库模块必须以 Java 17 为目标。这在根 build.gradle.kts 中配置(build.gradle.kts:83)。不要在模块特定的构建文件中覆盖此设置。

5. 向 cosec-api 添加依赖

cosec-api 模块按设计没有框架依赖。永远不要向 cosec-api 添加 Spring、Reactor 或其他框架依赖。如果你需要在接口中使用框架类型,它属于 cosec-core 或集成模块。

6. 不测试拒绝优先逻辑

编写策略评估测试时,始终同时测试 ALLOW 和 DENY 路径。拒绝优先的评估顺序意味着 DENY 语句将覆盖 ALLOW 语句,即使 ALLOW 排在列表前面。这是一个关键的安全属性。

7. 策略评估中的 RateLimiter 异常

DefaultPolicyEvaluator 在评估过程中捕获 TooManyRequestsExceptionDefaultPolicyEvaluator.kt:48)。如果你的策略使用限流条件,不要期望它们在评估时抛出异常 -- 它们会被静默跳过。

8. SecurityContext 的线程安全性

SimpleSecurityContext 使用 ConcurrentHashMap 作为其 attributesSimpleSecurityContext.kt:41)。该类标记为 @ThreadSafe。创建自定义 SecurityContext 实现时,请使用线程安全的集合。


附录

附录 A:术语表

术语定义
ActionMatcher判断请求是否匹配特定 Action 模式(如 URL 路径)的接口。通过 SPI 注册。
AllActionMatcher匹配所有请求的内置 ActionMatcher。类型键:"*"
AllConditionMatcher始终返回 true 的内置 ConditionMatcher。用作默认值。
AllowAnonymous应用于未认证(匿名)用户的策略或语句。
AppId从请求头或查询参数中提取的应用标识符。
AppPermission按应用隔离的权限,包含多组单独的权限。
AppPermissionEvaluator用于评估请求是否匹配应用权限配置的接口。
AppRolePermission应用角色与权限之间的映射。用于授权的第三阶段。
Attribute存储在 SecurityContext.attributes 中的键值对,用于在组件间传递数据。
Authenticate(认证)通过验证凭证来核实用户身份的过程。
Authentication凭证验证的核心接口,返回 Mono<CoSecPrincipal>
AuthenticationProvider将凭证类型映射到 Authentication 实现的注册表。
Authorize(授权)判断已认证用户是否有权执行某操作的过程。
Authorization访问控制决策的核心接口,返回 Mono<AuthorizeResult>
AuthorizeResult授权决策的结果:ALLOW、EXPLICIT_DENY、IMPLICIT_DENY、TOKEN_EXPIRED 或 TOO_MANY_REQUESTS。
BlacklistChecker可选组件,在策略评估前阻止被封禁用户或 IP 的请求。
CoSec框架名称,源自 "Co"(协作)+ "Sec"(安全)。也是配置属性的常量前缀。
CoSecPrincipal表示已认证或匿名用户的核心身份类型,继承 java.security.Principal
CompositeActionMatcher当任一子匹配器匹配时即匹配的 ActionMatcher。用于组合多个路径模式。
CompositeAuthentication根据凭证类型委托给相应处理器的 Authentication 实现。
CompositeToken包含访问令牌和刷新令牌的令牌。
ConditionMatcher评估条件是否满足以使策略或语句生效的接口。
Credentials(凭证)认证用户所需的数据(如用户名/密码、令牌)。
DefaultPolicyEvaluator通过用模拟数据测试所有匹配器来验证策略配置的工具。
DENY明确禁止某操作的 Effect 值。优先于 ALLOW。
DeviceId客户端设备的唯一标识符,从请求头或查询参数中提取。
Effect枚举,包含两个值:ALLOWDENY。决定语句是允许还是阻止操作。
EXPLICIT_DENY表示请求被 DENY 语句主动拒绝的 AuthorizeResult
Fun interfaceKotlin 的 SAM(单抽象方法)接口,可以用 Lambda 实例化。
Global Policy(全局策略)PolicyType.GLOBAL 的策略,适用于所有应用的所有请求。
IMPLICIT_DENY表示没有策略或权限匹配请求的 AuthorizeResult。默认结果。
JMHJava Microbenchmark Harness。用于每个模块的性能基准测试。
JWTJSON Web Token。用于无状态认证令牌。
LocalPolicyLoader从本地配置文件加载策略的组件。
MatcherFactory从 JSON 配置创建 ActionMatcherConditionMatcher 实例的工厂接口。
MatcherFactoryRegisterSpring SmartLifecycle Bean,从 Spring 上下文中发现和注册匹配器工厂。
MonoProject Reactor 类型,表示 0 或 1 个元素的异步流。
Multi-Tenancy(多租户)按客户(租户)隔离数据和策略的能力。
Null Safety(空安全)Kotlin 的类型系统,区分可空(T?)和非空(T)类型。
OGNLObject-Graph Navigation Language。用作条件匹配器的表达式语言。
PathActionMatcher通过 URL 路径模式(Ant 风格)匹配请求的内置 ActionMatcher
Permission(权限)单条访问控制规则,包含 Effect、Action 匹配器和可选条件。
PermissionVerifierfun interface,验证请求是否被允许,返回 VerifyResult
Policy(策略)Statement 的命名集合,带可选条件。访问控制配置的主要单元。
PolicyCapable具有策略分配的实体接口。提供 policies: Set<String>
PolicyDataPolicy 的数据类实现。
PolicyEvaluator用于验证策略配置的接口。
PolicyIdString 的类型别名,表示策略标识符。
PolicyRepository按 ID 获取策略或获取全局策略的仓库接口。
PolicyType枚举:GLOBAL(全局适用)、SYSTEM(管理员管理)、CUSTOM(用户自定义)。
Principal(主体)安全身份。在 CoSec 中,CoSecPrincipal 继承 java.security.Principal
Project ReactorCoSec 全程使用的响应式编程库,用于异步操作。
RateLimiterConditionMatcher限制请求频率的条件匹配器。
ReactiveAuthorizationFilterSpring WebFlux WebFilter,对每个请求执行授权。
ReactiveSecurityFilter响应式授权过滤器的基类,包含共享的过滤逻辑。
Request表示传入 HTTP 请求的接口,包含路径、方法、头信息和元数据。
RequestId用于在系统中追踪请求的唯一标识符。
Role(角色)分配给用户的权限命名集合。
RoleCapable具有角色分配的实体接口。提供 roles: Set<RoleId>
RoleIdString 的类型别名,表示角色标识符。
SAM Interface单抽象方法接口。Kotlin 的 fun interface 创建此类接口。
SecurityContext持有当前主体、租户信息和请求的可变属性的对象。
ServiceLoaderJava 的 SPI 机制,用于在运行时发现实现。用于匹配器工厂。
SimpleAuthorization核心 Authorization 实现,包含 Root 绕过、黑名单、策略和角色评估。
SimplePrincipal基础的 CoSecPrincipal 实现。
SimpleSecurityContext使用 ConcurrentHashMap 的线程安全 SecurityContext 实现。
SpaceId从请求头中提取的租户/工作空间标识符。
SpELSpring Expression Language。用作条件匹配器的表达式语言。
SPIService Provider Interface。Java 在运行时发现和加载实现的机制。
Statement(语句)Policy 中的单条权限规则,包含 EffectActionMatcherConditionMatcher
StatementDataStatement 的数据类实现。
StepVerifierReactor Test 工具,用于在测试中验证 MonoFlux 流。
Tenant(租户)用于隔离策略和数据的组织边界。三种类型:Platform、Default、User。
TenantCapable属于某个租户的实体接口。提供 tenantId
TenantPrincipal同时实现 TenantCapableCoSecPrincipal,将用户与租户关联。
Token(令牌)代表已认证会话的凭证(通常是 JWT)。
TokenVerificationException当令牌无法验证(如无效签名、已过期)时抛出的异常。
TooManyRequestsException当限流条件被超出时抛出的异常。
VerifyResult枚举:ALLOWEXPLICIT_DENYIMPLICIT_DENY。策略验证的内部结果。

附录 B:关键文件参考

cosec-api -- 核心接口

文件关键接口用途
CoSec.ktCoSec框架常量(COSECDEFAULT
CoSecPrincipal.ktCoSecPrincipal核心身份,包含 idrolespoliciesattributes
TenantPrincipal.ktTenantPrincipal带租户上下文的主体
RoleCapable.ktRoleCapableRoleId角色分配能力
PolicyCapable.ktPolicyCapable策略分配能力
Authentication.ktAuthentication<C, P>凭证验证
Authorization.ktAuthorization访问控制决策
AuthorizeResult.ktAuthorizeResult决策结果(ALLOW/DENY/IMPLICIT_DENY)
Policy.ktPolicyStatement 的命名集合
Statement.ktStatement单条权限规则
Effect.ktEffectALLOW 或 DENY
PolicyType.ktPolicyTypeGLOBAL、SYSTEM、CUSTOM
ActionMatcher.ktActionMatcher请求 Action 匹配
ConditionMatcher.ktConditionMatcher条件评估
PermissionVerifier.ktPermissionVerifierVerifyResult权限检查
SecurityContext.ktSecurityContext请求安全上下文
Request.ktRequest传入的 HTTP 请求
Tenant.ktTenant带 ID 和类型检查的租户

cosec-core -- 实现

文件关键类用途
SimpleAuthorization.ktSimpleAuthorization核心授权逻辑
PolicyRepository.ktPolicyRepository策略存储接口
AppRolePermissionRepository.ktAppRolePermissionRepository角色权限存储
PolicyVerifyContext.ktVerifyContext跟踪评估结果的上下文
CompositeAuthentication.ktCompositeAuthentication基于凭证类型的认证委托
DefaultPolicyEvaluator.ktDefaultPolicyEvaluator策略验证工具
PolicyData.ktPolicyDataPolicy 数据类
StatementData.ktStatementDataStatement 数据类
PathActionMatcher.ktPathActionMatcherURL 路径模式匹配
AllActionMatcher.ktAllActionMatcher匹配所有请求的通配符
ActionMatcherFactory.ktActionMatcherFactory工厂 SPI 接口
ActionMatcherFactoryProvider.ktActionMatcherFactoryProviderSPI 注册表
ConditionMatcherFactory.ktConditionMatcherFactory工厂 SPI 接口
AbstractConditionMatcher.ktAbstractConditionMatcher带取反支持的基类
SimplePrincipal.ktSimplePrincipal基础 CoSecPrincipal 实现
SimpleSecurityContext.ktSimpleSecurityContext线程安全的 SecurityContext
BlacklistChecker.ktBlacklistChecker黑名单检查接口

SPI 注册文件

文件注册的工厂
ActionMatcherFactoryAllActionMatcherFactoryPathActionMatcherFactoryCompositeActionMatcherFactory
ConditionMatcherFactory16 个工厂,包括 SpelOgnlRateLimiter、基于路径和基于上下文的匹配器

集成模块

文件关键类用途
ReactiveSecurityFilter.ktReactiveSecurityFilter基础过滤器逻辑(解析、认证、决策)
ReactiveAuthorizationFilter.ktReactiveAuthorizationFilterWebFlux WebFilter
AuthorizationGatewayFilter.ktAuthorizationGatewayFilterSpring Cloud Gateway GlobalFilter
CoSecAutoConfiguration.ktCoSecAutoConfiguration自动配置入口点
MatcherFactoryRegister.ktMatcherFactoryRegisterSpring Bean 工厂注册
ConditionalOnCoSecEnabled.kt@ConditionalOnCoSecEnabled功能开关注解

构建配置

文件用途
build.gradle.kts根构建:插件、Detekt、Jacoco、JMH、发布
settings.gradle.kts模块引入
gradle/libs.versions.toml版本目录
config/detekt/detekt.ymlDetekt 静态分析配置

测试文件(关键示例)

文件测试内容
SimpleAuthorizationTest.ktRoot 绕过、黑名单、全局/用户/角色策略
DefaultPolicyEvaluatorTest.kt策略验证
PathActionMatcherTest.ktURL 路径匹配
ReactiveAuthorizationFilterTest.ktWebFlux 过滤器集成
CoSecAutoConfigurationTest.kt自动配置

附录 C:速查卡

构建命令

bash
./gradlew build                                                    # 完整构建
./gradlew test                                                     # 所有测试
./gradlew :cosec-core:test                                         # 单模块测试
./gradlew :cosec-core:test --tests "*.SimpleAuthorizationTest"     # 单个测试类
./gradlew detekt                                                   # 静态分析
./gradlew :code-coverage-report:codeCoverageReport                 # 覆盖率报告
./gradlew :cosec-core:jmh -PjmhIncludes=*.SomeBenchmark            # 基准测试

关键类型层次结构

java.security.Principal
  └── CoSecPrincipal(接口)
        ├── PolicyCapable(policies: Set<String>)
        ├── RoleCapable(roles: Set<RoleId>)
        └── TenantPrincipal(tenantId: String)

Request(path, method, remoteIp, appId, spaceId, deviceId)
  └── EvaluateRequest(测试用模拟)

SecurityContext(principal, tenantId, attributes)
  └── SimpleSecurityContext(ConcurrentHashMap attributes)

Policy(id, name, type, condition, statements)
  └── PolicyData(data class)

Statement(name, effect, action, condition)
  └── StatementData(data class)

Effect = ALLOW | DENY
PolicyType = GLOBAL | SYSTEM | CUSTOM
VerifyResult = ALLOW | EXPLICIT_DENY | IMPLICIT_DENY
AuthorizeResult = ALLOW | EXPLICIT_DENY | IMPLICIT_DENY | TOKEN_EXPIRED | TOO_MANY_REQUESTS

授权决策树

1. principal.isRoot?      → ALLOW
2. 请求在黑名单中?         → EXPLICIT_DENY
3. 全局策略匹配?
   - 有 DENY 匹配?        → EXPLICIT_DENY
   - 有 ALLOW 匹配?       → ALLOW
4. 用户策略匹配?
   - 有 DENY 匹配?        → EXPLICIT_DENY
   - 有 ALLOW 匹配?       → ALLOW
5. 角色权限匹配?
   - 有 DENY 匹配?        → EXPLICIT_DENY
   - 有 ALLOW 匹配?       → ALLOW
6. 无匹配?                → IMPLICIT_DENY

配置属性

所有 CoSec 属性以 cosec. 为前缀:

属性默认值用途
cosec.enabledtrue主开关
cosec.authentication.enabledtrue启用认证
cosec.authorization.enabledtrue启用授权
cosec.jwt.enabledtrue启用 JWT 支持
cosec.gateway.enabledfalse启用 Gateway 集成
cosec.social.enabledfalse启用社交认证
cosec.cache.enabledfalse启用 Redis 缓存
cosec.ip2region.enabledfalse启用 IP 地理位置
cosec.openapi.enabledfalse启用 OpenAPI 集成
cosec.opentelemetry.enabledfalse启用 OpenTelemetry 链路追踪
cosec.inject.enabledfalse启用安全上下文注入

技术栈速查

组件版本来源
Kotlin2.3.20libs.versions.toml:26
Java 目标17build.gradle.kts:83
Spring Boot4.0.5libs.versions.toml:3
Spring Cloud2025.1.1libs.versions.toml:4
JUnit6.0.3libs.versions.toml:18
MockK1.14.9libs.versions.toml:21
FluentAssert0.2.6libs.versions.toml:19
Detekt1.23.8libs.versions.toml:24
Jackson(JWT)4.5.1libs.versions.toml:12
OGNL3.4.11libs.versions.toml:11

基于 Apache License 2.0 发布