前阵子有人在技术群里抛了个问题:“你都知道哪些登录?”底下回答很快就歪了:有说账号密码登录的,有说短信验证码登录的,有说扫码登录的,也有人直接抛出来“OAuth2.0联合登录”“SSO单点登录”。开始大家聊得挺热闹,等有人问“那 OAuth2.0 联合登录和 SSO 单点登录到底有什么区别”时,群里反而冷场了。这话题我在实际项目里被问过不止一次,尤其是有人把第三方联合登录直接当成单点登录来设计,最后权限全部乱掉。这篇就把这两种登录方式彻底拆开讲清楚:它们各自解决什么问题,核心流程是什么,真实落地的配置应该怎么接,以及后续你在企业系统里对接 NC65、致远OAV8这类产品时会踩到的坑。适合后端开发、架构师,也适合负责企业系统集成的实施同学,内容偏工程实践,不会只堆概念。
1. 先把登录这件事梳理清楚
1.1 登录的本质不是“输入密码”
不管对外做成什么样,登录的逻辑最终都逃不开三件事:确认你是谁、给你发凭证、系统每次认这个凭证。传统表单登录是输入账号密码,服务端验证后写 Session;短信验证码登录本质是拿你手机号作为身份标识,确认你拥有这个手机号后签发凭证;扫码登录则是让 App 端帮你完成身份确认,然后网页端拿到一个临时凭证再换正式登录态。
这些东西落到产品形态上可以千奇百怪,再叠加企业微信、钉钉、Github 之类的第三方登录方式,整个认证体系就越来越复杂。写代码时大家常说的“登录”,其实通常只指“给这个系统做一套 Session 机制”。而单点登录和联合登录,已经远远超过“一个系统认证”的范畴,它们是在解决多个系统、多个业务方之间的身份互信问题。
我习惯把登录需求分成三层来理解:
- 单系统登录:用户只在你这套系统里认证,服务端维护登录态。
- 联合登录:用户用第三方平台身份登录你的系统,本质是“借第三方来验证账号”。
- 单点登录:一个企业或一个产品矩阵内,用户在一个系统登录,其他系统自动可用。
很多人混淆 OAuth2.0 联合登录和 SSO 单点登录,根源就是把“用什么身份验证”和“验证一次后能否访问多个系统”混在了一起。前者决定你信谁,后者决定你认几次。
1.2 OAuth2.0、联合登录、SSO 到底是什么关系
先把定义摆正。OAuth2.0 本身是“授权框架”,它设计出来的目的并不是为了做登录,而是让第三方应用在用户授权之后,有限制地访问用户在其他平台上的资源。说人话就是:用户在自己的系统里,授权某个应用帮他获取资源,但不需要把平台账号密码交给这个应用。
而联合登录,在我们日常语境里基本等于“使用 OAuth2.0 协议做身份认证”。应用把你引导到第三方平台,第三方平台确认你本人后,给应用一个 code,应用再用 code 换 token,最后拿 token 获取用户信息。所以联合登录是基于 OAuth2.0 的一种常见业务形态,但它不是唯一形态。
SSO 单点登录则是一个更大的概念。它关心的是:同一用户在公司内部多个系统之间切换时,能不能只认证一次。实现 SSO 可以用共享 Session,也可以用 CAS、SAML、OIDC 等协议,也可以基于 OAuth2.0 改造。很多企业做“统一身份认证平台”,在 Web 形态下给子系统发 token 来维持登录态,这在工程上层面上和 OAuth2.0 很像,但它的业务目标已经不一样了。
用一句话区分就是:联合登录解决“第三方身份如何被我的系统信任”,SSO 解决“同一个身份体系下,多个系统如何共享认证结果”。如果你要做的是一套公司内部的统一登录平台,优先想的是 SSO 架构;如果你的产品要支持“微信登录”“Github 登录”,优先想的是 OAuth2.0 联合登录。
1.3 选择方案前先问清业务场景
在项目初期,我会先让业务方回答几个问题,这些问题往往直接决定认证方案。
第一个问题是:用户账号从哪来?账号只在自己的用户表里,那就做最基本的账号密码+Session;如果用第三方账号,就是联合登录;如果组织用户来自企业 AD/LDAP 或统一身份源,那么大概率需要 SSO,至少需要一个统一认证中心。
第二个问题是:登录之后要访问几个系统?只访问一个后台管理系统,没必要强行上 SSO;要同时进 ERP、OA、报表、运维平台,还不希望每个系统都登录一遍,那就必须上 SSO。
第三个问题是:系统之间有没没有现成的信任关系?同一家公司自研的多个系统,可以通过中心认证实现 SSO;第三方产品如 NC65、致远OAV8,往往只对外暴露某个接口或配置项,这时候就要判断用哪一种接入方式最省事。
等这几个问题有答案了,后面 OAuth2.0、CAS、OIDC、自定义票据方案的取舍才会有依据。反正我见过最多返工的项目,不是因为协议写得不好,而是压根没搞清楚该做联合登录还是单点登录,就把两套方案叠上去,最后到处都是 session 冲突。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. OAuth2.0 联合登录的核心流程与落地细节
2.1 授权码模式依然是首选
OAuth2.0 有几种授权模式,名称容易让你晕,实际场景里能稳定用于联合登录的,基本就是授权码模式(Authorization Code)和带 PKCE 的授权码模式。
早期的隐式授权(Implicit)已经不推荐用于纯前端应用,因为它直接把 access_token 放在回调地址里面,token 被浏览器历史、日志、Referer 头泄露的风险很高。密码模式(Resource Owner Password Credentials)要求用户把账号密码直接交给第三方应用,和“联合登录避免泄露密码”的设计初衷冲突,在 Web 联合登录里也少见。客户端模式(Client Credentials)用于服务器之间的机器身份认证,没有真实用户参与,不适合做用户登录。
授权码模式之所以稳,是因为它把“让用户授权”和“换 token”拆成了两条通道:用户浏览器只负责获得一个临时 code,应用服务器再用 code 和 client_secret 去 token 端点换 token。这样第三方应用的真实密钥永远不会出现在浏览器 URL 里,安全性高很多。
网页应用联合登录的流程,用大白话描述是这样的:
- 用户在你的系统点“用 XX 账号登录”。
- 你的后端把用户浏览器重定向到 XX 平台的授权页面。
- 用户在 XX 平台完成登录并确认授权。
- XX 平台带着授权 code 重定向回你配置好的回调地址。
- 你的后端拿到 code,用 client_id 和 client_secret 去 XX 平台换 token。
- 拿到 token 后,调用 XX 平台的用户信息接口,获取唯一标识和昵称头像。
- 后端拿着用户唯一标识查你本系统的用户表,不存在就自动创建或引导绑定,存在就直接建立本地登录态。
这里有一个很多人忽略的点:第 5 步必须放在后端完成,不能在前端用 JavaScript 拿 code 去换 token。把 client_secret 放到前端,等同于把账号密码贴在门上。凡是线上出安全事件的项目,绝大多数是这里图省事出了问题。
2.2 一个典型回调地址的完整链路
假设你开发的是纯 Java 后端,前端页面点击登录后,后端返回一个重定向地址,大概长这样:
text复制https://auth.example.com/oauth/authorize?
client_id=web_portal&
redirect_uri=https://app.example.com/callback&
response_type=code&
scope=read_profile&
state=8f3a72d1e1c74b8f&
code_challenge=eUx...&
code_challenge_method=S256
这里有几个参数建议每个都认真对待:
- redirect_uri 必须和你在平台注册的回调地址完全一致,协议、域名、端口、路径都不能差。平台方会做精确匹配,不一致时会直接拒绝授权。
- response_type=code 表示走授权码模式。
- state 是一个随机字符串,后端生成后要保存到 Session 或 Cookie 里,回调时再比对。它可以防止第三方通过伪造请求让用户登录到攻击者控制的账号上,也就是 CSRF 风险。
- 如果应用是纯前端 SPA,没有后端保存 code 的条件,建议把 state 存到 sessionStorage,同时使用 PKCE(code_challenge 参数)来防止授权码被截获后的风险。
code 本身有效期一般很短,大多只有几分钟,而且只能使用一次。平台方在兑换 token 时如果发现 code 已经使用过,通常会直接拒绝。后端代码不要以为拿 code 能重复换 token。
等回调请求到达你的 callback 接口后,后端要做的事大概是:
- 先从参数里取 state,和自己存的 state 比对,不一致直接中断。
- 取 code,用 client_id、client_secret、redirect_uri 一起 POST 到 token 端点。
- token 响应里一般包含 access_token、refresh_token、expires_in,如果平台支持 OIDC 还可能有 id_token。
- 调用 userinfo 端点,最好只接受 HTTPS,拿到用户的 sub 字段作为唯一标识。
- 用这个 sub 去你自己的用户库里找或创建用户,最后在你的系统内写入一个独立 session。
如果你的平台返回的是 JWT 格式的 access_token,并且你本地能验证签名,也可以减少一次 userinfo 的远程调用。但要注意 JWT 的密钥轮换和过期时间,本地验证失败时一定要做一次降级拉取,不能直接让用户登录失败。
2.3 联合登录容易踩的坑和服务端注意事项
我在实际项目里整理过一份对接联合登录的问题清单,很多是重复踩坑:
| 现象 | 常见原因 | 解决思路 |
|---|---|---|
| 点击第三方登录报 redirect_uri 不匹配 | 注册回调地址和代码里使用的地址不一致,比如末尾多了斜杠 | 把两者改成完全一致,检查是否配置了 HTTPS 一级域名差异 |
| 回跳后一直提示 state 不对 | 回调页面刷新导致 code 重复使用,或 state 存到了不稳定的位置 | code 用后即失效,刷新场景直接回到原应用首页重新发起登录 |
| 用户授权后无法建立本地账号 | 平台返回的 sub 字段被当成业务工号使用,但业务系统里没有对应记录 | 引入绑定关系表,把第三方唯一标识关联到本地账号 |
| token 过期后页面请求静默失败 | 应用没有保存 refresh_token,或 refresh_token 没有刷新机制 | OAuth2.0 客户端保存 refresh_token,在 access_token 过期前主动刷新 |
| 用户头像昵称没更新 | 本地首次创建账号后不再同步 | 定期或每次登录时通过 userinfo 同步一次基础资料 |
这里额外提醒一句:第三方平台的唯一标识不要用邮箱作为主键。用户修改主邮箱会造成账号漂移,而且同一用户在微信、Github、企业微信里的邮箱根本对不上,反而把自己搞乱。要尽可能用平台返回的 sub、openid、unionid 这类稳定字段。
另外,授权页面的跳转必须走标准 HTTP 302 跳转,不要在前端手动拼 URL 后 window.location 跳到一个拼接不完整的地址上。很多产品在门户系统里二次封装登录页,结果把 redirect_uri 里的 & 符号转义错了,整个联合登录流程直接断掉。
3. SSO 单点登录到底是怎么实现的
3.1 SSO 的宏观架构和核心逻辑
SSO 单点登录要解决的是“员工登录公司 OA 之后,再打开报表系统、NC65 等外部系统时不用重复输入密码”。注意这里有一个非常重要的前提:这些系统之间未必是同一家公司开发的,甚至可能部署在不同域名、不同机房。
所以在做 SSO 时,认证中心必须是一个独立存在的角色。它负责统一校验用户名密码,生成一个“全局凭证”,再让各个业务子系统都信任该凭证,并在本地建立起自己的会话。从业务系统的角度看,业务系统不直接验证密码,只判断“凭证是不是认证中心发出来的”。
目前工程上用到的 SSO 方案可以归成三类:
第一类是共享 Session。应用之间放在同一域名下,把 Session ID 写到同一个父级 Cookie 上,比如 .example.com。一个系统登录后,另一个系统在同一浏览器里发请求也能带过来,服务端共享同一份 Redis Session。这种方案简单直接,但只适合同域、同技术栈的系统,跨域后 Cookie 无法携带,扩展性很差。
第二类是基于票据的 CAS 风格方案。认证中心下发一个 ticket,用户浏览器带着 ticket 去访问系统 A,系统 A 再用 ticket 找认证中心换取用户信息。能够做到跨域,但每个系统都要自助接入认证客户端。
第三类是基于 OIDC/OAuth2 协议的 SSO。系统 A 充当 OIDC Client,认证中心充当身份提供者,用户认证后通过 id_token 或 access_token 换取用户信息并建立本地会话。对现代前端和后端系统来说,这套方案比较友好,也有大量现成框架支持。
工程里还有定制型的 SSO:许多外部系统不开放完整源码,只留一个登录接口或一个插件点。这时候通常不是改认证中心,而是在外部系统侧配置“单点登录模式”,指定验证地址和密钥,认证通过后按固定参数传回工号。NC65、致远OAV8这类重量级企业产品,很多都是走这种模式。
3.2 一个可运行的 SSO 流程拆分
我拆一个最简化的 SSO 登录流程,方便你理解各个组件之间的关系。
假设有三个角色:认证中心 auth.example.com,业务系统 A app1.example.com,业务系统 B app2.example.com。用户之前没有登录过任何系统,现在首次访问系统 A。
流程会是这样:
- 浏览器访问系统 A,A 的后端检查本地 Session,发现没有用户信息。
- A 把浏览器重定向到
https://auth.example.com/login?service=https://app1.example.com/sso/callback。 - 用户输入用户名密码登录认证中心,认证中心验证通过后,在认证中心域名下写入一个用户凭证,然后生成一个一次性 ticket,拼在 service 后面重定向回去。
- 系统 A 收到 service 回调请求后,从 URL 取出 ticket,然后系统 A 后端直接向认证中心验证 ticket,获得用户标识。
- 系统 A 确认用户有效,在本地写 Session。至此用户能正常使用系统 A。
- 用户接着访问系统 B,系统 B 也发现没有本地 Session,于是跳转到认证中心。
- 浏览器访问认证中心时,认证中心发现之前已经写过用户凭证,所以不再要求重新登录,而是直接生成一个新的 ticket,再跳回系统 B。
- 系统 B 同样用 ticket 去换用户信息,并建立本地会话。
整个过程从用户视角看,只在第 3 步输了一次密码,后续系统全部自动登录。
如果想用框架快速实现,不推荐从零写。Spring Boot 环境下可以用 Spring Security 配合 OAuth2 Client 接入已有 IdP;如果想自己扮演认证中心的角色,可以关注 Spring Authorization Server。自研 SSO 最花时间的往往不是登录流程,而是用户绑定关系、账号禁用踢出、踢人下线、审计日志、权限同步这些边角逻辑。
3.3 企业产品对接 SSO:NC65、致远OAV8这类系统怎么处理
搜索“外部系统单点登录 NC65 重量端”“致远 OAV8 单点登录在哪里配置”的人,多半不是后端开发,而是实施经理或信息化同事。这类产品有一个特点:它们本身就是完整系统,不可能为你的统一登录中心改源码,只能把自身配置成“SSO 客户端”。
传统外部系统接入 SSO,通常绕不开三件事:
- 找外部系统里配置认证中心的入口,比如常见的“系统管理-认证配置-单点登录设置”这类路径。不同产品叫法差很多,有的是 SLO、SSO、外部认证,有的是企业集成配置。
- 准备认证中心回跳时的地址。外部系统需要知道用户从哪里回来,这决定了回跳路径怎么配。
- 约定密钥和参数映射。有的系统用 AES 对称加密,有的用 RSA 签名,有的只是简单校验时间和用户编码。真正要传的字段一般就两个:用户唯一标识和签名。问题通常出在字符编码上,比如工号里带空格、转义符,登录接口会偶尔好偶尔失败。
集成 NC65 这种重量端时,还要额外注意前后端地址分离的问题。重量端登录成功后,页面 SPA 会请求后端接口,如果单点登录地址只配置在后端,而前端没有把 ticket 或 session cookie 保存到正确的位置,用户界面仍然会表现为“没有登录”。这种问题不太容易从后端日志里看出来,一般要先抓浏览器的请求,看看回跳是不是落在正确的域名下,再看后端有没有生成对应会话。
致远 OAV8 这类 OA 产品则通常提供标准的协议集成。在配置前,先去找厂商文档里的“单点登录配置说明”,重点看它支持哪一种方式:是提供通用 CAS 接口,还是只支持自定义票据接口。如果产品不支持你现在这套认证中心协议的官方适配器,就得写一个很小的中间适配层,把你的认证协议翻译成 OA 系统能识别的票据格式。很多项目卡壳就是因为在“产品能不能做”上花了两三周,却没有先去确认适配方式。
3.4 SSO 里的安全细节不要偷懒
SSO 因为影响面大,安全问题容易被放大。一个系统被攻破,所有接入系统都可能受影响,所以下面几个点务必做到。
第一个是 ticket 必须是一次性的。认证中心把 ticket 标记为“已使用”之后不能再校验通过。如果不做一次性限制,攻击者拿到 ticket 后可以反复进入任意系统。
第二个是 ticket 要有过期时间,一般不要超过 5 分钟。登录后立刻回跳的业务场景,几分钟已经足够。时间太长增加重放风险。
第三个是认证中心自己的用户凭证要有下线机制。很多所谓 SSO 只是“能登录”,却做不到“统一退出”。账号禁用时,认证中心要能广播会话失效,否则离职员工已经不能登录 OA,但报表系统还留着一个活跃的本地 session,那就闹大了。
第四个是实现上要注意 session fixation。用户登录成功后,后端要重新生成 Session ID,避免使用登录前浏览器已经带过来的旧 Session ID。很多框架默认处理了,但如果你用自定义鉴权过滤器,必须显式处理。
4. OAuth2.0 和 SSO 到底怎么选,以及共存场景
4.1 选型对比表和核心判断标准
有不少国内开发团队,一聊单点登录就默认用 OAuth2.0,其实不一定正确。我做了下表尽量把区别讲清楚:
| 对比维度 | OAuth2.0 联合登录 | SSO 单点登录 |
|---|---|---|
| 核心目的 | 用户授权应用访问资源,顺便完成身份认证 | 多个系统共享同一个用户凭证 |
| 典型场景 | 用微信/Github/企业微信登录某个产品 | 员工从 OA 跳转到报表、NC65、致远OA |
| 用户账号归属 | 账号在第三方,你的系统保存映射关系 | 账号统一在企业用户源,系统按映射同步 |
| 会话管理 | 各系统各自管理访问令牌和刷新令牌 | 认证中心管理全局凭证,子系统管理本地会话 |
| 跨域能力 | 通过标准重定向支持 | 可以支持,但需要额外设计回调与票据校验 |
| 标准程度 | OAuth2.0、OIDC 都是成熟标准 | CAS、SAML、OIDC 都有实现,需依据产品选择 |
| 运维复杂度 | 中低,主要处理密钥和回调 | 较高,全平台认证状态和权限同步都需要管理 |
在选择时,我习惯先问是否有统一的账号源。如果没有统一账号源,优先考虑联合登录;如果有一个组织身份库,且希望一套账号走所有系统,SSO 优先级更高。
很多中大型项目实际情况是两种都要做:员工用企业微信或钉钉登录公司统一门户,这是“外部身份+内部账号”的联合登录;登录门户之后访问 OA、报表、NC65 等系统不再重复登录,这是 SSO。两套体系在工程上会同时存在,所以选型不是非此即彼。
4.2 在技术栈里怎么把这套东西串起来
用一个 Java 后端项目举例。如果项目只是接入已有的企业微信登录,可以直接用 Spring Security OAuth2 Client 的配置:
yaml复制spring:
security:
oauth2:
client:
registration:
wechat:
provider: wechat
client-id: xxxxxx
client-secret: xxxxxx
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
authorization-grant-type: authorization_code
provider:
wechat:
authorization-uri: https://open.weixin.qq.com/connect/oauth2/authorize
token-uri: https://api.weixin.qq.com/sns/oauth2/access_token
user-info-uri: https://api.weixin.qq.com/sns/userinfo
但关键是 OAuth2 框架只解决“认证信息拿到手”,后续用哪个字段建立本地用户还需要自己写 UserService。很多人的登录链路断就断在这:OAuth2 回调成功后,用户表里找不到记录,也没有提示绑定流程,前端就一直卡在加载中。
如果是做成企业内部 SSO,先要决定认证中心是自建还是购买现成的。自建可以在认证中心里做 OAuth2/OIDC Provider,然后各个业务系统通过 OAuth2 Client 接入。用户登录认证中心后,认证中心会生成一个 session,之后业务系统每次跳转回来认证中心会检测到这个 session,不会再要求输入密码。开发上要特别注意回调地址不能随便让一个系统串进来,每个子系统都要用独立的 client_id,避免越权。
我建议团队优先使用成熟方案,不要自己写加密算法、签名算法、票据机制。认证体系牵涉很多细节,自己写很容易忽略边界场景。如果项目偏老、用的是 Spring Boot 2 时代的 Spring Security OAuth2,更要注意它只适合接入,不适合做授权服务器;从 Spring Security 5 开始,Spring 官方推出了 Spring Authorization Server,可以作为认证中心的基础。
4.3 报表类开源组件怎么“被单点登录”
项目里经常出现“积木报表支持单点登录”的搜索词,其实积木报表这类开源报表组件通常不是独立系统,而是被打包到具体业务项目里使用的。这时它的登录态基本来自宿主系统,不需要单独做一遍 SSO 登录。
用户在宿主系统登录后,报表组件作为嵌入页面,一般已经带有宿主系统的 session cookie。后端调用报表时把当前用户标识传过去即可。比如在请求时带一个业务参数 userCode,报表系统内部校验来源 IP 或内部接口凭证,再直接渲染对应数据;有更高要求时,可以把 userCode 放到 authorization header 里。绝对不要把用户名密码直接放到报表 URL 参数上,那样访问日志里会留下明文敏感信息。
如果报表组件单独部署,需要它接入公司统一认证中心,配置思路和普通 OAuth2 Client 一样:报表服务作为客户端,认证中心作为身份源,用户访问报表重定向到认证中心,认证通过后报表拿到用户信息并建立会话。这种独立部署的方式更适合包含敏感数据的报表中心,因为它可以单独控制访问范围和审计,不会让用户名在 URL 里裸奔。
4.4 网关层统一登录会不会更容易
很多微服务团队讨论单点登录时会希望“在网关层统一处理”。在短期的内部系统架构里确实可行,但不能把网关当成万能入口。
网关统一登录的本质是:请求先经过网关,网关校验 token 或 session,校验通过后把用户信息放到请求头里,传给下游服务。这对普通业务服务是友好的,服务不需要再关心认证逻辑。但问题在于你没办法强行让 NC65 这类外部重量系统把流量都走你的网关。它们有自己的登录页设置、自己的会话管理,网关层顶多帮你转发一次,但不可能做到穿透它的内部状态。
所以实践上我的建议是:自研微服务之间,尽量把登录态收敛到一套公共鉴权中间件里,不要在多个服务里各写各的 session;对外部系统,该去产品配置中心处理就配置,该写适配层就写适配层,不要试图用网关一把梭。
5. 实战排查清单与项目实施经验
5.1 一次对接联合登录的排查记录
有次上线前,用户反馈“用微信登录时经常需要登录两次”。我第一反应是回调地址写错了,但查看配置发现没问题。后来抓包发现微信回调到系统的地址时,系统内有一次 302 跳转到自定义登录页,结果把微信带过来的 code 和 state 丢了。
这类问题的根因常常不是协议配置选项,而是后端过滤器和拦截器的逻辑。系统落地一个 loginUrl 白名单时,如果回调地址不在白名单里,拦截器就会在回调被业务代码处理前,先把用户带到登录页。那“第二次登录”就变成系统自己的登录页。
排查方法是在服务器端日志里打印回调 URL 的完整参数,不要只看有没有 code,而是要看到 code 被哪个环节消费。凡是联合登录出现“回调后页面突然跳走”的,基本都是登录拦截器没放行或 state 在跳转过程中没有被正确保存。
另一个常见现象是,callback 接口能拿到 code,但调用第三方 token 接口超时。有些网络环境下第三方授权地址和 token 地址走不通,表现为前端授权没问题,后端换 token 报 connect timeout。这种问题要从部署环境配置代理上排查,而不是怀疑自己的签名算法。
5.2 SSO 项目里最容易拖进度的三个问题
SSO 项目极少有一个月就全部落地的,因为牵扯到的系统数量多,每个系统的时间表都不同。过程中最容易拖累进度的,第一是账号映射。公司里同一个人的工号在 OA 里可能叫 8 位,在 NC65 里却是 U001,在报表系统里又映射到邮箱。要提前整理一张“用户标识映射表”,每接一个系统就比对一次,不要做到一半才发现某个系统无法识别用户。
第二是时间同步。签名类认证最常见问题是系统时间偏差。企业内网服务器往往有几十秒到几分钟的时间偏差,当认证中心校验 ticket 是否过期时,会被误判。统一 NTP 时间同步应该在讲技术方案时就提出来,否则最后上线时到处都是“认证失败,时间戳无效”的报错。
第三是会话超时标准不统一。认证中心的全局会话超时可能是 2 小时,业务系统本地会话一个是 1 小时,另一个是 8 小时,用户会感到有些系统老是要重新登录,有些系统则明明很久没操作还是保持登录。要提前约定各系统的 idle timeout 和绝对超时,至少让用户体验在一个可接受范围内,不能让用户刚登录三分钟报表系统又要求他重新认证。
5.3 从“能登录”到“好登录”的一段心得
项目到后期,登录能不能正常走通已经不再是主要矛盾,矛盾和体验往往出在细节上。比如“用户修改密码后,刷新 token 还能不能继续用”“把某个用户踢下线后,它已经获得的 access_token 是不是马上失效”“用户在认证中心注销后,子系统本地会话有没有跟着清掉”。这些问题如果不做细化,上线后就会被安全部门找上门。
我比较推荐的做法是把认证模块拆成独立的认证中心,业务方只负责对接,不要在多个系统里各自存数据库和认证密钥。早期的项目为了省事,把企业微信联合登录、自建账号密码登录、OA 单点登录都写在同一个业务系统里。结果后续每加一个子系统,都要复制一整套认证代码,密钥管理混乱,一旦用户组织架构发生变化,维护成本直接起飞。改为独立认证中心后,虽然初期被业务系统推动着多写了不少接口,但后续新增子系统的效率高了很多。
给正在设计登录体系的团队一个建议:不要先写代码,先把“用户在网络里的身份是什么”“哪些系统是自己人”“第三方平台提供的是什么凭证”这三个问题画清楚。只要这个图画对了,后期 OAuth2.0 联合登录和 SSO 单点登录就算交叠再厉害,也能理得清清楚楚。
