# 单点登录集成问题排查指南
# 一、文档导读
本文档面向 AFCenter 运维人员与实施工程师,用于解决 OAuth2 / CAS 等单点登录(SSO)与第三方身份认证平台对接过程中遇到的各类问题。
# 阅读导航
| 读者角色 | 推荐阅读路径 |
|---|---|
| 首次对接 | 第一章 → 第二章(接口兼容性检查)→ 第三章(连接器配置)→ 第五章(快速检查清单) |
| 遇到报错 | 直接跳到第四章,按现象匹配对应小节 |
| 快速核对 | 直接查阅第五章快速检查清单 |
| 需要调试命令 | 直接查阅第六章附录 |
# 二、对接前接口兼容性检查
⚠️ 重要:在开始配置前,必须先检查第三方接口是否与 AFCenter 标准一致,如不一致需要先进行自定义扩展开发。
# 2.1 接口兼容性检查
向客户索要第三方 OAuth2 / CAS 接口文档,将第三方接口规范与 AFCenter 默认实现逐项对比:
| 接口 | 对比项 | AFCenter 默认实现 | 常见差异点 |
|---|---|---|---|
| Authorize(授权重定向) | 请求方式 | GET {authorize-url} | — |
| Query 参数 | response_type=code, client_id, redirect_uri, state, scope | — | |
| 回调端点 | POST /api/afc/login/third-party/auth | 第三方可能要求 GET | |
| 回调参数名 | code(从 JSON Body 中取) | 可能返回 ticket、auth_code 等 | |
| Token(code 换 access_token) | 请求方式 | POST {token-url} | 可能要求 GET |
| 参数位置 | URL Query String(Body 为空) | 可能要求 form-urlencoded Body | |
| Content-Type | application/json | 可能要求 application/x-www-form-urlencoded | |
| 响应解析 | 取 accessTokenKey 字段值(可配 afc.sso.oauth2.access-token-key) | 可能嵌套在 data.access_token | |
| Userinfo(获取用户信息) | 请求方式 | POST {userinfo-url} | 可能要求 GET |
| 认证方式 | Authorization: {token} ⚠️ 无 Bearer 前缀 | 标准格式要求 Bearer {token},否则可能 401 | |
| Body | 空(null) | 可能要求 Body 传 token | |
| 响应解析 | 取 userKey 字段值(可配 afc.sso.oauth2.user-key) | 支持嵌套路径如 data.userId | |
| 回调(接收第三方回传 code) | 端点 | POST /api/afc/login/third-party/auth | — |
| Content-Type | application/json | — | |
| Body | {"code": "xxx"} | — | |
| 白名单 | 在 user-config.xml Exclude 列表中,无需登录态 | — |
如有不兼容项,需先完成自定义扩展开发(实现
IOauthCustomService接口),再进入配置环节。更细致的参数与源码实现可基于DefaultOauth2ClientServiceImpl.java和Oauth2RestTemplateUtil.java进一步分析。
# 2.2 相关文档
- 🔗 OAuth2 集成方案 — 包含自定义扩展开发(
IOauthCustomService接口)的完整说明 - 🔗 AFCenter 连接器配置手册 — 连接器各配置项的详细说明
# 三、连接器配置
# 3.1 连接器必填配置项
连接器配置项(authorize-url、token-url、user-info-url、client-id、client-secret、user-key 等)的详细说明请参考:
# 3.2 redirect_uri 配置规范
redirect_uri 是 OAuth2 流程中最容易出错但最关键的一环,必须严格遵守以下规范:
- ❌ 严禁使用
127.0.0.1或localhost - ✅ 必须使用 AFCenter 服务器的实际域名或 IP(如
https://afc.example.com/api/afc/login/third-party/auth) - ✅ 必须与第三方平台注册时填写的回调地址完全一致(包括协议、域名、端口、路径)
- ⚠️ 注意末尾是否有
/,这也算不一致
# 3.3 白名单配置检查
在 user-config.xml 配置文件中,确保以下 OAuth2 相关的 URL 路径已加入白名单:
<!-- OAuth2 统一认证 -->
/api/afc/oauth2/*
<!-- 第三方登录 -->
/api/afc/login/third-party/qrConnect
/api/afc/login/third-party/mobile/authorize
/api/afc/login/third-party/auth
/api/afc/login/web/third-party/auth
/api/afc/login/third-party/types
/api/afc/login/third-party/validate
<!-- 登录基础接口 -->
/api/afc/validation-code
/api/afc/login
/api/afc/login/password/key
/api/afc/user/validation-code
/api/afc/login/clientId
<!-- SSO -->
/api/afc/sso/redirect-url/default
# 四、按现象排查问题
# 4.1 现象一:OAuth2 配置后,报错接口为 validation-sso-cas
典型表现:
配置了 OAuth2 连接器,但前端调用的报错接口却是 /api/afc/login/validation-sso-cas(这是 CAS 接口,而非 OAuth2 接口)。
排查过程:
Step 1 — 观察前端实际调用的接口
- 打开浏览器开发者工具(F12)→ Network 标签页
- 触发 SSO 登录
- 查看实际调用的接口路径
Step 2 — 检查第三方接口返回格式
- 标准 OAuth2 流程:授权完成后 302 重定向到:
{redirect_uri}?code={authorization_code}&state={state} - 实际观察:返回的参数名为
ticket而非code
Step 3 — 判断流程差异
前端代码会根据回调 URL 中的参数名判断认证类型:
| 参数名 | 前端判断的认证流程 |
|---|---|
code | OAuth2 流程 |
ticket | CAS 流程 |
结论:
第三方接口不符合标准 OAuth2 规范,返回的是 CAS 风格的 ticket 参数。前端识别到 ticket 后自动走 CAS 流程,导致调用了 /api/afc/login/validation-sso-cas 接口。
解决方案:
需要自定义扩展来适配第三方的非标准返回:
- 参考 OAuth2 集成方案 中的自定义实现章节
- 实现
IOauthCustomService接口,将第三方返回的ticket映射为code - 确保前端能正确识别为 OAuth2 流程
# 4.2 现象二:点击 SSO 登录后,无法跳转到第三方登录页
可能原因:
| 原因 | 说明 |
|---|---|
authorize-url 配置错误 | URL 地址拼写错误或路径不对 |
client-id 未在第三方注册 | 第三方平台不认识该应用 ID |
redirect_uri 与第三方白名单不一致 | 第三方校验回调地址不通过 |
排查步骤:
F12 → Network → 查看跳转 URL 参数
检查浏览器实际发出的请求 URL,确认参数完整且正确:
{authorize-url}?response_type=code&client_id={client-id}&redirect_uri={redirect_uri}&state={state}对比连接器配置与第三方平台注册信息
- 逐一核对
client-id、client-secret、authorize-url与第三方平台登记的信息 - 特别注意是否有空格、多余字符
- 逐一核对
检查 URL 编码是否正确
redirect_uri 作为 URL 参数传递时需要进行编码,特殊字符可能导致解析异常。
# 4.3 现象三:第三方认证成功后,回调 URL 没有 code 参数
排查步骤:
确认第三方平台 redirect_uri 白名单配置
检查第三方平台应用配置中的回调地址是否与 AFCenter 的 redirect_uri 完全一致。
检查是否有额外的重定向中转
如果中间有网关(如 Nginx、API Gateway)做了额外重定向,可能导致
code参数丢失。检查 Nginx 配置中是否有:
# 可能导致参数丢失的配置 proxy_pass http://backend/; # 注意:末尾 / 可能影响 URI 拼接确认
response_type是否配置为code如果第三方支持多种授权模式,确认连接器请求的
response_type=code。
# 4.4 现象四:回调后报 "INVALID_CODE" 或授权码无效
排查步骤:
检查时间差
F12 Network 中对比两个时间点:
- 302 回调时间(第三方返回 code 的时间)
- POST
/api/afc/login/third-party/auth时间(AFCenter 用 code 换 token 的时间)
如果时间差超过 60 秒,授权码可能已过期。
检查 code 是否被重复使用
OAuth2 授权码
code只能使用一次。如果中间有重试逻辑导致重复请求,第二次请求会返回无效。检查网络延迟
如果 AFCenter 服务器与第三方平台之间网络延迟过高,建议联系第三方调整 code 有效期。
# 4.5 现象五:登录成功但 AFCenter 显示的用户不对或空白
排查步骤:
开启 DEBUG 日志
在日志配置(logback-spring.xml)中开启 DEBUG 级别日志。
搜索关键字 "userInfo"
在日志中搜索
userInfo关键字,找到第三方返回的原始 JSON:示例日志: [DEBUG] userInfo response: {"code":200,"data":{"userId":"zhangsan","userName":"张三"}}对比
user-key配置与实际字段名配置的 user-key 实际返回的字段路径 是否正确匹配 userIddata.userId(嵌套在 data 下)❌ 不匹配,应配置为 data.userIduserIduserId(平铺字段)✅ 匹配 user_iduserId(驼峰 vs 下划线)❌ 不匹配 处理嵌套字段
如果用户标识在嵌套结构中(如
data.userId),需要:- 配置
user-key为完整路径data.userId - 或者通过自定义扩展解析嵌套 JSON
- 配置
# 4.6 现象六:userinfo 接口调用失败(404/405)
排查步骤:
用 curl 手动测试两种请求方式
GET 方式测试:
curl -X GET "{user-info-url}" \ -H "Authorization: Bearer {access_token}"POST 方式测试:
curl -X POST "{user-info-url}" \ -H "Authorization: Bearer {access_token}" \ -H "Content-Type: application/json"确认第三方支持的请求方式
对比 GET 和 POST 两种方式,确定哪个返回 200。AFCenter 默认用 POST 调用 userinfo 接口,如果第三方只支持 GET,则需要自定义扩展。
检查 Authorization 头格式
正确的格式 常见的错误格式 Authorization: Bearer eyJhbG...Authorization: eyJhbG...(缺少 Bearer 前缀)— token: eyJhbG...(Header 名称错误)— URL 参数 ?access_token=xxx(不是 Header 方式)
# 4.7 现象七:登录一段时间后自动掉线
排查步骤:
检查第三方 access_token 的
expires_in查看 token 接口返回的
expires_in字段,确认 token 有效期:{ "access_token": "eyJhbG...", "expires_in": 3600, "token_type": "Bearer" }上例中
expires_in: 3600表示 1 小时后过期。检查 AFCenter 会话超时配置
确认 AFCenter 的 session 超时时间是否短于 token 有效期。
refresh_token 说明
⚠️ 注意:按角色区分:
- AFCenter 作为 OAuth2 服务端:✅ 支持 refresh_token。
/api/afc/oauth2/token端点会处理grant_type=refresh_token请求(参见AfcOAuth2Handle.java:108, 343),自动校验并返回新的 access_token。 - AFCenter 作为 OAuth2 客户端对接第三方:❌ 不支持自动 refresh_token。
DefaultOauth2ClientServiceImpl.java:122注释明确:"这里 accessToken 不做缓存,如果还有 refreshToken 需要做刷新的操作则自己重写方法实现"。如业务需要长时间在线,需自行扩展实现 refresh_token 逻辑。
- AFCenter 作为 OAuth2 服务端:✅ 支持 refresh_token。
# 4.8 现象八:开启SSO 自动登录后,登录失败后登录页一直刷新
说明:
这是正常现象。开启 SSO 自动登录后,如果 SSO 登录失败(如 token 过期、第三方不可用),系统会自动重试 → 失败 → 重试,导致登录页面无限循环刷新。
解决方法:
- 关闭 SSO 自动登录开关,恢复手动登录入口
- 排查 SSO 登录失败的根本原因后,再重新开启
- 如果是 token 过期导致,考虑按 4.7 节 调整 token 有效期配置
# 4.9 现象九:iframe 嵌套集成无法登录
不同模式的处理方式:
| 认证模式 | URL 参数传递方式 | 示例 |
|---|---|---|
| CAS 模式 | URL 拼接 ticket 参数 | https://app.example.com?ticket=ST-xxxx |
| OAuth2 模式 | URL 拼接 Authorization 参数(值为 access_token,需 URL 编码) | https://app.example.com?Authorization=eyJhbG... |
建议:
优先使用标准登录认证集成方式(而非 iframe 嵌套),以避免跨域策略、Cookie 隔离等问题。如必须使用 iframe,请确保第三方登录页面允许被嵌入(检查
X-Frame-Options响应头)。
# 4.10 现象十:钉钉 / 企业微信 / 飞书集成失败
排查步骤:
检查权限是否开通
- 钉钉:确认应用已获得"通讯录读取"等必要权限
- 企业微信:确认可见范围包含目标用户
- 飞书:确认应用已发布并通过审核
确认员工手机号与第三方平台一致
AFCenter 默认通过手机号匹配用户。确保:
- 第三方平台返回的用户手机号字段正确
- AFCenter 中用户手机号与第三方一致
参考第三方集成文档
# 五、附录
# 5.1 日志关键字速查表
在日志中搜索以下关键字快速定位问题:
| 关键字 | 用途 | 可能对应的现象 |
|---|---|---|
userInfo | 查看第三方返回的原始用户信息 JSON | 现象五(用户空白) |
access_token | 查看 token 接口返回结果 | 现象四(INVALID_CODE) |
INVALID_CODE | 授权码无效错误 | 现象四 |
redirect_uri | 查看实际使用的回调地址 | 现象二、三 |
OAuth2 | 全局 OAuth2 流程日志 | 通用排查 |
validation-sso-cas | CAS 接口被调用 | 现象一 |
401 | 认证失败(通常 token 或 secret 问题) | 现象一、四、六 |
404 | 接口不存在(URL 错误或请求方式不对) | 现象六 |
405 | 请求方式不允许(GET/POST 不匹配) | 现象六 |