当前 muxi_auth_service_v2 已经同时支持两套获取 OAuth 授权码的方式:
-
原有用户名/密码模式
客户端调用POST /auth/api/oauth,直接拿到授权码。 -
新增 CAS 模式
客户端先跳转到 CAS 登录页,CAS 登录成功后回调本服务的GET /auth/api/oauth/cas/callback,再由本服务签发 OAuth 授权码并重定向回业务系统。
这意味着:
- 下游业务系统依旧只需要理解 OAuth 授权码、
access_token、refresh_token - CAS ticket 只在本服务内部消费一次
- 客户端不需要自己去调用 CAS 的
serviceValidate
下面这张时序图描述了当前“业务应用 -> CAS -> OAuth 服务 -> 业务后端”的完整登录链路:
sequenceDiagram
participant User as 用户
participant App as 业务应用
participant OAuth as OAuth服务
participant CAS as CAS服务
participant Backend as 后端服务
App->>User: 拼接CAS登录URL并跳转
User->>CAS: 访问CAS登录页
CAS->>User: 显示登录表单
User->>CAS: 输入账号密码
CAS->>CAS: 验证登录
CAS->>OAuth: 回调callback地址,携带ticket
OAuth->>CAS: 通过ticket验证身份
CAS-->>OAuth: 返回用户信息
OAuth->>OAuth: 存储用户信息,签发code
OAuth->>User: 重定向到callback_url,携带code
User->>App: 携带code回调到业务应用
App->>Backend: 发送code到后端
Backend->>OAuth: 用client_secret换取身份信息
OAuth-->>Backend: 返回身份信息
Backend->>Backend: 完成业务登录,签发系统token
Backend-->>App: 返回系统token
App-->>User: 登录成功
这个图里有三点需要特别注意:
- 开始访问 CAS 系统的 URL 是通过client_id(每个服务在oauth服务这边注册得到的),callback_url(每个服务自己管理的)等参数拼接而成的,
- CAS ticket 只在 OAuth 服务内部使用一次,业务应用和业务后端都不直接消费 CAS ticket。
- 业务系统最终感知到的仍然是 OAuth 授权码和它自己签发的业务 token,而不是 CAS 会话本身。
CAS 登录完成后,会回调到:
/auth/api/oauth/cas/callback
这个接口要求至少携带以下查询参数:
ticket:CAS 回调带回来的 service ticketclient_id:要给哪个 OAuth 客户端发授权码callback_url:OAuth 授权码最终要重定向回去的业务地址token_exp:可选,透传给 OAuth 授权码对应的 token 过期时间配置
服务端收到 callback 之后,会按下面的顺序处理:
- 读取
ticket、client_id、callback_url、token_exp - 根据当前请求还原出当初传给 CAS 的原始
serviceURL - 使用
cas.server_url指定的 CAS 服务执行 ticket 校验 - 从 CAS 校验结果中拿到用户名
- 将该用户名编码成独立 subject,格式为
cas:<username> - 复用现有 OAuth 发码逻辑生成授权码
- 重定向到
callback_url?code=...
当前实现里,CAS 用户身份已经与原来的本地 users.id 解耦:
- 不再要求将 CAS 用户映射回本地用户表
- 不再依赖原来的本地账号密码体系
- CAS 登录成功后生成的 OAuth subject 为
cas:<username>
这意味着 CAS 用户和本地用户是两套独立身份来源。
当前与 CAS 相关的配置位于 conf/config.yaml:
cas:
server_url: http://localhost:8443/cas
callback_base_url: http://localhost:8083含义如下:
-
cas.server_url- CAS 服务地址
- 本服务会基于它调用 CAS 的 ticket 校验接口
-
cas.callback_base_url- 当前 OAuth 服务对外暴露的基础地址
- 用于在 callback 阶段还原原始
serviceURL - 当服务跑在反向代理后面时,这个值必须是“外部访问地址”,不能写内部容器地址
本地联调前需要准备三样东西:
-
MySQL
- 当前配置默认连接:
- 地址:
0.0.0.0:3306 - 数据库:
muxisite_auth
- 地址:
- 当前配置默认连接:
-
CAS 服务
- 仓库内已经提供了本地 CAS 调试目录: cas/docker-compose.yaml
-
OAuth 服务自身
- 默认监听
:8083
- 默认监听
进入 cas 目录后执行:
docker compose up -d当前已提供的 CAS 服务注册文件是: cas/cas-services/service-8083.json
它允许 CAS 回调到:
http://localhost:8083/auth/api/oauth/cas/callback
如果你把 OAuth 服务端口或域名改掉了,这个 JSON 也必须一起改。
进入项目根目录执行:
go run .也可以使用:
make默认服务地址是:
http://localhost:8083
如果你还没有 client_id / client_secret,可以先调用客户端注册接口:
curl -X POST "http://localhost:8083/auth/api/oauth/store" \
-H "Content-Type: application/json" \
-d "{\"domain\":\"http://localhost:8081\"}"返回示例:
{
"client_id": "your-client-id",
"client_secret": "your-client-secret"
}客户端需要自己拼接 CAS 登录地址。关键点是:
service必须指向本服务的 CAS callbackservice里面还要带上client_id和callback_url
示例:
http://localhost:8443/cas/login?service=http%3A%2F%2Flocalhost%3A8083%2Fauth%2Fapi%2Foauth%2Fcas%2Fcallback%3Fcallback_url%3Dhttp%3A%2F%2Flocalhost%3A8081%2Flogin%26client_id%3Dyour-client-id
如果还要自定义 token 过期时间,也可以继续追加:
token_exp=7200
完整示例:
http://localhost:8443/cas/login?service=http%3A%2F%2Flocalhost%3A8083%2Fauth%2Fapi%2Foauth%2Fcas%2Fcallback%3Fcallback_url%3Dhttp%3A%2F%2Flocalhost%3A8081%2Flogin%26client_id%3Dyour-client-id%26token_exp%3D7200
建议按下面的顺序观察:
- 打开上面的 CAS 登录地址
- 在 CAS 页面完成登录
- 浏览器应先回到:
http://localhost:8083/auth/api/oauth/cas/callback?...
- 然后服务端会再次 302 到:
http://localhost:8081/login?code=...
如果最后一步成功出现 code,说明:
- CAS ticket 校验通过了
- OAuth 授权码已经签发成功
拿到 code 之后,业务后端可以继续调用:
curl -X POST "http://localhost:8083/auth/api/oauth/token?grant_type=authorization_code&response_type=token&client_id=your-client-id" \
-F "client_secret=your-client-secret" \
-F "code=the-code-from-callback"返回示例:
{
"access_token": "...",
"access_expired": 7200,
"refresh_token": "...",
"refresh_expired": 157680000
}优先检查:
- CAS 是否加载了 service-8083.json
service参数里的地址是否和 JSON 正则匹配client_id、callback_url是否被错误地拼进了未转义的 URL
通常是以下几类原因:
- 同一个
ticket被重复消费了 serviceValidate时使用的service与登录时传给 CAS 的service不一致callback_base_url配错,导致服务端还原出来的service地址和实际登录地址不一致
优先检查:
callback_url是否传对了- 业务系统是否拦截了 302 跳转
- 最终地址是否已经变成
callback_url?code=...
如果你要完整走通一遍,当前推荐的本地地址组合是:
- CAS:
http://localhost:8443/cas - OAuth 服务:
http://localhost:8083 - 业务回调页:
http://localhost:8081/login
只要这三者保持一致,通常就可以比较顺畅地完成本地联调。