fix: 小程序服务号绑定先打开门户域名再跳转微信授权。

web-view 不能直接打开 open.weixin.qq.com,移动端改为打开已配置的业务域名中转页。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-10-29 01:44:40 +08:00
1 parent 459b820f95
commit e7c1762113
7 files changed
+137 -8

No files matched your search

+50
View File
@@ -1,5 +1,6 @@
"""本人服务号绑定;OAuth 回调仅暂存候选,不直接绑定、不授予门户登录。"""
from html import escape
import json
from pathlib import Path
import secrets
from typing import Literal, Optional
@@ -49,6 +50,17 @@ def remove(user: dict = Depends(get_current_user), db: Session = Depends(get_db)
return success_response(svc.unbind(db, user))
@router.get("/public/wechat-notices/oauth/launch", response_class=HTMLResponse)
def oauth_launch(state: str = Query(default="")):
"""小程序 web-view 入口。先由业务域名打开本页,再跳到微信授权。"""
try:
target = svc.resolve_mobile_authorize_target(state)
except HTTPException as exc:
message = exc.detail if isinstance(exc.detail, str) else "授权页面打开失败,请返回小程序重试"
return _binding_message_page(message)
return _oauth_redirect_page(target)
@router.get("/public/wechat-notices/oauth/callback", response_class=HTMLResponse)
def callback(state: str = Query(min_length=20, max_length=100), code: str = Query(min_length=1, max_length=512)):
try:
@@ -68,6 +80,44 @@ def callback(state: str = Query(min_length=20, max_length=100), code: str = Quer
)
def _page_headers(nonce):
return {
"Cache-Control": "no-store", "Referrer-Policy": "no-referrer",
"Content-Security-Policy": (
"default-src 'none'; frame-ancestors 'none'; base-uri 'none'; "
f"script-src 'nonce-{nonce}'; style-src 'nonce-{nonce}'"
),
}
def _binding_message_page(message):
nonce = secrets.token_urlsafe(24)
return HTMLResponse(
'<!doctype html><html lang="zh-CN"><meta charset="utf-8">'
'<meta name="viewport" content="width=device-width, initial-scale=1">'
'<meta name="referrer" content="no-referrer"><title>微信授权</title>'
f'<style nonce="{nonce}">body{{font:16px/1.6 sans-serif;color:#444;padding:24px}}</style>'
f'<body><p>{escape(message)}</p></body></html>',
status_code=200,
headers=_page_headers(nonce),
)
def _oauth_redirect_page(target):
nonce = secrets.token_urlsafe(24)
payload = json.dumps(target).replace("<", "\\u003c")
return HTMLResponse(
'<!doctype html><html lang="zh-CN"><meta charset="utf-8">'
'<meta name="viewport" content="width=device-width, initial-scale=1">'
'<meta name="referrer" content="no-referrer"><title>微信授权</title>'
f'<body><p>正在打开微信授权…</p>'
f'<style nonce="{nonce}">body{{font:16px/1.6 sans-serif;color:#444;padding:24px}}</style>'
f'<script nonce="{nonce}">location.replace({payload})</script></body></html>',
status_code=200,
headers=_page_headers(nonce),
)
def mobile_callback_page(subscribed: bool):
"""只返回原小程序;真实绑定仍由原登录客户端提交并显示最终结果。"""
nonce = secrets.token_urlsafe(24)