Python 接入指南
使用通用 OAuth 2.1 库(authlib)接入 UserMatrix,支持 Flask/Django/FastAPI。
UM 目前未提供 Python 专属 SDK,但 UM 是标准 OAuth 2.1 + OIDC 服务,可直接用通用库 authlib 接入。下方示例适用于 Flask / Django / FastAPI。
安装
pip install authlib httpx
OIDC Discovery 配置
authlib 可自动从 OIDC Discovery 端点读取所有配置:
from authlib.integrations.flask_client import OAuth
oauth = OAuth()
oauth.register(
name='um',
server_metadata_url='https://um.yunjii.cn/.well-known/openid-configuration',
client_id='your_appid',
client_secret='your_appkey',
client_kwargs={'scope': 'openid profile'},
)
Flask 集成示例
from flask import Flask, session, redirect, url_for, request, jsonify
from authlib.integrations.flask_client import OAuth
app = Flask(__name__)
app.secret_key = 'your-secret-key'
oauth = OAuth(app)
oauth.register(
name='um',
server_metadata_url='https://um.yunjii.cn/.well-known/openid-configuration',
client_id='your_appid',
client_secret='your_appkey',
client_kwargs={'scope': 'openid profile'},
)
@app.route('/login')
def login():
redirect_uri = url_for('callback', _external=True)
return oauth.um.authorize_redirect(redirect_uri)
@app.route('/callback')
def callback():
token = oauth.um.authorize_access_token()
# token 结构:
# {
# 'access_token': 'eyJ...',
# 'token_type': 'Bearer',
# 'expires_in': 604800,
# 'refresh_token': 'rt_xxx...',
# 'user': {'id': 123, 'openid': 'um_xxx', 'nickname': '张三', ...}
# }
session['access_token'] = token['access_token']
session['refresh_token'] = token['refresh_token']
return redirect('/dashboard')
@app.route('/user')
def user():
access_token = session.get('access_token')
if not access_token:
return redirect('/login')
# 获取 OIDC UserInfo
userinfo = oauth.um.userinfo(token={'access_token': access_token})
return jsonify(userinfo)
# => {sub, name, nickname, picture, email, login_type, ...}
@app.route('/refresh')
def refresh():
refresh_token = session.get('refresh_token')
if not refresh_token:
return redirect('/login')
# 刷新 token(轮换制,旧 refresh_token 失效)
new_token = oauth.um.fetch_access_token(
grant_type='refresh_token',
refresh_token=refresh_token,
)
session['access_token'] = new_token['access_token']
session['refresh_token'] = new_token['refresh_token']
return jsonify({'msg': 'token refreshed'})
Django 集成示例
# views.py
from django.shortcuts import redirect
from django.http import JsonResponse
from django.conf import settings
from django.contrib.sessions.backends.db import SessionStore
from authlib.integrations.django_client import OAuth
oauth = OAuth()
oauth.register(
name='um',
server_metadata_url='https://um.yunjii.cn/.well-known/openid-configuration',
client_id=settings.UM_APPID,
client_secret=settings.UM_APPKEY,
client_kwargs={'scope': 'openid profile'},
)
def login(request):
redirect_uri = settings.UM_CALLBACK_URL
return oauth.um.authorize_redirect(request, redirect_uri)
def callback(request):
token = oauth.um.authorize_access_token(request)
request.session['access_token'] = token['access_token']
request.session['refresh_token'] = token['refresh_token']
return redirect('/dashboard')
def user_info(request):
access_token = request.session.get('access_token')
if not access_token:
return redirect('/login')
userinfo = oauth.um.userinfo(token={'access_token': access_token})
return JsonResponse(userinfo)
FastAPI 集成示例
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import RedirectResponse
from authlib.integrations.starlette_client import OAuth
app = FastAPI()
oauth = OAuth()
oauth.register(
name='um',
server_metadata_url='https://um.yunjii.cn/.well-known/openid-configuration',
client_id='your_appid',
client_secret='your_appkey',
client_kwargs={'scope': 'openid profile'},
)
@app.get('/login')
async def login(request: Request):
redirect_uri = request.url_for('callback')
return await oauth.um.authorize_redirect(request, redirect_uri)
@app.get('/callback')
async def callback(request: Request):
token = await oauth.um.authorize_access_token(request)
request.session['access_token'] = token['access_token']
request.session['refresh_token'] = token['refresh_token']
return RedirectResponse(url='/dashboard')
@app.get('/user')
async def user(request: Request):
access_token = request.session.get('access_token')
if not access_token:
return RedirectResponse(url='/login')
userinfo = await oauth.um.userinfo(token={'access_token': access_token})
return userinfo
手动调用(不使用 authlib)
如不想引入 authlib,可用标准 requests 库手动调用 OAuth 2.1 端点:
import requests
import secrets
import hashlib
import base64
UM_BASE = 'https://um.yunjii.cn'
APPID = 'your_appid'
APPKEY = 'your_appkey'
CALLBACK = 'https://your-app.com/callback'
def generate_pkce():
verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode().rstrip('=')
challenge = base64.urlsafe_b64encode(
hashlib.sha256(verifier.encode()).digest()
).decode().rstrip('=')
return verifier, challenge
def exchange_token(code, code_verifier):
resp = requests.post(f'{UM_BASE}/oauth/token.php', data={
'grant_type': 'authorization_code',
'code': code,
'client_id': APPID,
'client_secret': APPKEY,
'redirect_uri': CALLBACK,
'code_verifier': code_verifier,
})
return resp.json()
def get_userinfo(access_token):
resp = requests.get(f'{UM_BASE}/oauth/userinfo.php', headers={
'Authorization': f'Bearer {access_token}',
})
return resp.json()
def refresh_token(refresh_token):
resp = requests.post(f'{UM_BASE}/oauth/token.php', data={
'grant_type': 'refresh_token',
'refresh_token': refresh_token,
'client_id': APPID,
'client_secret': APPKEY,
})
return resp.json()
UM OAuth 2.1 端点使用 client_secret_post 鉴权方式,token 响应 code: 0 表示成功。详见 OAuth 2.1 API 参考。