08 · 安全
安全能力在 ace-security module(import aceboot::security.*)。框架内核不内置安全——安全是「第三方关注点」,经中间件接入。
所有 guard 中间件(
jwtAuth/csrf/rateLimit等)失败时抛HttpError(4xx)。前提是 App 已onError(jsonErrorHandler)(AceApplication.buildApp默认已接),否则 401/403 会退化成 500。
JWT
HS256(HMAC-SHA256)签发与校验:
public class Claims {
public let sub: String // 主体(用户标识)
public let roles: Array<String> // 角色
public let exp: Int64 // Unix 秒,0 表示不过期
public init(sub: String, roles: Array<String>, exp: Int64)
}
signJwt(claims: Claims, secret: String): String // 签发
verifyJwt(token: String, secret: String): ?Claims // 校验(不验 exp)
verifyJwtAt(token: String, secret: String, nowSec: Int64): ?Claims // 校验 + 验 exp
密钥不内置,从配置读取:
let secret = appConfig.getOr("jwt.secret", "change-me")
let token = signJwt(Claims(userId, ["admin"], 0), secret)
认证 / 授权 guard 中间件
jwtAuth(secret: String): Middleware // 从 Authorization: Bearer <token> 验签,通过则把 Principal 写入 ctx.state
authenticated(): Middleware // 要求已认证,否则 401
requireRoles(roles: Array<String>): Middleware // 要求拥有指定角色之一(OR),否则 403
当前用户(Principal)
public class Principal {
public let sub: String
public let roles: Array<String>
public func hasRole(role: String): Bool
}
currentPrincipal(ctx: Context): ?Principal // 读取已认证主体(jwtAuth 写入),未认证返回 None
@Post["/"]
public func create(ctx: Context, body: CreateTask): TaskView {
let owner = match (currentPrincipal(ctx)) {
case Some(p) => p.sub
case None => ""
}
tasks.create(body.title, owner)
}
路由级声明式授权
authorize(policies) 按策略对路由要求角色,与控制器解耦:
public struct RolePolicy {
public init(method: String, pathPrefix: String, roles: Array<String>) // method 用 "*" 通配
}
authorize(policies: Array<RolePolicy>): Middleware
- 命中的多条策略全部要求(AND);单条策略内
roles是之一(OR)。 - 无 Principal → 401;有但角色不匹配 → 403。
完整示例(认证 + 授权,声明式注册)
用 @Middleware(见 10 章)声明式接入:
package myapp.middleware
import aceboot::framework.*
import aceboot::framework_macros.*
import aceboot::security.*
// 认证:登录端点放行,其余要求合法 JWT
@Middleware[100]
public func authentication(): Middleware {
let auth = jwtAuth(appConfig.getOr("jwt.secret", "change-me"))
return {ctx, next =>
if (ctx.path == "/api/auth/login") { next() } else { auth(ctx, next) }
}
}
// 授权:声明式角色策略
@Middleware[110]
public func authorization(): Middleware {
authorize([
RolePolicy("GET", "/api/admin", ["admin"]), // 管理端点仅 admin
RolePolicy("*", "/api", ["user", "admin"]) // 其余 API 需 user/admin
])
}
登录端点签发 token:
@Controller["/api/auth"]
public class AuthController {
@AppConfig var config: Config
@Post["/login"]
public func login(body: LoginRequest): TokenResponse {
// 校验凭据(略)...
let token = signJwt(Claims(body.username, ["user"], 0),
config.getOr("jwt.secret", "change-me"))
let r = TokenResponse(); r.token = token; r
}
}
安全头 / CSRF / 限流
securityHeaders(): Middleware // X-Content-Type-Options/X-Frame-Options/Referrer-Policy 等安全响应头
csrf(): Middleware // 双提交 cookie 防护:安全方法下发 ace_csrf cookie,变更方法比对 X-CSRF-Token
限流是可插拔组件,import aceboot::security.* 即按配置自动装配(无需手写):
[ratelimit]
enabled = true
max = 100 # 窗口内最大请求数
windowMs = 60000 # 窗口毫秒
超额抛 HttpError(429, "rate limit exceeded")。也可手动用 rateLimit(max, windowMs, nowMs) 经 .use()/@Middleware 注册。
下一章:ORM 数据访问。