oauth2回调路由必须用handlefunc而非iris的get,因后者不保证query参数解析,导致code和state为空;需手动用r.url.query().get提取,并严格校验state、正确换token。

OAuth2回调路由必须用 HandleFunc 而不是 Get
Iris 的 Get 路由默认不支持 query 参数的自动解析(比如 code 和 state),而 OAuth2 回调必须从 URL query 中提取这两个关键字段。直接写 app.Get("/callback", handler) 会导致 r.URL.Query().Get("code") 始终为空——因为 Iris 的 Get 不保证 query 已被解析,尤其在某些中间件链或嵌入式服务器下更不稳定。
正确做法是用 HandleFunc,它绕过 Iris 的路由参数解析逻辑,把原始 *http.Request 交给你自己处理:
app.HandleFunc("GET", "/callback", func(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code")
state := r.URL.Query().Get("state")
// 后续校验、换 token 等逻辑
})
- 不要依赖
ctx.URLParam("code")或ctx.FormValue("code"),它们对回调 URL 无效 - 如果用了
RouterGroup,也要确保该 group 没有挂载干扰 query 解析的中间件(如 body 解析类中间件) - 该路由不能带任何路径参数(如
/callback/:id),OAuth2 平台只认纯静态路径
state 校验必须用 crypto/rand 生成 + 恒定时间比对
第三方平台(GitHub/微信/Google)返回的 state 是防 CSRF 的唯一凭证,但 Iris 自身不提供安全的 state 管理。你得自己存、取、比对,且不能出错。
常见错误包括:用 math/rand 生成(不安全)、存到前端 localStorage(可篡改)、用 == 直接比较(时序攻击风险):
- 生成时用
crypto/rand.Read+hex.EncodeToString,长度至少 16 字节 - 存到服务端 session(Iris 支持
ctx.Session().Set),**不能**存在 cookie 或前端传参 - 回调时用
subtle.ConstantTimeCompare比对,而不是==;否则攻击者可通过响应延迟推测 state 值 - 比对成功后立刻
ctx.Session().Delete("oauth2_state"),防止重放
换 token 必须用 r.Context() 发起请求,不能复用全局 client
OAuth2 的 Exchange 方法要求传入当前 HTTP 请求的 context(含超时、取消信号),否则在高并发或网络抖动时可能卡死或泄漏 goroutine。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
错误写法是把 oauth2.Config 存成包级变量然后反复调用 c.Exchange(context.Background(), code):
- 必须用
c.Exchange(r.Context(), code),让 exchange 受当前请求生命周期约束 - Exchange 返回的
*oauth2.Token需手动检查是否为*oauth2.RetrieveError类型,不能只看 err != nil - 微信等平台不返回
expires_in,要自己基于当前时间 + 固定 TTL(如 2 小时)算Expiry - 换 token 的 POST 请求 header 必须含
Authorization: Basic {base64(client_id:client_secret)},Iris 不自动加,得手写
回调路由不能加 JWT 或 OAuth2 中间件
这个点最容易被忽略:回调地址是第三方平台“主动推”给你的入口,此时用户还没登录、没 token、也没 session,所有鉴权中间件都该绕过。
如果你在 /callback 上挂了 JWTAuth 或 UseOAuth2,Iris 会直接拦截并返回 401 或重定向,导致 code 丢失、流程中断:
- 确保该路由注册在任何认证中间件之外,最好单独放在
app根上,而非某个带中间件的RouterGroup - 不要在回调 handler 里调用
ctx.User()或ctx.Values().Get("user"),此时为空 - 拿到 access_token 后再查用户信息、创建本地 session,这才是登录完成的标志
回调这一步本质是「协议握手」,不是「用户访问」。越干净越可靠,加任何额外逻辑都可能破坏 OAuth2 的原子性。










