先从一个生活场景开始 #
想象你去一栋办公楼办事。
你走到前台,前台问:“你是谁?”你拿出身份证或员工卡。前台核对以后确认:“你确实是张三。”这一步叫 认证,英文是 Authentication。
接着你要去 18 楼财务室。保安查了一下系统,说:“你是张三没错,但你没有权限进财务室,只能去 6 楼会议室。”这一步叫 授权,英文是 Authorization。
这两个词最容易混:
登录系统时输入用户名和密码,主要是在做认证。认证成功以后,系统再判断你是不是管理员、能不能看订单、能不能删用户,这才是授权。
一次请求进系统时发生了什么 #
假设你打开一个网站,点击“我的订单”。浏览器会向后端发请求:
GET /api/orders
后端不能直接把订单返回给你。它至少要问三个问题:
- 你有没有带凭证?
- 这个凭证能不能证明你是谁?
- 你有没有权限查看这些订单?
如果你没登录,系统通常返回:
401 Unauthorized
这个名字有点误导。401 Unauthorized 在 HTTP 语境里通常表示“你还没有通过认证”,也就是系统还不知道你是谁。
如果系统知道你是谁,但你没有权限,常见返回是:
403 Forbidden
也就是“我知道你是谁,但这件事你不能做”。
凭证是什么 #
凭证就是用来证明身份或访问资格的东西。
常见凭证包括:
- 用户名和密码
- 短信验证码
- 指纹、人脸等生物识别结果
- API key
- session id
- access token
- refresh token
- JWT
- SAML assertion
不同系统用不同凭证,但目标类似:让服务器能判断请求者是否可信。
Basic Auth:最朴素的用户名密码认证 #
Basic Auth 是最简单的 HTTP 认证方式。
客户端把用户名和密码拼在一起:
alice:my-password
然后做 Base64 编码,放进请求头:
服务器收到后解码,再检查用户名和密码。
它的问题是:Base64 不是加密,只是编码。任何人拿到这段内容都能还原出用户名和密码。所以 Basic Auth 必须使用 HTTPS,否则很危险。
Basic Auth 现在仍然会出现在:
但它不适合作为现代大型应用的主要登录方案。
Digest Auth:试图改进 Basic Auth 的老方案 #
Digest Auth 的思路是:不要直接传密码,而是传一个摘要值。
大致流程是:
- 客户端请求资源。
- 服务端返回
401,并给客户端一个 challenge。 - 客户端用用户名、密码、challenge 等信息计算摘要。
- 服务端验证摘要是否正确。
它比 Basic Auth 好一点,因为不直接发送密码。但 Digest Auth 依然比较老,早期版本依赖 MD5,而 MD5 已经不适合作为现代安全基础。
现在新系统通常不会优先选 Digest Auth。
API Key:给程序用的钥匙 #
API Key 可以理解成“给程序用的一把钥匙”。
比如你注册一个地图服务,平台给你一个 key:
map_live_abc123
你调用地图 API 时带上它:
或者:
服务器收到 key 后,会去数据库或缓存里查:
- 这个 key 是否存在?
- 属于哪个用户、团队或应用?
- 是否被禁用?
- 有没有过期?
- 能访问哪些 API?
- 有没有超过限流额度?
API Key 的优点是简单,特别适合机器访问 API。
它的缺点也明显:
- 泄露后别人可以冒用。
- 很多 API key 本身不携带身份信息,服务器需要查库。
- 如果不设计过期、撤销和轮换机制,风险会越来越大。
API Key 适合服务到服务、开发者平台、自动化脚本,但不适合作为普通用户网页登录的完整方案。
Cookie 和 Session:传统网站怎么记住你 #
HTTP 本身是无状态的。
也就是说,你第一次请求网站和第二次请求网站,服务器天然并不知道这是同一个人。为了“记住你”,传统 Web 应用通常使用 session 和 cookie。
流程如下:
- 你输入用户名和密码登录。
- 服务器验证成功。
- 服务器创建一条 session 记录,例如
session_id = abc123。 - 服务器把
session_id 放进 cookie 发给浏览器。 - 浏览器之后每次访问同一个网站,都会自动带上这个 cookie。
- 服务器用
session_id 去查 session store。 - 查到了,就知道你是谁。
响应里可能有这样的头:
后续请求里浏览器会自动带上:
session 可以放在:
生产环境里常见的是 Redis,因为它快,也支持自动过期。
Session 的关键特点是:服务器要保存状态。
这很适合传统网站。但在微服务、分布式系统、多区域部署里,所有服务都要共享或识别同一套 session,复杂度会变高。
Cookie 安全属性:HttpOnly、Secure、SameSite #
如果 cookie 里放的是 session id 或 refresh token,就要认真设置安全属性。
常见设置:
几个属性的意思:
HttpOnly:JavaScript 不能通过 document.cookie 读取,降低 XSS 窃取风险。Secure:只通过 HTTPS 发送。SameSite=Lax 或 Strict:减少 CSRF 风险。Max-Age 或 Expires:设置过期时间。Path 和 Domain:限制 cookie 的发送范围。
不要把 session id 当成普通字符串随便塞到页面里。session id 一旦泄露,攻击者可能直接冒充用户。
Token:把“通行证”交给客户端 #
Token-based authentication 是现代 API 常见模式。
流程是:
- 用户登录。
- 服务器验证用户名和密码。
- 服务器签发一个 token。
- 客户端之后请求 API 时带上 token。
- 服务器验证 token。
请求头通常长这样:
这里的 Bearer 可以理解成“持有人”。Bearer token 的意思是:谁拿着这个 token,谁就能访问。
所以 Bearer token 一旦泄露,就很危险。
JWT:一种常见的 token 格式 #
JWT 是 JSON Web Token 的缩写。
它通常长这样:
xxxxx.yyyyy.zzzzz
三个部分分别是:
- Header:说明 token 类型和签名算法。
- Payload:放 claims,也就是声明信息。
- Signature:签名,用来防篡改。
Payload 可能长这样:
{
"sub": "user_123",
"email": "alice@example.com",
"role": "admin",
"exp": 1777000000
}
这里:
sub 通常表示用户主体。email 是用户邮箱。role 是角色。exp 是过期时间。
JWT 的重要特点是:它可以自包含。
服务器收到 JWT 后,可以验证签名。如果签名有效,说明 token 没被改过。如果还没过期,服务器就可以从 payload 里读出用户身份。
这带来一个好处:服务器不一定每次都查数据库。
但也带来问题:
- JWT 一旦发出,在过期前很难立即失效。
- 如果用户权限变化,旧 JWT 里可能还是旧权限。
- 如果 JWT 放了太多信息,会变大,也可能泄露不该暴露的信息。
- JWT payload 默认只是编码,不是加密,不能放密码、身份证号等敏感数据。
所以 JWT 适合短期 access token,不适合无限期使用。
Access Token 和 Refresh Token #
现代登录系统通常会把 token 分成两类:
- Access token:短期有效,用来访问 API。
- Refresh token:长期有效,用来换新的 access token。
为什么要两个?
因为 access token 每次都要拿出去用,暴露面更大,所以应该短命。refresh token 更敏感,使用频率更低,可以用更严格的方式保存。
典型流程:
- 用户登录。
- 服务器返回 access token 和 refresh token。
- 客户端用 access token 调用 API。
- access token 过期后,API 返回
401。 - 客户端用 refresh token 请求新的 access token。
- 用户不用重新输入密码。
常见有效期:
- access token:几分钟到一小时。
- refresh token:几天到几周,具体取决于风险。
浏览器应用里,refresh token 通常建议放在 HttpOnly + Secure + SameSite cookie 里。不要随手放进 localStorage,因为一旦页面出现 XSS,JavaScript 可以读取 localStorage。
更成熟的系统还会做 refresh token rotation:每次刷新时发一个新的 refresh token,并让旧的失效。这样可以降低长期 token 被偷后的风险。
OAuth2:不是登录,而是授权 #
OAuth2 是最容易被误解的概念之一。
很多人说“用 OAuth2 登录”,这在日常表达里可以理解,但严格说 OAuth2 本身主要解决的是授权,不是认证。
OAuth2 回答的问题是:
某个应用能不能代表用户访问某些资源?
例子:你使用一个在线文档工具,它想读取你的 Google Drive 文件。
流程大概是:
- 文档工具把你重定向到 Google。
- Google 问你:“是否允许这个应用读取你的 Drive 文件?”
- 你点击允许。
- Google 给文档工具一个 authorization code。
- 文档工具拿 code 去换 access token。
- 文档工具用 access token 调 Google Drive API。
这个 access token 证明的是:
这个应用被允许访问某些资源。
它不一定证明:
当前用户是谁。
这就是 OAuth2 和登录认证的区别。
OAuth2 里常见角色:
- Resource Owner:资源所有者,通常是用户。
- Client:想访问资源的应用。
- Authorization Server:负责授权和发 token 的服务器。
- Resource Server:保存资源的服务器,例如 Google Drive API。
OpenID Connect:在 OAuth2 上补上“你是谁” #
OpenID Connect,简称 OIDC,是建立在 OAuth2 之上的身份层。
OAuth2 给 access token,让应用访问资源。OIDC 额外引入 ID token,让应用知道用户是谁。
当你点击“Sign in with Google”时,背后常见的是 OIDC。
流程大致是:
- 你的应用把用户跳转到 Google。
- 用户在 Google 登录。
- Google 返回 authorization code。
- 应用用 code 换 token。
- Google 返回 access token 和 ID token。
- 应用验证 ID token。
- 应用从 ID token 里读出用户身份。
ID token 通常也是 JWT,里面可能有:
{
"sub": "google-user-id",
"email": "alice@gmail.com",
"name": "Alice"
}
所以:
- OAuth2:重点是授权访问资源。
- OIDC:重点是确认用户身份。
SSO:登录一次,到处可用 #
SSO 是 Single Sign-On,单点登录。
它不是一种具体协议,而是一种体验:
用户登录一次,就能访问多个系统。
比如你登录公司账号以后,可以访问:
- 邮箱
- 网盘
- Jira
- Confluence
- Salesforce
- 内部报表系统
这些系统不一定自己保存你的密码,而是信任同一个身份提供方,比如 Okta、Microsoft Entra ID、Google Workspace。
SSO 背后常用协议包括:
SAML:企业系统里常见的老牌 SSO 协议 #
SAML 是 Security Assertion Markup Language。
它是基于 XML 的身份协议,在企业系统里非常常见。
典型流程:
- 你访问 Salesforce。
- Salesforce 发现你没登录,把你重定向到公司身份系统。
- 你在公司身份系统登录。
- 身份系统返回一个 SAML assertion。
- Salesforce 验证 assertion。
- 验证通过后,你进入 Salesforce。
SAML 比 OIDC 更老,也更重,但仍然广泛用于企业、政府、教育和遗留系统。
OIDC 更现代,更适合 Web、移动端和 API 生态。
常见误区 #
误区一:JWT 是一种认证方式。
更准确地说,JWT 是一种 token 格式。它可以用于认证系统,也可以用于信息交换,但它本身不是完整登录方案。
误区二:Bearer token 就是 JWT。
Bearer 是 token 的使用方式。JWT 是 token 的一种格式。Bearer token 可以是 JWT,也可以只是随机字符串。
误区三:OAuth2 是登录协议。
OAuth2 主要是授权框架。真正补上用户身份认证的是 OpenID Connect。
误区四:有了 JWT 就完全不需要数据库。
不一定。如果你要支持主动登出、封号、权限实时变化、token 撤销、设备管理,仍然可能需要数据库、缓存、黑名单或 token version。
误区五:把 token 放 localStorage 很方便,所以没问题。
方便不等于安全。localStorage 可以被同源 JavaScript 读取,一旦出现 XSS,token 容易被偷。浏览器场景下,敏感长期凭证更常见的做法是使用安全 cookie。
新手怎么选方案 #
如果你做的是普通服务端渲染网站:
- 可以优先考虑 session + cookie。
- cookie 设置
HttpOnly、Secure、SameSite。 - session 存 Redis 或数据库。
如果你做的是前后端分离 Web 应用:
- 可以使用短期 access token。
- refresh token 放安全 cookie。
- 注意 CSRF 和 XSS 防护。
- 不要把长期 refresh token 放 localStorage。
如果你做的是移动 App:
- 可以使用 access token + refresh token。
- token 存在系统安全存储里,例如 Keychain 或 Keystore。
- refresh token 要支持撤销和轮换。
如果你做的是开放平台 API:
- 使用 API key 或 OAuth2。
- API key 要支持权限、过期、撤销、轮换、限流。
- 第三方代表用户访问资源时,更适合 OAuth2。
如果你要做“使用 Google 登录”:
- 用 OpenID Connect。
- 验证 ID token。
- 不要只拿 access token 当用户身份。
如果你接企业客户:
- 准备支持 SSO。
- 现代客户常用 OIDC。
- 传统企业系统可能要求 SAML。
一张总表 #
| 名词 | 它是什么 | 主要解决什么 |
|---|
| Authentication | 认证 | 确认你是谁 |
| Authorization | 授权 | 决定你能做什么 |
| Basic Auth | HTTP 认证方式 | 用用户名密码证明身份 |
| Digest Auth | HTTP 认证方式 | 用摘要减少明文密码传输 |
| API Key | 凭证 | 让程序调用 API |
| Cookie | 浏览器机制 | 自动保存和发送小段数据 |
| Session | 服务端状态 | 记住登录用户 |
| Bearer Token | token 使用方式 | 持有者即可访问 |
| JWT | token 格式 | 携带 claims 并支持签名 |
| Access Token | 短期访问凭证 | 调用 API |
| Refresh Token | 长期续期凭证 | 换新的 access token |
| OAuth2 | 授权框架 | 允许应用代表用户访问资源 |
| OpenID Connect | 身份协议 | 在 OAuth2 上确认用户身份 |
| SSO | 登录体验 | 登录一次访问多个系统 |
| SAML | 身份协议 | 企业 SSO 身份断言 |
最后用一句话记住 #
认证是“你是谁”,授权是“你能做什么”;session 是服务器记住你,token 是你带着通行证来;JWT 是一种通行证格式,OAuth2 是授权别人办事,OIDC 是用 OAuth2 做登录身份,SSO 是登录一次到处可用。
延伸资料 #
- MDN: HTTP authentication, for the
401 challenge flow and Authorization header model. - MDN: Secure cookie configuration, for
HttpOnly, Secure, SameSite, and cookie expiry guidance. - IETF RFC 6749: The OAuth 2.0 Authorization Framework.
- IETF RFC 7519: JSON Web Token.
- OpenID Foundation: OpenID Connect Core 1.0, especially the role of ID Token.
- OWASP Authentication Cheat Sheet and Session Management Cheat Sheet.
- OWASP JSON Web Token Cheat Sheet, especially token leakage, rotation, and refresh-token risk controls.