直接用requests拼URL获取access_token容易失败,因多数平台强制要求POST请求、redirect_uri严格匹配注册值、code一次性使用,且敏感参数不得放URL中。

为什么直接用 requests 拼 URL 获取 access_token 很容易失败
OAuth2.0 不是简单发个 GET 请求就能拿到 token 的流程。常见错误是手动拼接 https://auth.example.com/token?grant_type=authorization_code&code=xxx&client_id=xxx...,结果返回 {"error":"invalid_request","error_description":"Missing required parameter: redirect_uri"} 或更隐蔽的 401 Unauthorized。根本原因在于:多数平台(如 GitHub、Google、微博)强制要求 redirect_uri 必须与注册应用时完全一致(含末尾斜杠),且 code 仅能使用一次;同时 /token 端点通常只接受 POST + application/x-www-form-urlencoded,不能用 URL 参数传敏感字段。
实操建议:
- 始终用
requests.post()发起 token 请求,把client_id、client_secret、code、redirect_uri、grant_type=authorization_code放在请求体(data=...),不是 URL 参数 - 检查注册应用后台填写的
redirect_uri,本地开发常用http://localhost:8000/callback,必须一字不差地复用,包括协议、端口、路径大小写和结尾斜杠 - 从授权回调 URL 中解析
code后立即使用,不要缓存或重放;若误刷新回调页导致 code 失效,需重新走授权页跳转
如何安全处理授权回调并提取 code(以 Flask 为例)
你不能靠前端 JS 读取 URL hash(如 #access_token=xxx)来拿 token —— 那是 implicit flow,已被主流平台弃用或限制。现代 OAuth2.0 推荐使用 authorization code flow,需要一个服务端可访问的回调地址接收 ?code=xxx 查询参数。
Flask 示例中关键点:
立即学习“Python免费学习笔记(深入)”;
- 定义路由如
@app.route('/callback'),用request.args.get('code')提取code,而非request.args.get('access_token') - 回调页本身不要渲染敏感内容,避免日志或代理记录
code;拿到 code 后立刻 POST 换 token,不要在模板里展示或存 session - 务必校验
state参数防 CSRF:生成随机字符串传给授权页(scope=...&state=abc123),回调时比对是否一致,不一致则拒绝处理
示例片段:
@app.route('/callback')
def callback():
code = request.args.get('code')
state = request.args.get('state')
if state != session.get('oauth_state'): # 前期已存入 session
return 'Invalid state', 400
# 接着用 code 换 token...
调用 requests.post() 换 token 时必须设置的 headers 和 data
不同平台对 token 接口的要求差异极大:GitHub 要求 Accept: application/json 且认证方式为 client_id/client_secret 放 body;Google 要求 Authorization: Basic base64(client_id:client_secret);微博则要求所有参数都进 body 且 grant_type 必须是 authorization_code(注意不是 code)。
通用写法(适配多数平台):
- headers 至少包含:
{"Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded"} - data 字典必须含:
{"grant_type": "authorization_code", "code": code, "redirect_uri": redirect_uri} - client credentials 放哪?优先查文档:GitHub 和微博走
data,Google 和 Microsoft 走headers["Authorization"];混用会导致400 Bad Request或401 Invalid client - 响应体一律用
r.json()解析,不要用r.text手动切字符串;失败时检查r.status_code和r.json().get("error")
拿到 access_token 后调用用户数据 API 的坑
token 换回来只是第一步。直接拿它去请求 https://api.github.com/user 还可能被拒,因为:
- 部分平台(如微信)要求在 header 里加
Authorization: Bearer xxx,部分(如微博)要求作为 query 参数?access_token=xxx,错一种就403 Forbidden - GitHub 的
/user接口默认只返回公开字段;要邮箱等私有信息,授权时必须申请scope=user:email,且用户点击授权页时实际勾选了该权限(可在回调后调用/user/emails验证) - access_token 有有效期(GitHub 是长期,Google 默认 3600 秒),但 refresh_token 并非所有平台都返回(GitHub 不返回,Google 返回);没 refresh_token 就只能让用户重新授权
真正麻烦的是错误静默:比如微博返回 {"error": "invalid_access_token", "error_code": 21330},但 HTTP 状态码仍是 200;必须主动检查响应体里的 error 字段,不能只看 status_code。


















