125 lines
3.8 KiB
Markdown
125 lines
3.8 KiB
Markdown
# auth-kit
|
||
|
||
`auth-kit` 是一个可复用的认证/多租户中间件库(Rust + Axum),用于在多个微服务中统一实现:
|
||
|
||
- JWT 认证:从 `Authorization: Bearer <token>` 解析并验证 token,注入 `AuthContext`
|
||
- 租户隔离:从 `X-Tenant-ID` 解析租户并注入 `TenantId`,并在同时存在 token 与 header 时强制一致性
|
||
|
||
该 crate 不包含任何业务逻辑、数据库访问或 RBAC 聚合,仅提供“请求上下文注入 + 类型提取器 + JWT 验证能力”。
|
||
|
||
## 依赖与约定
|
||
|
||
- Web 框架:Axum
|
||
- 统一错误类型:`common_telemetry::AppError`
|
||
- 头部约定:
|
||
- `Authorization: Bearer <access_token>`
|
||
- `X-Tenant-ID: <tenant_uuid>`
|
||
|
||
## 快速开始(在 Axum 中挂载)
|
||
|
||
推荐链路顺序:
|
||
|
||
1. `authenticate_with_config`(注入 `AuthContext`)
|
||
2. `resolve_tenant_with_config`(注入 `TenantId`,并校验 header tenant 与 token tenant 一致)
|
||
|
||
示例:
|
||
|
||
```rust
|
||
use axum::{Router, middleware::from_fn_with_state};
|
||
use auth_kit::middleware::{
|
||
auth::{self, AuthMiddlewareConfig},
|
||
tenant::{self, TenantMiddlewareConfig},
|
||
};
|
||
|
||
let auth_cfg = AuthMiddlewareConfig {
|
||
skip_exact_paths: vec!["/healthz".to_string()],
|
||
skip_path_prefixes: vec!["/scalar".to_string()],
|
||
jwt: auth_kit::jwt::JwtVerifyConfig::rs256_from_jwks(
|
||
"iam-service",
|
||
"http://127.0.0.1:3000/.well-known/jwks.json",
|
||
)?,
|
||
};
|
||
|
||
let tenant_cfg = TenantMiddlewareConfig {
|
||
skip_exact_paths: vec!["/healthz".to_string()],
|
||
skip_path_prefixes: vec!["/scalar".to_string()],
|
||
};
|
||
|
||
let app = Router::new()
|
||
.layer(from_fn_with_state(tenant_cfg, tenant::resolve_tenant_with_config))
|
||
.layer(from_fn_with_state(auth_cfg, auth::authenticate_with_config));
|
||
```
|
||
|
||
## Handler 中如何使用(类型注入)
|
||
|
||
### AuthContext
|
||
|
||
`AuthContext` 会从 request extensions 中提取,如果中间件未运行或缺失认证信息,会返回 `AppError::MissingAuthHeader`。
|
||
|
||
```rust
|
||
use auth_kit::middleware::auth::AuthContext;
|
||
|
||
pub async fn handler(AuthContext { user_id, tenant_id, .. }: AuthContext) {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### TenantId
|
||
|
||
`TenantId` 可从 request extensions 中提取;若不存在,会尝试从 `X-Tenant-ID` 解析;缺失则返回 `AppError::BadRequest("Missing X-Tenant-ID header")`。
|
||
|
||
```rust
|
||
use auth_kit::middleware::tenant::TenantId;
|
||
|
||
pub async fn handler(TenantId(tenant_id): TenantId) {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
## JWT 验证配置(JwtVerifyConfig)
|
||
|
||
`JwtVerifyConfig` 定义在 [jwt.rs](file:///home/shay/project/backend/auth-kit/src/jwt.rs)。
|
||
|
||
- **静态公钥**:`rs256_from_pem(issuer, public_key_pem)`
|
||
- **JWKS 拉取**:`rs256_from_jwks(issuer, jwks_url)`(带缓存与降级)
|
||
- **对称密钥(调试/兼容)**:`hs256_from_secret(issuer, secret)`
|
||
|
||
注意点:
|
||
|
||
- RS256 模式要求 token header 中包含 `kid`(用于从 JWKS 中选 key)
|
||
- issuer 必须与 token 中 `iss` 一致
|
||
- JWKS 目前只支持 RSA + `use=sig` + `alg=RS256`
|
||
|
||
### JWKS 缓存策略(当前实现)
|
||
|
||
- HTTP 超时:1500ms
|
||
- 缓存 TTL:300s
|
||
- stale-if-error:3600s(JWKS 拉取失败时可在窗口内使用缓存 key 继续验签)
|
||
|
||
## Skip 规则(免鉴权路径)
|
||
|
||
`AuthMiddlewareConfig` / `TenantMiddlewareConfig` 都支持:
|
||
|
||
- `skip_exact_paths`:精确路径匹配
|
||
- `skip_path_prefixes`:前缀匹配
|
||
|
||
用于跳过如 `/healthz`、`/scalar`、`/.well-known/jwks.json` 等公开端点。
|
||
|
||
## 端到端验证(推荐)
|
||
|
||
本库自带一个集成测试,用于验证 RS256 + JWKS 的验签路径:
|
||
|
||
- 测试文件: [jwks_verify.rs](file:///home/shay/project/backend/auth-kit/tests/jwks_verify.rs)
|
||
- 运行:
|
||
|
||
```bash
|
||
cd /home/shay/project/backend/auth-kit
|
||
cargo test
|
||
```
|
||
|
||
## 已知限制
|
||
|
||
- 暂未提供“从环境变量自动组装配置”的辅助函数(建议业务服务自行装配 `AuthMiddlewareConfig`)
|
||
- 暂未实现 ECDSA(ES256)/EdDSA(Ed25519)JWKS 解析
|
||
|