为什么需要统一的认证授权

公司里的系统越来越多,每个系统各管一套账号密码,用户要记一堆口令,运维要维护一堆账号表,安全上还容易出现弱口令与一人多账号的混乱。认证授权的统一,是把"你是谁"(认证)和"你能干什么"(授权)集中到一处:一个身份体系打通所有业务系统,一次登录到处可用,权限由平台统一管控。

OAuth 2.0 解决的是授权问题:应用能不能替用户访问某类资源、能访问多久,通过颁发令牌来约束。单点登录(SSO)解决的是体验问题:在统一认证中心登录一次,其他系统自动获得会话。两者经常组合使用,授权码模式加 SSO 会话,是当前企业级应用的主流形态。

本文核心知识点
  • 认证确认身份,回答你是谁。
  • 授权授予权限,回答你能做什么。
  • 授权码换取令牌的中间凭证。
  • SSO一次登录,多系统共享会话。
OAuth 2.0 与单点登录配图:蓝紫渐变背景中央发光方形令牌节点,彩色连线连接左侧浏览器窗口方块、右侧手机方块与下方数据库方块,扁平科技插画
一枚统一令牌连接起浏览器、手机与服务端,正是认证授权的意象。

OAuth 2.0 的四种授权模式

OAuth 2.0 按客户端类型和信任程度,定义了四种授权模式。授权码模式最安全,适用于有后端的 Web 应用,浏览器跳转授权页、拿授权码、后端换令牌,密钥不暴露在前端;隐式模式把令牌直接给前端,现在已不推荐,被授权码加 PKCE 取代;密码模式由应用代收用户名密码换令牌,只适用于第一方应用;客户端凭证模式用密钥直接换令牌,适用于服务间调用。

// 客户端凭证模式:服务间调用
POST /token
grant_type=client_credentials
client_id=service-a
client_secret=****

选型口诀:有后端用授权码,纯前端加 PKCE,第一方应用才考虑密码模式,机器对机器用客户端凭证。理解这四种模式,就能看懂绝大多数授权协议文档。

授权码流程实战

授权码流程是前后端协作的典型。第一步,前端把用户重定向到授权中心,带上 client_id、redirect_uri、response_type=code 和随机 state;第二步,用户登录并同意授权后,授权中心带着 code 和 state 重定向回来;第三步,前端把 code 交给后端,后端用 client_id 与 client_secret 换 token;最后,应用拿 token 访问受保护资源。

// 后端用授权码换令牌
const resp = await fetch("https://auth.example.com/token", {
  method: "POST",
  body: new URLSearchParams({
    grant_type: "authorization_code",
    code: authCode,
    redirect_uri: redirectUri,
    client_id: clientId,
    client_secret: clientSecret,
  }),
});
const { access_token } = await resp.json();

state 参数用来防 CSRF:发起授权时生成随机值存好,回调时校验一致才继续,否则攻击者可以诱导用户把授权结果绑定到恶意会话上。redirect_uri 要精确校验白名单,不能只比对前缀。

令牌:Access Token 与 Refresh Token

Access Token 是访问资源的凭证,有效期短,通常十几分钟到几小时,过期后不能续期;Refresh Token 是换新令牌的凭证,有效期长,保存在后端,过期后用户需要重新登录。双令牌机制让访问令牌保持短命、降低泄露风险,同时长会话又不打扰用户。

令牌生命周期要点
  • 短命访问令牌过期快,泄露窗口小。
  • 刷新刷新令牌换新,无需重新认证。
  • 撤销登出或异常时主动失效令牌。
// 令牌过期后刷新
async function refresh() {
  const resp = await fetch("/token", {
    method: "POST",
    body: JSON.stringify({ refresh_token: storedRefreshToken }),
  });
  const data = await resp.json();
  accessToken = data.access_token;
  refreshToken = data.refresh_token || refreshToken;
}

刷新令牌也有讲究:刷新后轮换 refresh token,旧令牌作废;发现旧令牌仍被使用时,视为异常,提示用户重新登录。客户端要处理令牌并发刷新,避免多请求同时刷新导致互相覆盖。

单点登录:Session 与 JWT 的取舍

单点登录的会话载体有两条路线。Session 路线:统一认证中心维护服务端会话,各业务系统通过校验会话凭证间接确认登录,实现简单、可随时吊销,但依赖共享会话存储;JWT 路线:把用户信息签名进令牌,业务系统验签即可信任,无状态、适合跨域与微服务,但吊销麻烦、令牌膨胀。

// JWT 载荷示意
{
  "sub": "user-1001",
  "iss": "https://auth.example.com",
  "exp": 1710000000,
  "scope": ["profile", "orders"]
}

实际项目常做混合:核心业务用 Session 会话保安全可控,开放平台对外用 JWT 降低接入成本。无论哪种,认证中心都是唯一信任源,业务系统不要自己发明会话协议,否则 SSO 形同虚设。