指南:Python
第一步:从请求头中提取 Bearer 令牌
一个授权请求应该包含一个 Authorization
头,其内容为 Bearer <access_token>
。从请求头中提取授权令牌 (Authorization token):
"""requires-auth.py
"""
def get_auth_token():
auth = request.headers.get("Authorization", None)
if not auth:
raise Error({ code: 'auth.authorization_header_missing', status: 401 })
contents = auth.split()
if len(contents) < 2
raise Error({code: 'auth.authorization_token_invalid_format', status: 401})
elif contents[0] != 'Bearer'
raise Error({code: 'auth.authorization_token_type_not_supported', status: 401})
return contents[1]
第二步:令牌验证
为了演示,我们使用 Flask 应用和 jose 包来创建 require_auth 装饰器,以验证令牌的签名、过期状态和所需声明 (Claims)。
安装 python-jose
作为你的依赖
选择你在 Logto 中使用的加密算法。(默认是 ecdsa
)
pip install python-jose[ecdsa]
获取 Logto 的 OIDC 配置
你将需要一个 JWK 公钥集和令牌发行者 (Issuer) 来验证接收到的 JWS 令牌的签名和来源。所有最新的 Logto 授权配置可以在 https://<your-logto-domain>/oidc/.well-known/openid-configuration
找到。
例如,调用 https://tenant-id.logto.app/oidc/.well-known/openid-configuration
。并在响应体中找到以下两个字段:
{
"jwks_uri": "https://tenant-id.logto.app/oidc/jwks",
"issuer": "https://tenant-id.logto.app/oidc"
}
创建授权验证装饰器
注意:
如果你使用 基于角色的访问控制 (RBAC),还需要进行权限 (Scope) 验证。
"""requires-auth.py
"""
import json
from flask import request, _request_ctx_stack
from six.moves.urllib.request import urlopen
from functools import wraps
from jose import jwt
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
token = get_token_auth_header()
# 从 Logto 获取的 jwks_uri 端点
jwks_uri = urlopen('https://<your-logto-domain>/oidc/jwks')
# 从 Logto 获取的发行者 (Issuer)
issuer = 'https://<your-logto-domain>/oidc'
jwks = json.loads(jwks_uri.read())
try:
payload = jwt.decode(
token,
jwks,
# 与 jwks 一起检索到的 jwt 编码算法。默认是 ES384
algorithms=jwt.get_unverified_header(token).get('alg'),
# 在 Logto 中注册的 API 的资源指示器
audience='<your request listener resource indicator>',
issuer=issuer,
options={
'verify_at_hash': False
}
)
except Exception:
# 异常处理程序
raise Error({code: 'invalid_token', status: 401})
# 处理 payload 的自定义代码
_request_ctx_stack.top.user_id = payload.get('sub')
return f(*args, **kwargs)
return decorated
将装饰器应用于你的 API
from flask import Flask
from flask_cors import cross_origin
APP = Flask(__name__)
@APP.route("/user/info")
@cross_origin(headers=["Content-Type", "Authorization"])
@requires_auth
def api:
# 你的 API 逻辑