skyplatform-iam 1.0.1__py3-none-any.whl → 1.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- skyplatform_iam/__init__.py +77 -17
- skyplatform_iam/api.py +366 -0
- skyplatform_iam/config.py +173 -18
- skyplatform_iam/connect_agenterra_iam.py +291 -12
- skyplatform_iam/global_manager.py +272 -0
- skyplatform_iam/middleware.py +363 -24
- skyplatform_iam-1.0.5.dist-info/METADATA +461 -0
- skyplatform_iam-1.0.5.dist-info/RECORD +10 -0
- skyplatform_iam/auth_middleware.py +0 -201
- skyplatform_iam-1.0.1.dist-info/METADATA +0 -262
- skyplatform_iam-1.0.1.dist-info/RECORD +0 -9
- {skyplatform_iam-1.0.1.dist-info → skyplatform_iam-1.0.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: skyplatform-iam
|
|
3
|
+
Version: 1.0.5
|
|
4
|
+
Summary: SkyPlatform IAM认证SDK,提供FastAPI中间件和认证路由
|
|
5
|
+
Project-URL: Homepage, https://github.com/xinmayoujiang12621/agenterra_iam
|
|
6
|
+
Project-URL: Documentation, https://skyplatform-iam.readthedocs.io/
|
|
7
|
+
Project-URL: Repository, https://github.com/xinmayoujiang12621/agenterra_iam.git
|
|
8
|
+
Project-URL: Issues, https://github.com/xinmayoujiang12621/agenterra_iam/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/xinmayoujiang12621/agenterra_iam/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: x9 <xuanxienanxunmobao@gmail.com>
|
|
11
|
+
License: MIT
|
|
12
|
+
Keywords: authentication,fastapi,iam,middleware,skyplatform
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Framework :: FastAPI
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
25
|
+
Classifier: Topic :: Security
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Requires-Python: >=3.9
|
|
28
|
+
Requires-Dist: fastapi>=0.68.0
|
|
29
|
+
Requires-Dist: pydantic>=1.8.0
|
|
30
|
+
Requires-Dist: requests>=2.25.0
|
|
31
|
+
Requires-Dist: starlette>=0.14.0
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: black>=22.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: flake8>=4.0.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: httpx>=0.23.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: isort>=5.10.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: mypy>=0.950; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-asyncio>=0.18.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=6.0.0; extra == 'dev'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# SkyPlatform IAM SDK
|
|
43
|
+
|
|
44
|
+
SkyPlatform IAM认证SDK,提供FastAPI中间件和认证路由,简化第三方服务的认证集成。
|
|
45
|
+
|
|
46
|
+
## 功能特性
|
|
47
|
+
|
|
48
|
+
- 🔐 **FastAPI中间件**: 自动拦截请求进行Token验证和权限检查
|
|
49
|
+
- 🚀 **统一初始化**: 使用 `init_skyplatform_iam` 一键设置认证功能
|
|
50
|
+
- 🔧 **全局客户端**: 通过 `get_iam_client()` 在任何地方访问IAM功能
|
|
51
|
+
- 🛡️ **懒加载支持**: 使用 `create_lazy_iam_client()` 解决初始化顺序问题
|
|
52
|
+
- ⚙️ **灵活配置**: 支持环境变量和代码配置
|
|
53
|
+
- 🛡️ **白名单机制**: 支持配置无需认证的路径
|
|
54
|
+
- 🔧 **完整兼容**: 基于现有ConnectAgenterraIam类,保持完全兼容
|
|
55
|
+
- 📝 **类型提示**: 完整的TypeScript风格类型提示
|
|
56
|
+
- 🚨 **异常处理**: 完善的错误处理和自定义异常
|
|
57
|
+
|
|
58
|
+
## 快速开始
|
|
59
|
+
|
|
60
|
+
### 安装
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install skyplatform-iam
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 环境变量配置
|
|
67
|
+
|
|
68
|
+
创建 `.env` 文件或设置环境变量:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
AGENTERRA_IAM_HOST=https://your-iam-host.com
|
|
72
|
+
AGENTERRA_SERVER_NAME=your-server-name
|
|
73
|
+
AGENTERRA_ACCESS_KEY=your-access-key
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 基本使用
|
|
77
|
+
|
|
78
|
+
#### 方式1:统一初始化(推荐)
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from fastapi import FastAPI
|
|
82
|
+
from skyplatform_iam import init_skyplatform_iam, AuthConfig
|
|
83
|
+
|
|
84
|
+
app = FastAPI()
|
|
85
|
+
|
|
86
|
+
# 一键设置认证中间件和路由
|
|
87
|
+
config = AuthConfig(
|
|
88
|
+
agenterra_iam_host="http://127.0.0.1:5001",
|
|
89
|
+
server_name="Agenterra_shop",
|
|
90
|
+
access_key="zYqZwWEAW7iCi6qjVcVlnjrK5GxAkmk8"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
init_skyplatform_iam(app, config)
|
|
94
|
+
|
|
95
|
+
@app.get("/protected")
|
|
96
|
+
async def protected_endpoint(request):
|
|
97
|
+
# 获取用户信息(由中间件自动设置)
|
|
98
|
+
user = request.state.user
|
|
99
|
+
return {"message": "访问成功", "user": user}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### 方式2:从环境变量初始化
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from fastapi import FastAPI
|
|
106
|
+
from skyplatform_iam import init_skyplatform_iam, AuthConfig
|
|
107
|
+
|
|
108
|
+
app = FastAPI()
|
|
109
|
+
|
|
110
|
+
# 从环境变量加载配置
|
|
111
|
+
config = AuthConfig.from_env()
|
|
112
|
+
init_skyplatform_iam(app, config)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### 方式3:自定义配置
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from skyplatform_iam import init_skyplatform_iam, AuthConfig
|
|
119
|
+
|
|
120
|
+
# 自定义配置
|
|
121
|
+
config = AuthConfig(
|
|
122
|
+
agenterra_iam_host="https://your-iam-host.com",
|
|
123
|
+
server_name="your-server-name",
|
|
124
|
+
access_key="your-access-key",
|
|
125
|
+
whitelist_paths=[
|
|
126
|
+
"/docs", "/redoc", "/openapi.json",
|
|
127
|
+
"/health", "/public",
|
|
128
|
+
"/auth/register", "/auth/login"
|
|
129
|
+
],
|
|
130
|
+
enable_debug=True
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
init_skyplatform_iam(app, config)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 在业务代码中使用IAM客户端
|
|
137
|
+
|
|
138
|
+
初始化SDK后,您可以在任何地方使用IAM客户端:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from typing import Optional, Dict, Any
|
|
142
|
+
import logging
|
|
143
|
+
from skyplatform_iam import get_iam_client
|
|
144
|
+
|
|
145
|
+
logger = logging.getLogger(__name__)
|
|
146
|
+
|
|
147
|
+
class AuthService:
|
|
148
|
+
"""认证服务类,提供统一的登录验证功能"""
|
|
149
|
+
|
|
150
|
+
def authenticate_user(self, username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
151
|
+
"""验证用户账号密码
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
username: 用户名
|
|
155
|
+
password: 密码
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
验证成功返回IAM响应数据,失败返回None
|
|
159
|
+
"""
|
|
160
|
+
try:
|
|
161
|
+
result = get_iam_client().login_with_password(
|
|
162
|
+
username=username,
|
|
163
|
+
password=password
|
|
164
|
+
)
|
|
165
|
+
return result
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logger.error(f"用户认证失败: {str(e)}")
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
def get_user_info(self, user_id: str) -> Optional[Dict[str, Any]]:
|
|
171
|
+
"""获取用户信息"""
|
|
172
|
+
try:
|
|
173
|
+
return get_iam_client().get_user_by_id(user_id)
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.error(f"获取用户信息失败: {str(e)}")
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
# 创建全局认证服务实例
|
|
179
|
+
auth_service = AuthService()
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 避免初始化顺序问题
|
|
183
|
+
|
|
184
|
+
如果您需要在模块级别使用IAM客户端,推荐使用懒加载方式:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from skyplatform_iam import create_lazy_iam_client
|
|
188
|
+
|
|
189
|
+
# 在模块级别安全使用(推荐用于解决导入顺序问题)
|
|
190
|
+
iam_client = create_lazy_iam_client()
|
|
191
|
+
|
|
192
|
+
class AuthService:
|
|
193
|
+
def __init__(self):
|
|
194
|
+
# 使用懒加载客户端,避免初始化顺序问题
|
|
195
|
+
self.iam_client = create_lazy_iam_client()
|
|
196
|
+
|
|
197
|
+
def authenticate_user(self, username: str, password: str):
|
|
198
|
+
# 只有在实际调用时才会初始化IAM客户端
|
|
199
|
+
return self.iam_client.login_with_password(username, password)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### 传统方式(已废弃,仅供参考)
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
# ⚠️ 已废弃:请使用 init_skyplatform_iam 替代
|
|
206
|
+
from skyplatform_iam import setup_auth
|
|
207
|
+
|
|
208
|
+
app = FastAPI()
|
|
209
|
+
setup_auth(app) # 不推荐使用
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## 内置方法
|
|
213
|
+
|
|
214
|
+
ConnectAgenterraIam 类提供以下内置方法:
|
|
215
|
+
|
|
216
|
+
### 用户认证相关
|
|
217
|
+
- `register(cred_type, cred_value, password=None, nickname=None, avatar_url=None)` - 用户注册
|
|
218
|
+
- `login_with_password(cred_type, cred_value, password, ip_address=None, user_agent=None)` - 账号密码登录
|
|
219
|
+
- `login_without_password(cred_type, cred_value, ip_address=None, user_agent=None)` - 免密登录
|
|
220
|
+
- `logout(token)` - 用户登出
|
|
221
|
+
|
|
222
|
+
### Token管理
|
|
223
|
+
- `verify_token(token, api, method, server_ak="", server_sk="")` - Token验证和权限检查
|
|
224
|
+
- `refresh_token(refresh_token)` - 刷新Token
|
|
225
|
+
|
|
226
|
+
### 密码管理
|
|
227
|
+
- `reset_password(cred_type, cred_value, new_password)` - 重置密码
|
|
228
|
+
|
|
229
|
+
### 角色管理
|
|
230
|
+
- `assign_role_to_user(user_id, role_name)` - 为用户分配角色
|
|
231
|
+
|
|
232
|
+
### 用户信息
|
|
233
|
+
- `get_userinfo_by_token(token)` - 通过Token获取用户信息
|
|
234
|
+
- `get_user_by_credential(cred_type, cred_value)` - 通过凭证获取用户信息
|
|
235
|
+
|
|
236
|
+
### 用户配置
|
|
237
|
+
- `add_custom_config(user_id, config_key, config_value)` - 添加用户自定义配置
|
|
238
|
+
- `get_custom_configs(user_id, config_key=None)` - 获取用户自定义配置
|
|
239
|
+
- `delete_custom_config(user_id, config_key)` - 删除用户自定义配置
|
|
240
|
+
|
|
241
|
+
### 凭证管理
|
|
242
|
+
- `merge_credential(primary_cred_type, primary_cred_value, secondary_cred_type, secondary_cred_value)` - 凭证合并
|
|
243
|
+
|
|
244
|
+
### 使用示例
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
from skyplatform_iam import get_iam_client
|
|
248
|
+
|
|
249
|
+
# 获取IAM客户端
|
|
250
|
+
iam_client = get_iam_client()
|
|
251
|
+
|
|
252
|
+
# 用户注册
|
|
253
|
+
result = iam_client.register(
|
|
254
|
+
cred_type="username",
|
|
255
|
+
cred_value="testuser",
|
|
256
|
+
password="password123",
|
|
257
|
+
nickname="测试用户"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# 用户登录
|
|
261
|
+
response = iam_client.login_with_password(
|
|
262
|
+
cred_type="username",
|
|
263
|
+
cred_value="testuser",
|
|
264
|
+
password="password123"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Token验证
|
|
268
|
+
user_info = iam_client.verify_token(
|
|
269
|
+
token="user_token",
|
|
270
|
+
api="/api/protected",
|
|
271
|
+
method="GET"
|
|
272
|
+
)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## 中间件功能
|
|
276
|
+
|
|
277
|
+
### 自动Token验证
|
|
278
|
+
|
|
279
|
+
中间件会自动:
|
|
280
|
+
1. 检查请求路径是否在白名单中
|
|
281
|
+
2. 从请求头提取Authorization Token
|
|
282
|
+
3. 调用IAM服务验证Token和权限
|
|
283
|
+
4. 将用户信息设置到 `request.state.user`
|
|
284
|
+
|
|
285
|
+
### 白名单配置
|
|
286
|
+
|
|
287
|
+
默认白名单路径:
|
|
288
|
+
- `/docs`, `/redoc`, `/openapi.json` - API文档
|
|
289
|
+
- `/health` - 健康检查
|
|
290
|
+
- `/auth/*` - 认证相关接口
|
|
291
|
+
|
|
292
|
+
添加自定义白名单:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
config = AuthConfig.from_env()
|
|
296
|
+
config.add_whitelist_path("/public")
|
|
297
|
+
config.add_whitelist_path("/status")
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 获取用户信息
|
|
301
|
+
|
|
302
|
+
在受保护的路由中获取用户信息:
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
@app.get("/user-profile")
|
|
306
|
+
async def get_user_profile(request):
|
|
307
|
+
if hasattr(request.state, 'user'):
|
|
308
|
+
user = request.state.user
|
|
309
|
+
return {
|
|
310
|
+
"user_id": user["user_id"],
|
|
311
|
+
"username": user["username"],
|
|
312
|
+
"session_id": user["session_id"]
|
|
313
|
+
}
|
|
314
|
+
else:
|
|
315
|
+
raise HTTPException(status_code=401, detail="未认证")
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## 异常处理
|
|
319
|
+
|
|
320
|
+
SDK提供完整的异常处理:
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
from skyplatform_iam.exceptions import (
|
|
324
|
+
AuthenticationError, # 认证失败
|
|
325
|
+
AuthorizationError, # 权限不足
|
|
326
|
+
TokenExpiredError, # Token过期
|
|
327
|
+
TokenInvalidError, # Token无效
|
|
328
|
+
ConfigurationError, # 配置错误
|
|
329
|
+
IAMServiceError, # IAM服务错误
|
|
330
|
+
NetworkError # 网络错误
|
|
331
|
+
)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## 配置选项
|
|
335
|
+
|
|
336
|
+
### AuthConfig参数
|
|
337
|
+
|
|
338
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
339
|
+
|------|------|------|------|
|
|
340
|
+
| `agenterra_iam_host` | str | ✓ | IAM服务地址 |
|
|
341
|
+
| `server_name` | str | ✓ | 服务名称 |
|
|
342
|
+
| `access_key` | str | ✓ | 访问密钥 |
|
|
343
|
+
| `whitelist_paths` | List[str] | ✗ | 白名单路径 |
|
|
344
|
+
| `token_header` | str | ✗ | Token请求头名称(默认:Authorization) |
|
|
345
|
+
| `token_prefix` | str | ✗ | Token前缀(默认:Bearer ) |
|
|
346
|
+
| `enable_debug` | bool | ✗ | 启用调试模式 |
|
|
347
|
+
|
|
348
|
+
## 开发和测试
|
|
349
|
+
|
|
350
|
+
### 运行测试
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
# 安装开发依赖
|
|
354
|
+
pip install -e ".[dev]"
|
|
355
|
+
|
|
356
|
+
# 运行测试
|
|
357
|
+
python examples/test_sdk.py
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### 运行示例
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
# 启动示例应用
|
|
364
|
+
python examples/basic_usage.py
|
|
365
|
+
|
|
366
|
+
# 访问 http://localhost:8000/docs 查看API文档
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## 兼容性
|
|
370
|
+
|
|
371
|
+
- Python 3.8+
|
|
372
|
+
- FastAPI 0.68.0+
|
|
373
|
+
- 完全兼容现有的 `ConnectAgenterraIam` 类
|
|
374
|
+
|
|
375
|
+
## 许可证
|
|
376
|
+
|
|
377
|
+
MIT License
|
|
378
|
+
|
|
379
|
+
## 贡献
|
|
380
|
+
|
|
381
|
+
欢迎提交Issue和Pull Request!
|
|
382
|
+
|
|
383
|
+
## 迁移指南
|
|
384
|
+
|
|
385
|
+
### 从旧版本迁移到新版本
|
|
386
|
+
|
|
387
|
+
如果您正在使用旧版本的 `setup_auth` 函数,请按照以下步骤迁移:
|
|
388
|
+
|
|
389
|
+
#### 旧版本代码:
|
|
390
|
+
```python
|
|
391
|
+
from skyplatform_iam import setup_auth
|
|
392
|
+
setup_auth(app)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### 新版本代码:
|
|
396
|
+
```python
|
|
397
|
+
from skyplatform_iam import init_skyplatform_iam, AuthConfig
|
|
398
|
+
|
|
399
|
+
config = AuthConfig.from_env() # 或者手动创建配置
|
|
400
|
+
init_skyplatform_iam(app, config)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### 常见问题解决
|
|
404
|
+
|
|
405
|
+
#### 1. 初始化顺序问题
|
|
406
|
+
|
|
407
|
+
**错误示例:**
|
|
408
|
+
```python
|
|
409
|
+
# ❌ 错误:在模块导入时直接调用
|
|
410
|
+
from skyplatform_iam import get_iam_client
|
|
411
|
+
iam_client = get_iam_client() # 可能导致初始化错误
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**正确示例:**
|
|
415
|
+
```python
|
|
416
|
+
# ✅ 正确:在函数内部调用
|
|
417
|
+
from skyplatform_iam import get_iam_client
|
|
418
|
+
|
|
419
|
+
def some_function():
|
|
420
|
+
iam_client = get_iam_client() # 在函数内部调用
|
|
421
|
+
return iam_client.login_with_password(...)
|
|
422
|
+
|
|
423
|
+
# 或者使用懒加载
|
|
424
|
+
from skyplatform_iam import create_lazy_iam_client
|
|
425
|
+
iam_client = create_lazy_iam_client() # 安全的模块级别使用
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
#### 2. 配置管理
|
|
429
|
+
|
|
430
|
+
推荐使用环境变量管理配置:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
# .env 文件
|
|
434
|
+
AGENTERRA_IAM_HOST=http://127.0.0.1:5001
|
|
435
|
+
AGENTERRA_SERVER_NAME=your_service_name
|
|
436
|
+
AGENTERRA_ACCESS_KEY=your_access_key
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
# Python 代码
|
|
441
|
+
from skyplatform_iam import init_skyplatform_iam, AuthConfig
|
|
442
|
+
|
|
443
|
+
config = AuthConfig.from_env()
|
|
444
|
+
init_skyplatform_iam(app, config)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## 更新日志
|
|
448
|
+
|
|
449
|
+
### v2.0.0
|
|
450
|
+
- 🚀 新增 `init_skyplatform_iam` 统一初始化函数
|
|
451
|
+
- 🔧 新增 `get_iam_client` 全局客户端获取函数
|
|
452
|
+
- 🛡️ 新增 `create_lazy_iam_client` 懒加载客户端
|
|
453
|
+
- 📚 改进文档和使用示例
|
|
454
|
+
- ⚠️ 废弃 `setup_auth` 函数(保持向后兼容)
|
|
455
|
+
- 🐛 修复模块导入顺序问题
|
|
456
|
+
|
|
457
|
+
### v1.0.0
|
|
458
|
+
- 初始版本发布
|
|
459
|
+
- 提供FastAPI中间件和认证路由
|
|
460
|
+
- 支持完整的认证功能
|
|
461
|
+
- 兼容现有ConnectAgenterraIam类
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
skyplatform_iam/__init__.py,sha256=eR2309T98s1UlxbxWBJEXTQkFV0ns-Dtfej5dYGkP-4,4000
|
|
2
|
+
skyplatform_iam/api.py,sha256=O-E8S5A1_IBQifEE2ZiuQFKyPgrCsKjFVdAifHzBpDs,11617
|
|
3
|
+
skyplatform_iam/config.py,sha256=ntIIuIsLWO4VD7-J4umRkXwiXUzZxJgBMykslsuqvQI,8990
|
|
4
|
+
skyplatform_iam/connect_agenterra_iam.py,sha256=m4JgfZuCPue-VaG4kEpR9oALoiX3uRTCQ4q3bZB7iyw,42097
|
|
5
|
+
skyplatform_iam/exceptions.py,sha256=Rt55QIzVK1F_kn6yzKQKKakD6PZDFdPLCGaCphKKms8,2166
|
|
6
|
+
skyplatform_iam/global_manager.py,sha256=EBlg9Z73UOJCWrG-o1AOQL5P96nWEMNEIdpm-femTds,9295
|
|
7
|
+
skyplatform_iam/middleware.py,sha256=nnr9CUEIB8gcLs5NsdDHVkB5AoB-pjzl4edlK1rXSuk,20059
|
|
8
|
+
skyplatform_iam-1.0.5.dist-info/METADATA,sha256=7xNHv_W28e9AMgV6W8ArRSw8f707TL8tdcGeZQHSnCM,12658
|
|
9
|
+
skyplatform_iam-1.0.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
10
|
+
skyplatform_iam-1.0.5.dist-info/RECORD,,
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
from fastapi import Request, HTTPException, status
|
|
2
|
-
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
3
|
-
from typing import Optional, Dict
|
|
4
|
-
import jwt
|
|
5
|
-
|
|
6
|
-
from .connect_agenterra_iam import ConnectAgenterraIam
|
|
7
|
-
from .config import AuthConfig
|
|
8
|
-
import logging
|
|
9
|
-
|
|
10
|
-
logger = logging.getLogger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class AuthMiddleware:
|
|
14
|
-
def __init__(self, auth_config: Optional[AuthConfig] = None):
|
|
15
|
-
self.security = HTTPBearer(auto_error=False)
|
|
16
|
-
self.iam_client = ConnectAgenterraIam()
|
|
17
|
-
self.auth_config = auth_config
|
|
18
|
-
|
|
19
|
-
def is_path_whitelisted(self, path: str) -> bool:
|
|
20
|
-
"""
|
|
21
|
-
检查路径是否在白名单中
|
|
22
|
-
"""
|
|
23
|
-
if not self.auth_config:
|
|
24
|
-
return False
|
|
25
|
-
return self.auth_config.is_path_whitelisted(path)
|
|
26
|
-
|
|
27
|
-
async def verify_token(self, request: Request):
|
|
28
|
-
# 通过token, server_ak, server_sk判断是否有权限
|
|
29
|
-
api_path = request.url.path
|
|
30
|
-
|
|
31
|
-
# 首先检查路径是否在白名单中
|
|
32
|
-
if self.is_path_whitelisted(api_path):
|
|
33
|
-
logger.info(f"路径 {api_path} 在白名单中,跳过IAM鉴权")
|
|
34
|
-
return True
|
|
35
|
-
|
|
36
|
-
credentials: HTTPAuthorizationCredentials = await self.security(request)
|
|
37
|
-
method = request.method
|
|
38
|
-
|
|
39
|
-
server_ak = request.headers.get("SERVER-AK", "")
|
|
40
|
-
server_sk = request.headers.get("SERVER-SK", "")
|
|
41
|
-
|
|
42
|
-
token = ""
|
|
43
|
-
if credentials is not None:
|
|
44
|
-
token = credentials.credentials
|
|
45
|
-
user_info_by_iam = self.iam_client.verify_token(token, api_path, method, server_ak, server_sk)
|
|
46
|
-
if user_info_by_iam:
|
|
47
|
-
return True
|
|
48
|
-
return False
|
|
49
|
-
|
|
50
|
-
async def get_current_user(self, request: Request) -> Optional[Dict]:
|
|
51
|
-
"""获取当前用户信息"""
|
|
52
|
-
try:
|
|
53
|
-
# 直接调用verify_token方法进行token验证
|
|
54
|
-
if not await self.verify_token(request):
|
|
55
|
-
return None
|
|
56
|
-
|
|
57
|
-
# 获取token用于后续用户信息获取
|
|
58
|
-
credentials: HTTPAuthorizationCredentials = await self.security(request)
|
|
59
|
-
token = credentials.credentials
|
|
60
|
-
|
|
61
|
-
# 直接解析JWT token获取payload
|
|
62
|
-
payload = self.decode_jwt_token(token)
|
|
63
|
-
if not payload:
|
|
64
|
-
logger.error("JWT token解析失败")
|
|
65
|
-
return None
|
|
66
|
-
|
|
67
|
-
# 从payload中提取用户信息
|
|
68
|
-
iam_user_id = payload.get("sub") # JWT标准中用户ID存储在sub字段
|
|
69
|
-
username = None
|
|
70
|
-
|
|
71
|
-
# 解析新的凭证信息结构
|
|
72
|
-
all_credentials = payload.get("all_credentials", [])
|
|
73
|
-
total_credentials = payload.get("total_credentials", 0)
|
|
74
|
-
|
|
75
|
-
# 从all_credentials中提取username(向后兼容)
|
|
76
|
-
for cred in all_credentials:
|
|
77
|
-
if cred.get("type") == "username":
|
|
78
|
-
username = cred.get("value")
|
|
79
|
-
break
|
|
80
|
-
|
|
81
|
-
# 向后兼容性:如果没有all_credentials,尝试从payload的其他字段构建
|
|
82
|
-
if not all_credentials:
|
|
83
|
-
credentials_list = []
|
|
84
|
-
# 检查payload中是否有直接的username字段
|
|
85
|
-
if payload.get("username"):
|
|
86
|
-
username = payload.get("username")
|
|
87
|
-
credentials_list.append({"type": "username", "value": username})
|
|
88
|
-
if payload.get("email"):
|
|
89
|
-
credentials_list.append({"type": "email", "value": payload.get("email")})
|
|
90
|
-
if payload.get("phone"):
|
|
91
|
-
credentials_list.append({"type": "phone", "value": payload.get("phone")})
|
|
92
|
-
all_credentials = credentials_list
|
|
93
|
-
total_credentials = len(credentials_list)
|
|
94
|
-
|
|
95
|
-
if not username:
|
|
96
|
-
return None
|
|
97
|
-
|
|
98
|
-
# 构建用户信息字典
|
|
99
|
-
user_info = {
|
|
100
|
-
"id": iam_user_id,
|
|
101
|
-
"username": username,
|
|
102
|
-
"all_credentials": all_credentials,
|
|
103
|
-
"total_credentials": total_credentials,
|
|
104
|
-
"microservice": payload.get("microservice") # 添加微服务信息
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
# 向后兼容:添加传统字段映射
|
|
108
|
-
for cred in all_credentials:
|
|
109
|
-
if cred.get("type") == "email":
|
|
110
|
-
user_info["email"] = cred.get("value")
|
|
111
|
-
elif cred.get("type") == "phone":
|
|
112
|
-
user_info["phone"] = cred.get("value")
|
|
113
|
-
elif cred.get("type") == "username" and not user_info.get("username"):
|
|
114
|
-
user_info["username"] = cred.get("value")
|
|
115
|
-
|
|
116
|
-
# 统计凭证类型分布
|
|
117
|
-
cred_types = [cred.get("type") for cred in all_credentials]
|
|
118
|
-
cred_type_count = {cred_type: cred_types.count(cred_type) for cred_type in set(cred_types)}
|
|
119
|
-
|
|
120
|
-
logger.info(
|
|
121
|
-
f"用户认证成功: user_id={iam_user_id}, username={username}, 凭证数量={total_credentials}, 凭证类型分布={cred_type_count}")
|
|
122
|
-
logger.debug(f"JWT payload: {payload}")
|
|
123
|
-
|
|
124
|
-
# 将用户信息添加到请求状态中
|
|
125
|
-
request.state.user = user_info
|
|
126
|
-
return user_info
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
except HTTPException as e:
|
|
130
|
-
print(403)
|
|
131
|
-
logger.error(f"获取当前用户信息失败: {str(e)}")
|
|
132
|
-
# 重新抛出HTTP异常(403权限不足)
|
|
133
|
-
return None
|
|
134
|
-
except Exception as e:
|
|
135
|
-
logger.error(f"获取当前用户信息失败: {str(e)}")
|
|
136
|
-
return None
|
|
137
|
-
|
|
138
|
-
async def require_auth(self, request: Request) -> Dict:
|
|
139
|
-
"""要求用户必须登录"""
|
|
140
|
-
try:
|
|
141
|
-
user_info = await self.get_current_user(request)
|
|
142
|
-
if not user_info:
|
|
143
|
-
raise HTTPException(
|
|
144
|
-
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
145
|
-
detail="需要登录认证",
|
|
146
|
-
headers={"WWW-Authenticate": "Bearer"},
|
|
147
|
-
)
|
|
148
|
-
return user_info
|
|
149
|
-
except HTTPException:
|
|
150
|
-
# 重新抛出HTTP异常(可能是403权限不足或401未认证)
|
|
151
|
-
raise
|
|
152
|
-
|
|
153
|
-
async def optional_auth(self, request: Request) -> Optional[Dict]:
|
|
154
|
-
"""可选的用户认证(不强制要求登录)"""
|
|
155
|
-
try:
|
|
156
|
-
return await self.get_current_user(request)
|
|
157
|
-
except HTTPException:
|
|
158
|
-
# 对于可选认证,如果是403权限不足,仍然抛出异常
|
|
159
|
-
# 如果是401未认证,返回None
|
|
160
|
-
raise
|
|
161
|
-
|
|
162
|
-
def decode_jwt_token(self, token: str) -> Optional[Dict]:
|
|
163
|
-
"""直接解析JWT token获取payload"""
|
|
164
|
-
try:
|
|
165
|
-
# 不验证签名,只解析payload(因为token已经通过verify_token验证过)
|
|
166
|
-
decoded_payload = jwt.decode(token, options={"verify_signature": False})
|
|
167
|
-
logger.debug(f"JWT token解析成功: {decoded_payload}")
|
|
168
|
-
return decoded_payload
|
|
169
|
-
except jwt.InvalidTokenError as e:
|
|
170
|
-
logger.error(f"JWT token解析失败: {str(e)}")
|
|
171
|
-
return None
|
|
172
|
-
except Exception as e:
|
|
173
|
-
logger.error(f"JWT token解析异常: {str(e)}")
|
|
174
|
-
return None
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
# 创建全局认证中间件实例
|
|
178
|
-
auth_middleware = AuthMiddleware()
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def setup_auth_middleware(auth_config: AuthConfig) -> None:
|
|
182
|
-
"""
|
|
183
|
-
设置认证中间件配置
|
|
184
|
-
|
|
185
|
-
Args:
|
|
186
|
-
auth_config: 认证配置实例,包含白名单路径等配置
|
|
187
|
-
"""
|
|
188
|
-
global auth_middleware
|
|
189
|
-
auth_middleware = AuthMiddleware(auth_config)
|
|
190
|
-
logger.info(f"认证中间件已配置,白名单路径数量: {len(auth_config.get_whitelist_paths())}")
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
# 便捷的依赖函数
|
|
194
|
-
async def get_current_user(request: Request) -> Dict:
|
|
195
|
-
"""获取当前用户的依赖函数"""
|
|
196
|
-
return await auth_middleware.require_auth(request)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
async def get_optional_user(request: Request) -> Optional[Dict]:
|
|
200
|
-
"""获取可选当前用户的依赖函数"""
|
|
201
|
-
return await auth_middleware.optional_auth(request)
|