Skip to content

快速入门

本指南将引导你将 CoSec 添加到 Spring Boot 应用中,配置 JWT 认证,创建第一个策略,并使用 curl 验证访问控制。

前提条件

  • Java 17+
  • Kotlin 2.x 或 Java 17 项目
  • Spring Boot 4.x
  • Gradle(推荐 Kotlin DSL)

第 1 步:添加依赖

build.gradle.kts 中添加 CoSec Spring Boot starter:

kotlin
dependencies {
    implementation("me.ahoo.cosec:cosec-spring-boot-starter")
}

该 starter 会传递性地引入 cosec-corecosec-apicosec-jwt。其他集成模块(WebFlux、Gateway、缓存)作为 Gradle 功能变体提供:

kotlin
dependencies {
    implementation("me.ahoo.cosec:cosec-spring-boot-starter") {
        capabilities {
            requireCapability("me.ahoo.cosec:cosec-spring-boot-starter:webflux-support")
        }
    }
    // 或者用于 Gateway:
    // requireCapability("me.ahoo.cosec:cosec-spring-boot-starter:gateway-support")
    // 或者用于缓存(Redis):
    // requireCapability("me.ahoo.cosec:cosec-spring-boot-starter:cache-support")
}

Starter 会根据检测到的依赖自动配置安全组件。自动配置由 @ConditionalOnCoSecEnabled 控制(cosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/ConditionalOnCoSecEnabled.kt)。

第 2 步:配置应用属性

创建或编辑 application.yaml

yaml
cosec:
  enabled: true
  jwt:
    algorithm: hmac256
    secret: "your-256-bit-secret-key-here-change-me"
    token-validity:
      access: PT10M     # 10 分钟
      refresh: P7D      # 7 天
  authorization:
    enabled: true
    local-policy:
      enabled: true
      locations:
        - "classpath:cosec-policy/*-policy.json"
属性类型默认值描述
cosec.enabledBooleantrueCoSec 的主开关
cosec.jwt.algorithmEnumhmac256JWT 签名算法(hmac256hmac384hmac512
cosec.jwt.secretString必填JWT 签名的密钥
cosec.jwt.token-validity.accessDurationPT10M访问令牌 TTL
cosec.jwt.token-validity.refreshDurationP7D刷新令牌 TTL
cosec.authorization.enabledBooleantrue启用授权
cosec.authorization.local-policy.enabledBooleanfalse从本地 JSON 文件加载策略
cosec.authorization.local-policy.locationsSet<String>classpath:cosec-policy/*-policy.json策略文件的 Glob 模式

JWT 属性定义在 JwtPropertiescosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/jwt/JwtProperties.kt:28)中,授权属性定义在 AuthorizationPropertiescosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/authorization/AuthorizationProperties.kt:27)中。

第 3 步:创建第一个策略

src/main/resources/cosec-policy/anonymous-access-policy.json 创建文件:

json
{
  "id": "anonymous-access",
  "name": "Anonymous Access",
  "category": "access",
  "description": "Allow anonymous access to auth endpoints and health checks",
  "type": "global",
  "tenantId": "(platform)",
  "statements": [
    {
      "name": "AuthEndpoints",
      "action": [
        "/auth/login",
        "/auth/register",
        "/auth/refresh"
      ]
    },
    {
      "name": "HealthCheck",
      "action": [
        "/actuator/health",
        "/actuator/health/readiness",
        "/actuator/health/liveness"
      ]
    }
  ]
}

此策略使用默认的 ALLOW 效果(没有 effect 字段时默认为 "allow"),并按路径模式匹配请求。没有 condition 的语句适用于所有请求,包括匿名请求。

策略 JSON 格式遵循 schema/cosec-policy.schema.json 中的 schema。

第 4 步:启动应用

bash
./gradlew bootRun

启动时,CoSec 将:

  1. 自动配置安全过滤器链
  2. 从配置的位置加载本地策略文件
  3. 通过 SPI 注册 ActionMatcherFactoryConditionMatcherFactory 实现
  4. 使用配置的算法和密钥初始化 JWT 令牌服务

自动配置入口点是 CoSecAutoConfigurationcosec-spring-boot-starter/src/main/kotlin/me/ahoo/cosec/spring/boot/starter/CoSecAutoConfiguration.kt:37)。

第 5 步:使用 curl 测试

访问公开端点(策略允许):

bash
curl -v http://localhost:8080/actuator/health
# 预期结果:200 OK

在没有令牌的情况下访问受保护端点:

bash
curl -v http://localhost:8080/api/users
# 预期结果:401 Unauthorized(无凭证)
# 或 403 Forbidden(匿名用户,没有匹配的 ALLOW 策略)

认证流程

当客户端进行认证时,将发生以下序列:

mermaid
sequenceDiagram
    autonumber
    participant Client
    participant App as Spring Boot App
    participant AuthN as Authentication Service
    participant JWT as JWT Service
    participant AuthZ as Authorization Service

    Client->>App: POST /auth/login {credentials}
    App->>AuthN: authenticate(credentials)
    AuthN->>AuthN: Validate Credentials
    AuthN-->>App: CoSecPrincipal
    App->>JWT: generateToken(principal)
    JWT-->>App: AccessToken + RefreshToken
    App-->>Client: 200 {accessToken, refreshToken}

    Note over Client,AuthZ: Subsequent requests

    Client->>App: GET /api/resource<br>Authorization: Bearer {accessToken}
    App->>JWT: validateToken(accessToken)
    JWT-->>App: CoSecPrincipal
    App->>AuthZ: authorize(request, context)
    AuthZ->>AuthZ: Evaluate Policies
    AuthZ-->>App: AuthorizeResult
    App-->>Client: 200 OK (or 403 Forbidden)

Authentication 接口对凭证类型 C 和主体类型 P 进行了泛型化(cosec-api/src/main/kotlin/me/ahoo/cosec/api/authentication/Authentication.kt:32):

kotlin
interface Authentication<C : Credentials, out P : CoSecPrincipal> {
    val supportCredentials: Class<C>
    fun authenticate(credentials: C): Mono<out P>
}

创建自定义认证(Kotlin)

要添加自定义认证机制,需实现 Authentication 接口:

kotlin
@Component
class UsernamePasswordAuthentication(
    private val userRepository: UserRepository
) : Authentication<UsernamePasswordCredentials, TenantPrincipal> {

    override val supportCredentials = UsernamePasswordCredentials::class.java

    override fun authenticate(
        credentials: UsernamePasswordCredentials
    ): Mono<TenantPrincipal> {
        return userRepository.findByUsername(credentials.username)
            .filter { passwordEncoder.matches(credentials.password, it.hashedPassword) }
            .map { user ->
                SimpleTenantPrincipal(
                    id = user.id,
                    roles = user.roles,
                    policies = user.policies,
                    tenantId = user.tenantId
                )
            }
    }
}

项目结构

按照本指南操作后,你的项目结构应如下所示:

src/main/
  resources/
    application.yaml
    cosec-policy/
      anonymous-access-policy.json

授权评估顺序

设计策略时,理解评估顺序至关重要:

mermaid
flowchart TD
    A["authorize(request, context)"] --> B{Principal is Root?}
    B -->|Yes| C["Return ALLOW"]
    B -->|No| D{Request Blacklisted?}
    D -->|Yes| E["Return EXPLICIT_DENY"]
    D -->|No| F["evaluateDenyFirst(Global Policies)"]
    F --> G{DENY Statement<br>Matches?}
    G -->|Yes| E
    G -->|No| H{ALLOW Statement<br>Matches?}
    H -->|Yes| C
    H -->|No| I["evaluateDenyFirst(Principal Policies)"]
    I --> J{DENY Statement<br>Matches?}
    J -->|Yes| E
    J -->|No| K{ALLOW Statement<br>Matches?}
    K -->|Yes| C
    K -->|No| L["evaluateDenyFirst(Role Permissions)"]
    L --> M{DENY Statement<br>Matches?}
    M -->|Yes| E
    M -->|No| N{ALLOW Statement<br>Matches?}
    N -->|Yes| C
    N -->|No| O["Return IMPLICIT_DENY"]

    style A fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style B fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style C fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style D fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style E fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style F fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style G fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style H fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style I fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style J fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style K fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style L fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style M fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style N fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style O fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

此逻辑在 SimpleAuthorizationcosec-core/src/main/kotlin/me/ahoo/cosec/authorization/SimpleAuthorization.kt:48)中实现。DENY 优先的方法确保显式拒绝始终优先于任何允许。

相关页面

参考资料

基于 Apache License 2.0 发布