Web 认证授权深度对比:Session / JWT / OAuth / Passkey
"用户登录"看似只是一个表单,背后却是 Web 安全里最复杂、最容易出错的子领域。Session 还是 JWT?OAuth 与 OIDC 有什么差别?Passkey 真的能干掉密码吗?刷新令牌该不该轮换?微信登录与 Apple 登录如何在同一个产品里共存?这些问题决定了产品的安全底盘是稳固还是漏风。本文从最古老的 Cookie-Session 讲到 2026 年正在普及的 FIDO2 Passkey,逐一拆解 Session、JWT、OAuth 2.1、WebAuthn 的核心机制、适用场景与典型陷阱,并补充国内国外社交登录的具体实施细节。读完你会有一张清晰的决策地图:什么场景该用什么方案,每种方案在哪里会失守,需要哪些工程动作把风险压到最低。
一、Session:最经典也最稳的有状态方案
Cookie-Session 是 Web 诞生之初就有的认证模型。流程很简单:用户登录成功后,服务端生成 session_id,把会话状态(用户 ID、权限、过期时间)存进 Redis 或数据库,response 通过 Set-Cookie 把 session_id 下发到浏览器;之后每次请求浏览器自动带上 cookie,服务端用 session_id 反查会话状态。Cookie 必须设置 HttpOnly(防 XSS 窃取)、Secure(仅 HTTPS)、SameSite=Lax 或 Strict(防 CSRF)。
Session 的最大优势是即时撤销与强一致:管理员点一下,对应 session_id 立即失效,所有终端马上掉线。劣势是横向扩展需要共享会话存储,跨域、跨子域、移动端原生 App 携带 cookie 不便。中小型单体应用、银行类强一致场景、需要复杂权限决策的后台系统应优先选 Session,简单稳定永远是优势。本站的 Cookie vs LocalStorage 详细讨论了浏览器侧的存储取舍。
二、JWT:无状态扩展的代价与好处
JWT 是过去十年最主流的无状态身份方案。服务端把用户 ID、权限、过期时间序列化为 JSON、加签名,得到一个紧凑的字符串发给客户端。客户端在每次请求 Authorization header 中带上 token,服务端只需要校验签名 + 过期时间即可识别身份,不用查数据库。优势是横向扩展简单、跨域跨服务校验统一,劣势是即时撤销难、payload 透明、token 长度大。
JWT 的核心安全要点:用 RS256 或 EdDSA(非对称签名)便于多服务校验、绝不在 payload 放敏感字段、access_token 5-15 分钟过期、refresh_token 配合 rotation、维护 kid 与密钥轮换、强制下线借助 Redis 黑名单或 user-level token version。完整细节参考本站 JWT 最佳实践。微服务架构下 JWT 是事实标准,前提是认证中心统一签发、其他服务只校验。
三、OAuth 2.1 与 OIDC:授权与认证的分工
OAuth 解决的是授权问题——A 应用让 B 服务以用户名义读取数据,但不暴露密码。它的核心是"用户在 IdP 处授权,IdP 给 client 一张 token,client 拿 token 调资源服务器"。OAuth 2.1 是 2024 年最终定型的安全收口版本,强制 PKCE、移除 implicit flow 与 password grant、所有 redirect_uri 严格匹配、refresh token 必须 rotation。
OIDC(OpenID Connect)建立在 OAuth 之上,解决的是认证问题。它在 OAuth 的 access_token 之外多发了一个 id_token(标准 JWT,含 sub、name、email 等),让 client 知道"是谁"在登录。社交登录场景实质用的就是 OIDC:Google、Apple、Microsoft、GitHub 都是标准 OIDC provider。新项目实现"使用 X 账号登录"应直接走 OIDC,不要重新发明轮子。本站的 OAuth 详解 提供了更完整的流程图。
四、Authorization Code + PKCE:当下推荐的授权流
OAuth 2.1 把 Authorization Code Flow with PKCE 定为唯一推荐流。流程:client 生成随机 code_verifier(43-128 字节)与 code_challenge(verifier 的 SHA256);用户跳到 IdP 登录,授权后 IdP 回调 redirect_uri 带上 authorization code;client 用 code + code_verifier 换 access_token,IdP 校验 code_challenge == SHA256(code_verifier) 确认是同一个 client。
PKCE 解决了"公开 client 没有 client_secret 也能安全交换 code"的问题。SPA、移动 App、桌面应用都属于公开 client,没有保密的存储。PKCE 让 code 即使被中间人截获也无法换 token,因为攻击者拿不到 verifier。原本仅给公开 client 用的 PKCE,2.1 版强制要求所有 client(包括有 secret 的后端)都启用,作为额外防御。落地时注意 verifier 必须每次随机生成,不能复用。
五、Refresh Token Rotation 与会话保持
access_token 5-15 分钟过期能限制泄露损失,但用户体验上不能让用户每 10 分钟登录一次。解决方案是 refresh_token:access_token 过期后用 refresh_token 静默换新,用户无感。但 refresh_token 寿命长(数天到数月),泄露危害比 access_token 大得多,必须配合 rotation 与异常检测。
Rotation 标准做法:每次刷新都同时下发新 refresh_token,旧的立即失效;维护 token family(同一次登录的所有 refresh_token 链);如果检测到旧的 refresh_token 被二次使用(说明被盗),立刻撤销整条 family,让所有相关 access 失效,强制用户重新登录。Auth0、Okta、Authelia 都内置 family 跟踪。自建认证服务务必在数据库为每个 refresh_token 设 family_id 与 used_at 字段,不要简单 INSERT 新行了事。
六、Passkey 与 WebAuthn:密码的终结者
Passkey 是 FIDO2 + WebAuthn + 平台账号同步的组合。注册时浏览器调用 navigator.credentials.create,安全芯片(Apple Secure Enclave、Android Titan、Windows TPM、YubiKey)生成密钥对,私钥永不离开设备,公钥发到服务端绑定账号。登录时用户用 Touch ID/Face ID/Windows Hello 解锁,浏览器调用 navigator.credentials.get 用私钥签名服务端发来的 challenge,服务端用公钥验签即可。
Passkey 的安全优势:1)无密码可泄露——服务端被拖库也只是公钥,无法登录;2)抗钓鱼——签名带 origin 绑定,钓鱼站点拿到的签名对真站点无效;3)无中间人——挑战响应一次性,重放无效。2026 年 iOS、Android、Chrome、Edge、Safari 都已稳定支持,跨设备同步走 iCloud Keychain、Google Password Manager、Microsoft Authenticator。落地建议:先做"密码 + passkey 双轨",再过渡到 passkey 优先、密码兜底,最后纯 passkey。本站 2FA 详解 讨论了 passkey 与传统二次验证的关系。
七、社交登录:Google / Apple / 微信落地细节
国内 to C 产品几乎必带微信登录。微信开放平台走 OAuth 2.0 风格但参数命名(appid/secret/code 而非 client_id/client_secret)与官方略有差异,access_token 短效(2 小时),需要缓存与定时刷新。注意区分网页授权(snsapi_userinfo)与开放平台扫码登录(snsapi_login),App 与小程序又是另一套接口。每个 unionid 关联同一开发者下多个 appid,必须以 unionid 而非 openid 作为内部用户主键。
Apple 登录是 App Store 强制:iOS App 只要支持第三方登录就必须同时提供 Sign in with Apple。Apple 走 OIDC + form_post 模式,授权完成后用 POST 把 id_token 回传到 redirect_uri,要在服务端验签并比对 nonce。Apple 提供"隐藏邮箱",给应用看到的是中转邮箱,应用必须接受这一点。Google 标准 OIDC 接口最规范,新项目可直接套用 openid-client 库。建议封装统一 IdP 适配层,对外只暴露内部 user_id 与 provider_info,业务代码无需关心三家差异。
八、决策框架与 2026 年路线图
选型框架四问:1)撤销实时性要求多高?金融级选 Session/Redis 黑名单;普通业务 JWT 短过期已足够。2)服务边界数?单体 Session,微服务 JWT/OIDC。3)用户类型?纯 Web 任选;多端混合 JWT;强体验要求 Passkey。4)合规要求?等保 3 级、PCI-DSS 倾向有状态会话与详细审计日志。新项目落地组合:Authorization Code + PKCE 走 OIDC + refresh rotation + 短 access JWT + Passkey 兜底密码。
2026 年 passkey 已度过早期落地阵痛,主流 SaaS(Google、Microsoft、Adobe、GitHub、Cloudflare)都已支持。国内蚂蚁、腾讯也在内部强推。建议路线图:Q1 产品调研接入难度,Q2 后台系统先行(员工集合可控),Q3 to C 产品双轨上线(密码 + passkey 可选),Q4 评估迁移率决定是否进入 passkey-first。同时密码场景必须升级到 argon2id、强制 12 位以上、配合泄露密码 API(HaveIBeenPwned)即时拦截,密码与 passkey 双管齐下,才是当下最稳妥的姿势。
常见问题
Session 与 JWT 到底该怎么选?
中小型单体应用、强一致性要求、需要即时撤销的场景优先 Session。微服务架构、跨域多服务校验、客户端类型多样(Web/移动/CLI)选 JWT。两者也可以混用:JWT 做无状态校验,关键操作前回查 Session 黑名单。决策标准是撤销实时性需求与服务边界数量,而不是哪个更时髦。
OAuth 2.0 与 OAuth 2.1 有什么区别?
OAuth 2.1 是 OAuth 2.0 的安全收口版本:强制 PKCE(Authorization Code 流必须)、去掉 implicit flow 与 password grant、refresh token 必须做 rotation、所有 redirect_uri 严格匹配。原本散落在多个 RFC 与 BCP 的最佳实践被收编为单一规范。新项目应直接按 2.1 实现,老系统也建议在年度安全升级中迁移。
什么是 Passkey?跟 WebAuthn 是一回事吗?
WebAuthn 是 W3C 规范,定义浏览器与安全密钥之间的 API。Passkey 是基于 WebAuthn + FIDO2 的端到端用户体验:用 Touch ID、Face ID、Windows Hello 或硬件密钥代替密码,私钥存储在设备安全芯片中,公钥注册到服务端。Apple、Google、Microsoft 三家在 2022-2024 年陆续完成系统级支持,2026 年大型互联网产品已普及。
Refresh token rotation 是什么?为什么必须做?
rotation 指每次用 refresh_token 换新 access_token 时同时下发新 refresh_token,旧的立即作废。这样即便旧 refresh_token 泄露,攻击者用一次后再被合法用户用就会触发"家族冲突",整条链条强制下线。配合 token family tracking 可以精确发现被盗时间。OAuth 2.1 已强制要求 rotation,所有自实现的认证服务都该跟上。
微信、Apple、Google 登录在国内项目里怎么搭配?
国内 to C 产品至少要支持微信登录,App Store 上架的 iOS 应用按苹果政策必须同时提供 Sign in with Apple。海外用户场景默认配 Google + Apple。微信走 OAuth 2.0 但参数命名与官方略有差异,token 短效需要缓存策略;Apple 用 form_post mode 回传,要在服务端验签 ID token;Google 走标准 OIDC 即可。三者落地建议封装统一的 IDP 适配层,对外只暴露内部用户 ID。