本文最后更新于24 天前,其中的信息可能已经过时,如有错误请发送邮件到 [email protected]
用户反馈:手机 QQ 里打开站点,点「QQ 登录」,转了几圈又回到登录页,怎么都进不去。
先看日志,别猜
第三方 OAuth 排查的第一步永远是看请求序列,而不是看页面:
12:49:19 GET /auth/qq 302
12:49:20 GET /auth/qq 302
12:49:25 GET /auth/qq 499
12:49:25 GET /auth/qq 302
12:49:34 GET /auth/qq 302
12:49:35 GET /auth/qq 302
十六秒里六次 302,一次 499(客户端主动断开)。而且没有一条 /auth/qq/cb。
正常流程应该是:
/auth/qq→ 302 到授权服务- 用户在 QQ 侧授权
- 回调
/auth/qq/cb?code=...→ 换用户信息 → 种 session → 跳首页
第 3 步没发生,说明 code 被送到了别的地方。
根因
授权服务把 code 回调到了 /auth/qq(少了 /cb)。而入口函数只负责「发起登录」,看到请求没有 token 就再 302 去授权页——于是形成闭环:
/auth/qq?code=xxx → 302 授权页 → 授权完成 → /auth/qq?code=yyy → …
浏览器一直跟着跳,用户看到的就是「转圈 → 回到登录页」。
修法
入口函数先判断有没有 code,有就当作回调处理:
async def auth_qq(request):
if request.query.get("code"):
return await auth_qq_cb(request)
...
一行判断,把死循环变成正常登录。这种东西不能指望第三方改——回调地址在它的后台里,你改不了;能改的只有自己这一侧。
顺带解决的两个隐患
- HTTP 页面里的登录:用户从
http://进站,登录完种下的是 https 的 cookie,中间一旦跨协议就容易丢态。把 80 端口整体 301 到 https,少一类玄学问题。 - 镜像域名没有路由:备用域名直接指向同一份静态文件,但没配
/auth/反代,点登录直接 404。给镜像域名加一条location = /auth/qq { return 302 https://主域/auth/qq; },按钮就不会再是死的。
验证方式很简单:拿一个坏 code 请求入口,如果返回 502「获取用户信息失败」,说明它已经走进回调逻辑;如果还是 302 回授权页,就是没改对。

