子平台接入 AuthHub 的官方 Python SDK:登录、运行时鉴权一站直达。
Project description
authhub-rbac-sdk
子平台(cortexa / athanor / …)接入 AuthHub 的官方 RBAC SDK。
PyPI: https://pypi.org/project/authhub-rbac-sdk/
核心约束:子平台只调 AuthHub,绝不直接连 PhilVault;登录、token 刷新、
每次请求的鉴权决策都走 AuthHub HTTP API(/api/v2/auth/*、/api/v2/sdk/*)。
路由 → 资源/动作 的映射由 AuthHub 服务端匹配,SDK 端无感。
与历史 authz/ 的关系
旧的 authz/ 是 Vortek/湍流版权的 Django 中间件,直接调用 philvault_sdk。
本 SDK 是其替代品:
| 维度 | 旧 authz/ (Vortek) |
新 authhub_sdk/ |
|---|---|---|
| 通信对象 | 直连 PhilVault | 只调 AuthHub |
| 路由映射匹配 | 子平台拉 /authz/mapping 本地缓存 |
AuthHub 服务端匹配 |
| 鉴权决策 | 本地 + PhilVault check | 一次 POST /sdk/authorize 拿结论 |
| 跨框架 | 仅 Django | Django + FastAPI |
| Token 缓存 | 30s 本地缓存 | 默认无缓存(运行时鉴权链路不缓存) |
新接入的子平台只用本包,不要再引入 authz/。
安装
从 PyPI 安装:
# 基础安装(只用 AuthhubClient / AsyncAuthhubClient)
pip install authhub-rbac-sdk
# Django 接入(自动带上 Django >=4.2)
pip install "authhub-rbac-sdk[django]"
# FastAPI 接入
pip install "authhub-rbac-sdk[fastapi]"
# Django + FastAPI 都装
pip install "authhub-rbac-sdk[all]"
仓库内开发:
# 基础安装
pip install /path/to/authhub/authhub_sdk
# Django 接入
pip install "authhub-rbac-sdk[django] @ file:///path/to/authhub/authhub_sdk"
# FastAPI 接入
pip install "authhub-rbac-sdk[fastapi] @ file:///path/to/authhub/authhub_sdk"
# 开发依赖(含 pytest)
pip install -e "/path/to/authhub/authhub_sdk[dev]"
依赖:
httpx >= 0.27(基础)- Python 3.10+
- Django >=4.2(extra
[django]) - FastAPI >=0.110(extra
[fastapi])
包采用
src/布局:源码在authhub_sdk/src/authhub_sdk/,安装后通过import authhub_sdk引用,与 Django/FastAPI 上游包不会发生命名冲突。PyPI 包名是
authhub-rbac-sdk,但 import 路径仍是authhub_sdk。
三件套环境变量
export AUTHHUB_BASE_URL=http://authhub.internal:6001 # 不带 /api/v2
export AUTHHUB_APP_CODE=cortexa
# 可选
export AUTHHUB_TIMEOUT=5
export AUTHHUB_VERIFY_SSL=true
export AUTHHUB_BYPASS_PATHS=/health,/static/,/api/v2/sdk/healthz
AUTHHUB_BYPASS_PATHS 是逗号分隔的前缀列表,命中后不调 AuthHub。
基础用法:HTTP 客户端
同步
from authhub_sdk import AuthhubClient
with AuthhubClient.from_env() as client:
# 登录(子平台前端或后端都可发起)
result = client.login("alice", "User@123456")
access_token = result["access_token"]
# 当前用户信息
me = client.me(access_token)
print(me.user_id, me.username, me.domain_roles)
# 每次业务请求前调一次(中间件已自动做了,这里仅演示)
outcome = client.authorize(
access_token,
method="GET",
path="/api/v1/projects/123",
)
if not outcome.allow:
raise PermissionError(outcome.reason)
异步
from authhub_sdk import AsyncAuthhubClient
async with AsyncAuthhubClient.from_env() as client:
outcome = await client.authorize(token, method="POST", path="/api/v1/orders")
显式传配置(不走 env):
from authhub_sdk import AuthhubClient
from authhub_sdk.config import AuthhubConfig
cfg = AuthhubConfig(base_url="http://authhub.internal:6001", app_code="cortexa")
client = AuthhubClient(config=cfg)
Django 接入
1. 安装中间件
settings.py:
INSTALLED_APPS = [
# ...
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.middleware.common.CommonMiddleware",
"corsheaders.middleware.CorsMiddleware", # 若用 corsheaders
"authhub_sdk.django.AuthhubMiddleware", # ← 放在业务中间件之前
# ...
]
# 与 from_env 等价;也可以省略改用环境变量
AUTHHUB_SDK = {
"base_url": "http://authhub.internal:6001",
"app_code": "cortexa",
"bypass_paths": ["/health/", "/static/", "/admin/login/"],
}
2. 在 view 里拿用户
中间件鉴权通过后会注入:
| 属性 | 类型 | 含义 |
|---|---|---|
request.authhub_outcome |
AuthorizeOutcome |
完整鉴权结果 |
request.authhub_user |
dict |
PhilVault /auth/me 返回的用户字典 |
request.authhub_user_id |
str |
用户 ObjectId |
request.authhub_access_token |
str |
透出本次请求的 token |
from django.http import JsonResponse
def get_my_projects(request):
user_id = request.authhub_user_id
return JsonResponse({"user_id": user_id})
3. 错误响应
| 场景 | HTTP | body |
|---|---|---|
| 缺/无效 Bearer token | 401 | {"detail": "Unauthorized: ...", "request_id": "..."} |
| AuthHub 决策为 deny | 403 | {"detail": <reason>, "decision": "deny", "mapping": {...}} |
| AuthHub 不可达 / 5xx | 502 | {"detail": "Auth backend unavailable"} |
| CORS preflight (OPTIONS) | — | 不鉴权,直接放行 |
FastAPI 接入
from fastapi import FastAPI, Depends
from authhub_sdk.fastapi import AuthhubContext, authhub_required
app = FastAPI()
guard = authhub_required() # 从 AUTHHUB_* env 读配置
@app.get("/api/v1/projects/{project_id}")
async def get_project(
project_id: str,
ctx: AuthhubContext = Depends(guard),
):
return {
"project_id": project_id,
"viewer": ctx.user_id,
"decision": ctx.outcome.decision,
}
按 view 粒度切换鉴权:把不需要鉴权的 view 不依赖 guard 即可。或者全局挂依赖:
app = FastAPI(dependencies=[Depends(guard)])
错误响应(标准 FastAPI HTTPException):
- 401
{"detail": "Unauthorized: missing bearer token"} - 403
{"detail": "<reason>"} - 502
{"detail": "Auth backend unavailable"}
登录链路(前端直调 AuthHub)
┌──────────┐ ┌──────────┐ ┌────────────┐
│ 子平台前端 │ ──login()──▶ │ AuthHub │ ──login()────▶ │ PhilVault │
│ │ ◀─token────── │ │ ◀─token─────── │ │
└──────────┘ └──────────┘ └────────────┘
│
│ Authorization: Bearer <token>
▼
┌──────────┐ ┌──────────┐ ┌────────────┐
│ 子平台后端 │ ◀──HTTP────── │ 中间件 │ ─authorize()─▶│ AuthHub │
│ (view) │ │ (SDK) │ │ /sdk/... │
└──────────┘ └──────────┘ └────────────┘
- 用户在子平台前端直接
POST /api/v2/auth/login到 AuthHub(不经过子平台后端) - 拿到
access_token后所有业务请求都带Authorization: Bearer <token> - 子平台后端的中间件收到请求 → 调
POST /api/v2/sdk/authorize→ 按allow字段放/拒 - token 过期:前端用
refresh_token调/api/v2/auth/refresh拿新 access_token
异常体系
from authhub_sdk import AuthhubError, AuthhubHTTPError, AuthhubUnauthorized
try:
client.authorize(token, method="GET", path="/api/v1/items")
except AuthhubUnauthorized:
# 401:token 无效或过期。让用户重新登录
...
except AuthhubHTTPError as exc:
# 其它 4xx/5xx。exc.status_code / exc.payload / exc.request_id
...
except AuthhubError:
# 网络层(超时、DNS、连接拒绝)
...
继承关系:
Exception
└── AuthhubError # 网络层 / SDK 本身错误
└── AuthhubHTTPError # 后端返回 4xx/5xx
└── AuthhubUnauthorized # 后端返回 401(特殊处理用)
行为约定
- 不缓存任何 token → user_id 信息。AuthHub 是单点 SOT,权限变更秒级生效
- OPTIONS 请求直接放行(CORS preflight 兼容)
- bypass_paths 走前缀匹配;不走鉴权但仍注入空
ctx(FastAPI) - 超时默认 5 秒;线上建议结合熔断和重试策略由调用方控制
- 调用
/sdk/authorize时若 AuthHub 不可达 → SDK 抛AuthhubError→ 中间件返回 502; AuthHub 决策为 deny → 中间件返回 403。两种情况要分清楚
调试
需要看每次鉴权的细节?AuthHub /sdk/authorize 返回包含:
{
"allow": false,
"decision": "deny",
"reason": "PhilVault 拒绝:用户 ... 在 cortexa 域对 rbac_cortexa::asset(read) 无授权",
"user": {"id": "...", "username": "..."},
"mapping": {"app_code": "cortexa", "method": "GET", "path": "/api/v1/asset/<id>", ...},
"philvault_resource_id": "rbac_cortexa::asset",
"action_code": "read"
}
在 Django view 里:print(request.authhub_outcome.raw) 看完整内容。
打开 AuthHub 「授权排查」页面(/diagnose),输入 user + method + path 可以
离线还原决策路径,不消耗 PhilVault 调用配额。
测试
cd authhub_sdk
pytest
22 个单测覆盖 client / Django 中间件 / FastAPI 依赖三个层,使用
httpx.MockTransport 模拟 AuthHub,零外部依赖。
版本
- 0.1.1 — 修正包名为
authhub-rbac-sdk,更新 README - 0.1.0 — 初版。后续兼容性以 SemVer 为准。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file authhub_rbac_sdk-0.1.1.tar.gz.
File metadata
- Download URL: authhub_rbac_sdk-0.1.1.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d2342230c54395b900db7b01b415180fbccdfec356427f83473eb8efea94e6a
|
|
| MD5 |
8284d0a1ff5c5b83aebd651516a77a64
|
|
| BLAKE2b-256 |
70d7727359d62190a6da61647b23483a56f0c94c866bcb3081eee2de2c51d10a
|
File details
Details for the file authhub_rbac_sdk-0.1.1-py3-none-any.whl.
File metadata
- Download URL: authhub_rbac_sdk-0.1.1-py3-none-any.whl
- Upload date:
- Size: 18.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1205bb6e6d396eb7b65ec49fd0e59168a6527ec6892e68e57a269dbf91bb99ed
|
|
| MD5 |
60117fb945d3290ef440b83748ec2ac7
|
|
| BLAKE2b-256 |
12527eda4ca13f40365fc9b12eb4a72605b4a00dcf4f20965ad23881157d0c52
|