Revel 框架不内置 SSO 支持,需手动集成 OIDC/SAML;其拦截器无法处理重定向、跨域 Cookie、state 校验等 SSO 必需环节,且默认不注册回调路径;推荐用 golang.org/x/oauth2 + go-oidc 实现 OIDC 认证,注意 RedirectURL 完整性、ID Token 验证配置、session 序列化及单点登出调用认证中心登出端点,并显式配置 cookie.domain 实现跨子域会话共享。

Revel 框架本身不内置 SSO 支持,必须手动集成 OIDC 或 SAML 协议的认证中心;硬套 CAS 或自建票据服务反而容易绕过 Revel 的拦截器机制,导致鉴权逻辑断裂。
为什么 Revel 不能直接用 revel.Intercept 做 SSO 中间件
Revel 的拦截器(revel.Intercept)在请求进入 Controller 之前执行,但它不处理重定向链路、Cookie 域跨域、state 参数校验等 SSO 必需环节。常见错误是:在 BeforFilter 里检查 session.Get("user_id"),发现为空就跳转到认证中心——但用户回来时,回调地址没被正确捕获,GET /callback 请求根本不会进你的 Controller,而是被 Revel 的静态路由或 404 拦截了。
- Revel 默认不注册
/callback这类动态路径,必须显式在routes文件中加一行GET /callback Application.Callback - OIDC 回调要求携带
state和code查询参数,但 Revel 的Params对象默认不自动解析 query string 到Controller.Params,得手动调用c.Params.Get("code") - 如果把 token 验证逻辑写在拦截器里,一旦认证中心响应慢或超时,整个请求链会卡住,且无法统一处理 401/403 错误页
golang.org/x/oauth2 + OIDC 是最稳的组合
Revel 项目集成 SSO,首选 OIDC 协议,用 golang.org/x/oauth2 处理授权码流程,再用 github.com/coreos/go-oidc 解析 ID Token。不要自己拼接 JWT 校验逻辑——OIDC 的 nonce、at_hash、issuer 匹配、JWKS 密钥轮换这些细节,手写极易出错。
- 配置 OAuth2 Config 时,
RedirectURL必须和你在认证中心(如 Keycloak、Auth0)注册的一致,且必须是完整 URL,比如"https://app.example.com/callback",不能写相对路径 - ID Token 验证必须传入
oidc.Config{ClientID: "your-client-id"},否则Verifier.Verify会静默失败,返回 nil 而不是 error - 验证通过后,把用户信息存进 Revel session 时,别用
session.Set("user", user)直接塞 struct,要序列化成 JSON 字符串,否则重启后 session 反序列化失败
登出时 session.Clear() 不等于单点登出
调用 session.Clear() 只清了当前应用的本地会话,认证中心和其他已登录系统的会话依然有效。用户切到另一个子系统,还能直接访问——这不是“登出”,只是“假装登出”。
- 真正的单点登出需要向认证中心发起
GET /protocol/openid-connect/logout(Keycloak)或GET /v2/logout(Auth0),并带上id_token_hint参数 - Revel 的
session.Clear()必须在重定向到认证中心登出地址之前执行,否则浏览器可能带着旧 Cookie 发起登出请求,认证中心拒绝处理 - 登出后不要直接
c.Render(),而要用c.Redirect("/logged-out")跳转到一个纯前端页面,避免用户点后退又回到受保护页面
最难缠的其实是跨域 Cookie:Revel 默认的 session.cookie.domain 是空字符串,意味着只对当前 hostname 生效。多个子系统(a.example.com、b.example.com)想共享登录态,必须在 app.conf 里显式设为 .example.com,且认证中心发放的 ID Token 里的 domain 属性也得匹配——这点常被忽略,导致登出后刷新页面又自动登录。










