golang oauth2常见错误及解决:redirect_uri须与后台注册完全一致;code需一次性立即exchange;session只存最小必要字段;redirect需手动拼接query参数。

golang oauth2.Client 为什么总返回 invalid_request?
绝大多数情况是 redirect_uri 没对上——不是“写错了”,而是没在 OAuth2 提供商后台精确注册。比如你在代码里用 "http://localhost:8080/callback",但 github 后台填的是 "http://localhost:8080/callback/"(末尾多斜杠),或漏了 http:// 直接写 "localhost:8080/callback",都会触发这个错误。
实操建议:
- 用
url.Parse检查你传给oauth2.Config.AuthCodeURL的redirect_uri是否和后台注册的**完全一致**(包括协议、端口、路径、结尾斜杠) - 本地开发时,别用
127.0.0.1和localhost混用;GitHub/Google 都要求二者视为不同域名 - 把
redirect_uri提成常量,避免在AuthCodeURL和Exchange两处硬编码不一致
go.org/x/oauth2.Exchange 返回 invalid_grant 怎么办
这通常不是 Token 过期,而是授权码(code)被重复使用或超时。OAuth2 规范要求 code 一次性且有效期极短(常见为 10 分钟),用完即废。
常见错误现象:
立即学习“go语言免费学习笔记(深入)”;
- 浏览器后退再点“登录”,重发同一
code—— 后端没清空 session 或没校验code是否已用过 - 前端把
code存 localStorage 后异步调后端,中间卡顿导致超时 -
Exchange调用时漏传ctx,导致默认超时太短(http.DefaultClient的 30 秒可能不够)
实操建议:
- 收到
code后立刻调config.Exchange(ctx, code),不要存、不要延时 - 用带 timeout 的 context:
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) - 检查响应错误是否为
*oauth2.RetrieveError,它包含原始 HTTP 状态码和响应体,比直接打印 error 更准
如何安全地把 OAuth2 用户信息存进 Go Web session
别存原始 access_token 到 cookie,更别存 refresh_token —— 它们一旦泄露就是长期权限。session 里只放最小必要字段:用户唯一标识(如 GitHub 的 id)、provider 名("github")、过期时间(token.Expiry)。
使用场景:
- 用户登录后跳转前,把
userID和provider写进服务端 session(如gorilla/sessions) - 后续请求用 session 中的
userID + provider查数据库,而不是每次拿access_token去调 API - 如果需要调用户 API(如获取头像),临时从 session 取
access_token,用完丢弃,不缓存到全局变量
性能影响:access_token 本身是 JWT 时,可解析其 exp 字段做本地过期判断,避免每次都调 TokenSource.Refresh。
golang net/http.Redirect 302 后丢失 query 参数怎么办
这是新手高频坑:用 http.Redirect(w, r, "/callback?code=xxx", http.StatusFound),结果浏览器跳转后 r.URL.Query() 是空的——因为 Redirect 默认不保留原始请求的 query,它只是发一个新 GET 请求到目标 URL。
正确做法是把 code 和 state 拼进跳转 URL 里,但必须手动构造:
u := url.URL{ Path: "/callback", RawQuery: "code=" + code + "&state=" + state, } http.Redirect(w, r, u.String(), http.StatusFound)
注意:state 必须和之前传给 AuthCodeURL 的一致,用于防 csrf;别用 r.FormValue("state"),因为那是原始 /login 请求里的值,而 redirect 是全新请求。
容易被忽略的地方:Google 和 GitHub 对 state 长度有限制(通常 ≤ 1024 字符),别塞 json 或加密长串;用随机字符串 + session ID 组合更稳妥。