Token、Cookie、Session、JWT、单点登录 是什么?有什么作用?你一般是怎么做的?以及你是怎么存储的呢?那你又是怎么保证 它 的安全的呢?

0 概念#

1. 认证(Authentication)#
“你是谁?” —— 验证用户身份的真实性
实现机制:
- 登录时:用户提供用户名+密码 → 后端校验数据库
- 后续请求:通过以下凭证证明“我就是刚才登录的那个人”:
| 凭证类型 | 如何工作 | 存储位置 | 特点 |
|---|---|---|---|
| Session + Cookie | 后端创建 Session(存用户ID),返回 JSESSIONID Cookie | 浏览器自动管理 Cookie | 有状态(服务端需存储 Session) |
| Token(如 JWT) | 后端签发包含用户信息的加密 Token | 前端存 localStorage 或 Cookie | 无状态(Token 自包含信息) |
认证成功 = 凭证有效 + 能解析出合法用户身份
⚠️ 注意:Token 本身不是认证,验证 Token 才是认证!
2. 授权(Authorization)#
“你能做什么?” —— 定义用户的角色和权限规则
实现机制:
- 在用户注册或管理后台配置权限策略,例如:
- RBAC(基于角色的访问控制):
用户A → 角色=管理员 → 权限=删除用户 - ABAC(基于属性的访问控制):
仅本人可编辑自己的资料
- RBAC(基于角色的访问控制):
- 这些规则存储在数据库或配置文件中,不随请求传递
授权是“静态策略”,发生在系统设计或管理配置阶段
3. 鉴权 / 权限控制(Access Control)#
“这次操作允许吗?” —— 在具体请求时执行权限检查
实现机制(运行时检查):
后端收到带凭证的请求后:
- 先认证:解析 Cookie 或 Token → 得到
userId = 1001 - 再鉴权:根据授权策略判断是否允许操作
1 HTTP 基本鉴权#
在 HTTP 中,基本认证方案(Basic Access Authentication) 是一种允许客户端(通常是网页浏览器)通过提供用户名和密码来验证身份的简单机制。
由于安全性极低,几乎所有面向公众的线上网站都不会使用该方案,仅用于了解或内部低风险场景。
1.1 认证流程图#

1.2 认证步骤解析#
-
客户端(如浏览器): 向服务器请求一个受保护的资源,初始请求不包含认证信息。
GET /list/ HTTP/1.1Host: www.baidu.com -
服务器:发现请求未认证,返回
401 Unauthorized状态码,并在响应头中要求客户端进行 Basic 认证。HTTP/1.1 401 Unauthorizedwww-Authenticate: Basic realm= "baidu.com"- Basic 表示认证方案;
- realm=“baidu.com” 定义了安全域,提示用户输入该域的凭据。
-
客户端: 浏览器自动弹出登录对话框,用户输入用户名和密码后,客户端将凭据按
用户名:密码格式进行 Base64 编码(不是加密),并添加到请求头中重新发送。GET /list/ HTTP/1.1Host: www.baidu.comAuthorization: Basic aHR0cHdhdGNoOmY= // 示例:对应 "httpwatch:f" -
服务器: 解码
Authorization头中的 Base64 字符串,验证用户名和密码。若正确,返回请求的资源。HTTP/1.1 200 OKContent-Type: application/json...
1.3 优缺点#
实现简单,所有主流浏览器和 HTTP 客户端均原生支持。
- 极度不安全:
- 凭据仅经 Base64 编码(可轻松解码还原为明文),等同于明文传输。
- 若未使用 HTTPS,任何中间人(如公共 Wi-Fi)都可窃取用户名和密码。
- 即使使用 HTTPS,凭证无时效性、无法撤销,一旦泄露即可长期冒用。
- 无法主动注销:
- 浏览器会缓存 Basic Auth 凭据,直到关闭标签页、窗口或清除浏览器数据。
- 没有标准方法通过代码强制清除该凭据,用户体验差。
- 无定制化 UI:
- 登录框由浏览器强制弹出,样式和交互无法自定义,不适合现代 Web 应用。
1.4 使用场景#
内部网络,或者对安全要求不是很高的网络。
2 Session-Cookie 鉴权#
Session-Cookie 是一种经典的 Web 认证机制,利用服务端的 Session(会话数据)和客户端的 Cookie(会话标识)协同工作,实现用户身份的持续验证。
⚠️ 注意:Session 数据本身存储在服务端,Cookie 仅存储 Session ID,二者分工明确。
2.1 什么是 Cookie#
HTTP 是无状态协议,服务器无法天然识别多次请求是否来自同一用户。
Cookie 是服务器通过 Set-Cookie 响应头下发给浏览器的小段数据,浏览器会在后续同域请求中自动携带它,从而实现“状态记忆”。
特点:
-
存储在客户端(浏览器),可被用户查看或清除;
-
大小限制:单个 Cookie ≤ 4KB,每个域名通常最多 20~50 个;
-
作用域:默认严格匹配不可跨域,但可通过设置
Domain=.example.com实现一级/二级域名共享;一级域名(顶级域名):指的是 example.com。
二级域名:指的是 a.example.com 或 b.example.com。
跨域:指的是完全不同的站点,例如 example.com 和 google.com。
-
安全性依赖配置:
HttpOnly:防止 JavaScript 读取(防 XSS)Secure:仅通过 HTTPS 传输SameSite=Lax:缓解 CSRF 攻击
在不同的框架下有不同的配置方式(Spring Boot 可以在
application.properties中配置)浏览器 DevTools → Application → Cookies
检查目标域名下的 Cookie 是否有安全配置
2.2 什么是 Session#
Session 的抽象概念是会话,是无状态协议通信过程中,为了实现中断/继续操作,将用户和服务器之间的交互进行的一种抽象;
具体来说,是服务端为每个用户维护的会话状态对象,用于存储登录状态、用户信息、购物车等内容。
特点:
-
存储在服务端(内存、Redis、数据库等),大型网站一般有专门的 Session 服务器集群来保存用户会话;
如果你的应用是分布式的(部署在多台机器上),或者对用户体验要求很高(不能因为重启服务器就掉线),或者需要实现单点登录(SSO),那么必须使用 Redis 来存储 Session。
-
安全性高:敏感数据不出服务端;
-
生命周期可控:可设置过期时间,支持主动销毁(如用户登出);
服务器判定 Session 失效的逻辑通常被称为“滑动过期”(Sliding Expiration)。 计时起点: 不是从你创建 Session(登录)的那一刻开始算,而是从该 Session 最后一次被访问(Last Accessed Time)的时间点开始倒计时。
在实际开发中,这个“最大非活动时间”可以从多个地方设置,它们之间存在优先级关系。
优先级 设置方式 说明 典型单位 最高 代码设置 在程序运行时动态设置(如 session.setMaxInactiveInterval(秒)或session_set_cookie_params(秒))。秒 中等 项目配置 在项目的配置文件中设置(如 web.xml中的<session-timeout>或 PHP 的session.gc_maxlifetime)。分钟 / 秒 最低 服务器/容器默认 如果上述两者都没设置,则使用服务器默认值(如 Tomcat 默认通常为 30 分钟)。 分钟 -
不依赖客户端存储能力(与 Token 方案对比)。
2.3 Session-Cookie 的认证流程图#

2.4 Session-Cookie 认证步骤解析#
- 客户端:发送用户名/密码到
/login接口; - 服务器:
- 验证凭据;
- 创建 Session(如
{ userId: 1001, role: 'user' }),存入内存/Redis; - 生成唯一标识字符串会话身份凭证
session_id(如abc123); - 通过响应头
Set-Cookie: sid=abc123; HttpOnly; Secure返回;
- 客户端:浏览器自动保存 Cookie,后续请求自动携带;
- 服务器:每次请求解析 Cookie 中的
sid,查询服务端 Session,验证有效性,恢复用户上下文; - 至此客户端与服务器的通信变成有状态的通信。
2.5 Session-Cookie 的优点#
- 安全性较高:用户敏感信息不出服务端;
- 易于管理:
- 登出 = 删除服务端 Session;
- 权限变更可立即生效;
- 浏览器自动管理 Cookie,前端无需手动处理凭证;
- 兼容性好:所有 Web 浏览器原生支持。
2.6 Session-Cookie 的缺点#
- 依赖 Cookie:若用户禁用 Cookie,则无法工作(但现代用户极少禁用);
- 易受 CSRF 攻击:因浏览器自动携带 Cookie,需额外防护(如 CSRF Token);
- 服务端有状态:
- 需要存储 Session(内存/Redis),增加运维成本;
- 不适合纯无状态微服务架构;
- 移动端原生 App 不适用:原生 App 通常使用 Token 机制,而非 Cookie。
2.7 使用场景#
- 传统 Web 应用(如后台管理系统、电商网站);
- 需要强会话控制的场景(如金融系统要求即时登出);
- 团队熟悉有状态架构,且能接受 Redis 等 Session 存储成本。
2.8 前端常用的 Session 库推荐#
- Express (Node.js 最经典的框架) express-session
- Koa (Express 原班人马开发的新一代框架) koa-session
3 Token 鉴权#
传统 Session-Cookie 机制存在明显短板:服务端需维护会话状态,难以水平扩展;在分布式系统中需引入 Redis 等外部存储,增加架构复杂度。为解决这些问题,基于 Token 的无状态鉴权机制应运而生。
3.1 Token(令牌)#
Token 是服务端在用户认证成功后签发的一串唯一凭证字符串。客户端后续请求携带此 Token,服务端仅需验证其有效性即可授权访问资源。
一句话概括:Token 是访问受保护 API 接口所需的资源凭证。
早期自定义 Token 的组成:
uid (用户唯一的身份标识) + time (当前时间的时间戳) + sign (基于上述信息 + 密钥生成的哈希签名)
⚠️ 注意:这种“自定义 Token”仍需服务端查库验证用户权限,并非真正的无状态。真正实现无状态的是 JWT(见第 4 节)。
Token 流程图:

- 客户端: 输入用户名和密码请求登录校验;
- 服务器: 收到请求,去验证用户名与密码;验证成功后,服务端会签发一个 Token 并把这个 Token 发送给客户端;
- 客户端: 收到 Token 以后需要把它存储起来,web 端一般会放在 localStorage 或 Cookie 中,移动端原生 APP 一般存储在本地缓存中;
- 客户端发送请求: 向服务端请求 API 资源的时候,将 Token 通过 HTTP 请求头 Authorization 字段或者其它方式发送给服务端;
- 服务器: 收到请求,然后去验证客户端请求里面带着的 Token ,如果验证成功,就向客户端返回请求的数据,否则拒绝返还(401)。
Token 的优点:
- 无状态 & 可扩展:服务端无需存储会话,天然支持分布式部署。
- 跨平台支持:适用于 Web、移动端、IoT 等任意客户端。
- 避免 CSRF:若 Token 存于 localStorage(非 Cookie),可规避 CSRF 攻击。
- 跨域友好:不受同源策略限制,适合微服务架构。
Token 的缺点:
- 带宽消耗:Token 体积通常大于 Session ID(如
sid=abc123)。 - 性能开销:每次请求需验签(但远低于查库成本)。
- 有效期管理:为降低盗用风险,Access Token 需设置较短有效期 → 引入 Refresh Token 机制。
3.2 Refresh Token(刷新令牌)#
为平衡安全性与用户体验,采用双 Token 机制:
| Token 类型 | 作用 | 有效期 | 存储位置 | 安全要求 |
|---|---|---|---|---|
| Access Token | 访问业务 API | 短(如 15 分钟) | 前端内存/LocalStorage | 可宽松传输 |
| Refresh Token | 获取新的 Access Token | 长(如 7 天) | HttpOnly Cookie / 安全存储 | 高(需严格校验) |
Token 的客户端存储方式:
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| HttpOnly Cookie | 自动携带、防 XSS | 易受 CSRF 攻击 | Web 端 SSO(同主域) |
| LocalStorage | 避免 CSRF、前端易操作 | 无法跨子域、易受 XSS | 前后端分离单页应用 |
SSO 场景必选 Cookie:通过
Domain=.example.com实现子域共享(如a.example.com与b.example.com)。
Refresh Token 流程图:

- 客户端: 输入用户名和密码请求登录校验;
- 服务端: 收到请求,验证用户名与密码;验证成功后,服务端会签发一个
Access Token和Refresh Token并返回给客户端; - 客户端: 把
Access Token和Refresh Token存储在本地; - 客户端发送请求: 请求数据时,携带
Access Token传输给服务端; - 服务端:
- 验证 Access Token 有效:正常返回数据
- 验证 Access Token 过期:拒绝请求
- 客户端 ( Access Token 已过期) : 则重新传输 Refresh Token 给服务端;
- 服务端 ( Access Token 已过期) : 验证 Refresh Token ,验证成功后返回新的 Access Token 给客户端;
- 客户端: 重新携带新的 Access Token 请求接口;
3.3 Token 和 Session-Cookie 的区别#
| 维度 | Session-Cookie | Token (JWT) |
|---|---|---|
| 存储位置 | 服务端(内存/Redis) | 客户端(Cookie/LocalStorage) |
| 状态 | 有状态 | 无状态 |
| 扩展性 | 差(需共享 Session) | 极佳(天然分布式) |
| 安全性 | 易受 CSRF 攻击 | 避免 CSRF(若不用 Cookie) |
| 适用场景 | 同域 Web 应用 | 跨域、移动端、微服务 |
4 JWT(JSON Web Token)鉴权#
自定义 Token 后续请求仍需查库(Token 映射表),未完全实现无状态。JWT 通过“自包含”设计彻底解决此问题。
无论是自定义 Token 还是 JWT,在用户“首次登录”时,服务端都必须查询数据库验证用户名和密码。
4.1 什么是 JWT#
JWT(RFC 7519)是一种开放标准,用于在各方之间安全地传输 JSON 声明。其核心思想:将用户信息直接编码进 Token,服务端无需查库。
4.2 JWT 的组成#
JWT 由三部分 Base64Url 编码的字符串组成,以 . 分隔:
xxxxx.yyyyy.zzzzz1、Header(头部)
{ "alg": "HS256", // 签名算法 "typ": "JWT" // Token 类型}2、Payload(负载)
包含声明(Claims),分为三类:
- 注册声明(标准字段):
iss:签发者exp:过期时间(必须设置!)sub:主题(如用户ID)iat:签发时间
- 公共声明:自定义但需避免冲突(如
name,role) - 私有声明:双方约定的字段
⚠️ 重要:Payload 默认不加密(仅 Base64 编码),敏感信息需额外加密!
3、Signature(签名)
对 base64UrlEncode(header) + "." + base64UrlEncode(payload) 使用密钥签名:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), 'your-secret-key')作用:确保 Token 未被篡改(服务端用相同密钥验签)。
4.3 JWT 的使用方式#
- 存储:Cookie(SSO) 或 LocalStorage(单页应用)
- 传输:HTTP Header
Authorization: Bearer <token>
4.4 JWT 的认证流程图#
其实 JWT 的认证流程与 Token 的认证流程差不多,只是不需要再单独去查询数据库查找用户用户;简要概括如下:

4.5 JWT 的优点#
- 真正的无状态:服务端无需存储任何会话数据。
- 减少数据库查询:用户信息内置于 Token。
- 跨语言兼容:所有主流语言均有标准库支持。
4.6 JWT 的缺点#
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 无法主动失效 | 服务端无状态,无法撤销已签发 Token | 1. 设置短有效期(15-30分钟) 2. 引入 Redis 黑名单(记录失效 Token) |
| 信息更新延迟 | Payload 是签发时的快照 | 1. 仅存必要字段(如 userID) 2. 敏感操作前二次验证 |
4.7 前端常用的 JWT 库推荐#
- 使用 express:express-jwt
- 使用 koa:koa-jwt
这些中间件自动拦截请求、验证 JWT,并将 payload 挂载到 req.user / ctx.state.user。
5 单点登录(Single Sign On)和联合身份(Federated Identity)#
1、单点登录SSO是用户体验目标
一次登录,全网通行:用户只需在身份提供方(IdP)认证一次,即可访问所有相互信任的应用系统(SP),无需重复登录。
2、联合身份是架构模式
联合身份(Federated Identity)是指用户使用一个身份提供商(Identity Provider, IdP),例如微信、Google、企业 AD 等,来登录多个依赖方(Relying Party, RP)或服务提供商(Service Provider, SP)。
典型场景:
- 使用“微信账号登录 CSDN”
- 使用“公司账号登录内部 OA、GitLab、Jira”
核心机制:
-
用户身份由 IdP 统一管理;
-
RP 信任 IdP 的认证结果,不直接处理用户密码;
-
通过标准协议交换身份信息,如:
- OAuth 2.0 + OpenID Connect(互联网常用)
- SAML(企业级常用)
- CAS(高校/政府系统,逐渐被 OIDC/SAML 替代)
SAML 与 OIDC 是竞争关系,都是联合身份协议,但技术栈不同(XML vs JSON/JWT)。
跨域的SSO通常就是通过联合身份实现的。
5.1 SSO 的典型应用场景#
- 企业办公(B2B): 员工使用一套账号密码登录邮箱、钉钉/飞书、ERP、CRM 等所有内部系统。
- 互联网产品矩阵(B2C): 比如腾讯系,QQ 登录后,腾讯视频、QQ音乐可能自动就登录了(同主域下的 Cookie 共享或 Token 同步)。
- 政务/教育: 学生用学号登录一次,即可访问图书馆、教务系统、校园网;市民用一个账号办理各种政务事项。
5.2 SSO 技术选型指南#
| 场景 | 推荐方案 | 原因 |
|---|---|---|
同主域 Web 应用(如 *.baidu.com) | Cookie + Session | 利用 Cookie Domain 共享,实现简单 |
| 跨域系统 / 微服务 | Cookie + JWT | 无状态、跨域友好、部署成本低 |
| 第三方登录(微信/QQ) | OAuth 2.0 + OIDC | 标准化授权协议,支持用户授权 |
| 企业级高安全需求 | CAS / SAML | 支持单点登出、多因素认证、审计日志 |
混合架构(JWT + Redis)#
尽管 JWT 无状态,但企业级 SSO 需支持强制登出和实时权限变更,因此常采用混合模式。
原理:
- Token 是一个随机字符串(如
a1b2c3d4...),本身不包含用户信息; - 服务端在数据库/缓存中维护一张 会话表(Session Table),记录:
- Token → 用户 ID
- 创建时间、过期时间
- 是否激活(用于唯一登录)
- 设备/IP 等元数据
- 验证方式:每次请求,服务端查会话表,确认 Token 是否存在且未过期。
流程:
- 用户在 SSO 中心登录成功。
- 服务端生成 随机 Token(如 UUID)。
- 将
Token → {userId, roles, ...}存入 Redis,设置 TTL。 - 通过
Set-Cookie下发 Token(Domain=.company.com)。 - 业务系统收到请求后:
- 携带 Token 查询 Redis
- 若存在 → 授权;若不存在 → 401
优势:
- 保留 Token 跨域能力
- 可主动删除会话(用户登出、管理员踢人);
- 支持唯一登录、多端管理;
- 可动态更新过期时间(滑动过期)。
缺点:有状态、需查库,性能略低。
同域下的 SSO(主域名相同)#
当百度网站存在两个相同主域名下的贴吧子系统 tieba.baidu.com 和网盘子系统 pan.baidu.com 时,以下为他们实现 SSO 的步骤:
- 客户端: 用户访问某个子系统时(例如
tieba.baidu.com),如果没有登录,则跳转至 SSO 认证中心提供的登录页面进行登录; - 服务端: 登录认证后,服务端把登录用户的信息存储于 Session 中,并且附加在响应头的
Set-Cookie字段中,设置 Cookie 的 Domain 为.baidu.com; - **客户端:**再次发送请求时,携带主域名 Domain 下的 Cookie 给服务器,此时服务端就可以通过该 Cookie 来验证登录状态了;
跨域下的 SSO(主域名不同)#
当主域名不同,需CAS(Central Authentication Service)中央授权服务。
CAS 核心票据:
| 票据 | 全称 | 作用 | 存储位置 |
|---|---|---|---|
| TGT | Ticket Granting Ticket | 用户在 CAS 的全局登录凭证 | CAS 服务端 Session |
| TGC | Ticket Granting Cookie | TGT 的引用(Session ID) | 浏览器 Cookie (Domain=sso.com) |
| ST | Service Ticket | 一次性访问某服务的票据 | URL 参数传递 |
单点登录下的 CAS 流程图:

- 用户访问 系统 A(如
taobao.com),未登录。 - 系统 A 重定向用户到 CAS 认证中心(如
sso.com/login?service=taobao.com)。 - 用户在 CAS 登录成功后,CAS:
- 生成一个 TGT(Ticket Granting Ticket,存服务端)
- 设置 TGC(Ticket Granting Cookie,存浏览器)
- 生成一个一次性 ST(Service Ticket,如
ST-12345)
- CAS 重定向用户回系统 A,URL 带上 ST:
https://taobao.com?ticket=ST-12345 - 系统 A 拿着 ST 去 CAS 后台验证(通过 HTTPS 请求
/serviceValidate?ticket=ST-12345&service=taobao.com)。 - CAS 验证 ST 有效后,返回用户身份信息(如用户名)。
- 系统 A 创建自己的会话(如 Session 或 JWT),并让用户登录。
CAS 一般提供四个接口:
/login:登录接口,用于登录到中央授权服务/logout:登出接口,用于从中央授权服务中登出/validate:用于验证用户是否登录中央授权服务/serviceValidate:用于让各个 Service 验证用户是否登录中央授权服务
CAS 安全增强:
- ST 一次性:验证后立即失效,防止重放攻击。
- 绑定服务地址:ST 仅对指定
service有效。 - 登出全局同步:CAS
/logout接口通知所有 SP 销毁局部会话。
5.3 SSO 的优缺点#
优点:
- 用户体验极佳: 减少记忆多套密码的负担,切换系统无缝衔接。
- 安全性提升: 企业 IT 管理员可以集中管理账号,一旦员工离职,只需在一处禁用账号,所有系统权限立即失效(防止“僵尸账号”风险)。
- 降低管理成本: 减少了各业务系统重复开发登录模块的工作量。
缺点与风险:
- 单点故障(SPOF): 如果 SSO 认证中心挂了,所有依赖它的系统都无法登录。因此生产环境通常需要部署 SSO 集群。
- 安全边界扩大: 一旦用户的 SSO 凭证(Token)被盗取,攻击者可能访问所有关联系统。因此 SSO 通常会配合多因素认证(MFA)(如短信验证码、指纹)来加固安全。
6 OAuth 2.0#
在浏览网站时,我们常看到“使用微信/QQ/微博账号登录”的选项。这种无需向第三方网站提供账号密码即可登录的方式,背后依赖的正是 OAuth 2.0 协议。
⚠️ 重要澄清: OAuth 2.0 本质是“授权”协议,不是“认证”协议。 它解决的问题是:“是否允许第三方应用代表我访问我的资源?” 而“登录”(即身份认证)通常是基于 OAuth 2.0 扩展的 OpenID Connect(OIDC)实现的。但在实践中,许多平台(如微信、QQ)将授权与认证结合使用,因此用户感知为“第三方登录”。
协议 目的 返回的关键令牌 OAuth 2.0 授权(Authorization)
“是否允许 App 访问我的资源?”access_token(用于访问 API)OIDC 认证 + 授权
“用户是谁?并允许 App 访问其资源”id_token(JWT,含用户身份) +access_token
OAuth 2.0 是目前最主流的开放授权标准(RFC 6749),相比 1.0 版本,流程更简洁、安全性更高。
6.1 什么是 OAuth 2.0?#
OAuth 2.0 是一个开放授权框架,允许用户(资源拥有者)授权第三方应用(客户端)访问其在服务提供者(如微信、QQ)上的受保护资源,而无需将用户名和密码透露给第三方。
核心角色:
| 角色 | 说明 |
|---|---|
| Resource Owner(资源拥有者) | 通常是用户,拥有对资源的控制权 |
| Client(客户端) | 请求访问用户资源的第三方应用(如 CSDN、掘金) |
| Authorization Server(授权服务器) | 验证用户身份并颁发访问令牌(如 open.weixin.qq.com) |
| Resource Server(资源服务器) | 存储用户数据的服务(如微信用户信息 API) |
典型场景: 用户点击“微信登录” → 微信授权服务器弹出确认页 → 用户同意 → 第三方应用获得 Token → 调用微信 API 获取用户昵称/头像。
令牌(Token) vs 密码(Password)
| 特性 | 令牌(Token) | 密码(Password) |
|---|---|---|
| 有效期 | 短期(如 2 小时),自动过期 | 长期有效,除非用户修改 |
| 可撤销性 | 可随时由用户或服务端撤销 | 无法单独撤销,需改密 |
| 权限范围(Scope) | 可限制为只读、特定接口等 | 通常拥有账户全部权限 |
| 用途 | 仅用于 API 访问 | 用于登录 + 全权限操作 |
OAuth 2.0 对于如何颁发令牌的细节,规定得非常详细。具体来说,一共分成四种授权模式 (Authorization Grant) ,适用于不同的互联网场景。
6.2 授权码模式#
授权码(Authorization Code Grant) 方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌并用令牌访问资源。
这是 最安全、最推荐 的 OAuth 2.0 流程,适用于有后端的 Web 应用(如传统网站、前后端分离系统)。
授权码通过前端传送,Access Token 始终在服务端交换,不会暴露给浏览器。
流程图:

-
客户端:
打开网站 A,点击登录按钮,请求 A 服务,A 服务重定向 (重定向地址如下) 至授权服务器 (如QQ、微信授权服务)。
GET https://qq.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read上面 URL 中,
response_type参数表示要求返回授权码(code),client_id参数让 B 知道是谁在请求,redirect_uri参数是 B 接受或拒绝请求后的跳转网址,scope参数表示要求的授权范围(这里是只读) -
授权服务器:
授权服务网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户表示同意,这时授权服务网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码,就像下面这样。HTTP/1.1 302 FoundLocation: https://a.com/callback?code=AUTHORIZATION_CODE上面 URL 中,
code参数就是授权码。 -
网站 A 服务器:
拿到授权码以后,就可以向
授权服务器 (qq.com)请求令牌,请求地址如下:POST /oauth/token HTTP/1.1Host: qq.comContent-Type: application/x-www-form-urlencodedgrant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2Fa.com%2Fcallback&client_id=CLIENT_ID&client_secret=CLIENT_SECRET🔒 更安全的做法(RFC 推荐): 将
client_id和client_secret放在 HTTP Basic Auth 头中,而不是 body:POST /oauth/token HTTP/1.1Host: qq.comAuthorization: Basic base64encode("CLIENT_ID:CLIENT_SECRET")Content-Type: application/x-www-form-urlencodedgrant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2Fa.com%2Fcallback上面 URL 中,
client_id参数和client_secret参数用来让授权服务器确认 A 的身份(client_secret参数是保密的,因此只能在后端发请求),grant_type参数的值是AUTHORIZATION_CODE,表示采用的授权方式是授权码,code参数是上一步拿到的授权码,redirect_uri参数仅用于验证(必须与Step1一致),不用于跳转。
-
授权服务器:
收到请求以后,验证通过,就会颁发令牌。具体做法是直接在 HTTP 响应体中返回 JSON,不涉及任何重定向!
HTTP/1.1 200 OKContent-Type: application/jsonCache-Control: no-storePragma: no-cache{"access_token": "ACCESS_TOKEN","token_type": "bearer","expires_in": 2592000,"refresh_token": "REFRESH_TOKEN","scope": "read","uid": "100101"}上面 JSON 数据中,
access_token字段就是令牌,A 网站在后端拿到。
| 场景 | 正确做法 |
|---|---|
调用 /token 获取令牌 | 必须用 POST,参数放在 请求体,Content-Type: application/x-www-form-urlencoded |
调用 /authorize 获取授权码 | 用 GET,参数放在 URL 查询字符串(因为要重定向浏览器) |
🔒 安全增强:PKCE(RFC 7636) 对于无后端的 SPA 应用(如纯 React/Vue),即使使用授权码模式,也必须配合 PKCE(Proof Key for Code Exchange)防止授权码拦截攻击。此时不再依赖
client_secret。
6.3 密码模式(Resource Owner Password Credentials Grant)#
如果你高度信任某个应用,OAuth 2.0 也允许用户直接向第三方应用提供账号和密码,由应用代为申请 Token。
步骤详解:
-
客户端:
A 网站要求用户提供
授权服务器(qq.com)的用户名和密码。拿到以后,A 就直接向授权服务器请求令牌。POST /token HTTP/1.1Host: oauth.b.comContent-Type: application/x-www-form-urlencodedgrant_type=password&username=user@example.com&password=secret&client_id=CLIENT_ID&client_secret=CLIENT_SECRET <!-- 如果需要 -->上面 URL 中,
grant_type参数是授权方式,这里的password表示”密码式”,username和password是授权服务器的用户名和密码。 -
授权服务器:
授权服务器验证身份通过后,直接给出令牌。注意,这时不需要跳转,而是把令牌放在 JSON 数据里面,作为 HTTP 回应,A 网站因此拿到令牌。
适用场景(极其有限):
- 高度信任的内部应用(如公司自研 App);
- 遗留系统迁移过渡期;
- 无法使用浏览器重定向的设备(如 CLI 工具)。
6.4 客户端凭证模式(Client Credentials Grant)#
客户端模式指客户端以自己的名义,而不是以用户的名义,向 授权服务器 请求 Token,用于访问与用户无关的 API。
步骤详解:
-
客户端:向授权服务器进行身份认证,并要求一个访问令牌。
POST /tokenContent-Type: application/x-www-form-urlencodedgrant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET上面 URL 中,
grant_type参数等于client_credentials表示采用凭证式,client_id和client_secret用来让授权服务器确认 A 的身份。 -
授权服务器:验证通过以后,直接返回令牌。
典型场景:
- 后台服务间通信(如订单服务调用支付服务);
- 定时任务、脚本工具;
- 获取公共数据(如天气 API)。
6.5 总结#
- 授权码模式(+ PKCE)是现代应用的黄金标准,兼顾安全与兼容性。
- 避免使用隐式模式和密码模式,除非有非常特殊的理由。
- 实际开发中,应优先选择支持 OIDC 的身份提供商(如 Auth0、Keycloak、Google Identity 支付宝、QQ、微信、微博),以同时满足认证与授权需求。
上述主要比较浅显的讲解了 OAuth2.0 的基本逻辑,如若想详细深入的了解,可查看官方文档 OAuth 或 RFC 6749; 亦可查看 OAuth 2.0 概念及授权流程梳理 做对比
7 唯一登录(Single Active Session)#
假设现在产品经理提一个需求:我想要实现用户只能在一个设备上登录,禁止用户重复登录。
7.1 什么是唯一登录#
唯一登录是指系统强制同一账号在同一时间只能有一个活跃会话。当用户在新设备/浏览器登录时,旧会话立即失效。
唯一登录是业务层会话控制策略,与底层认证协议如OAuth 2.0无关。
✅ 典型需求:
- 银行类应用:防止账号被多人同时操作;
- 企业后台:确保操作可追溯到单一终端。
❌ 常见误解:
- “禁止多端登录” ≠ “禁止多浏览器标签页”——后者应通过前端心跳或共享 Storage 实现,而非服务端踢人。
7.2 唯一登录流程图#

Token 本身可以自带有效期(如 JWT 的
exp声明),但仅靠 Token 自身无法实现“主动失效”或“会话粒度控制”——这时就需要会话表(Session Table)来配合管理。
-
会话表结构:
CREATE TABLE user_sessions (id BIGINT PRIMARY KEY,user_id VARCHAR(64) NOT NULL,token VARCHAR(255) NOT NULL, -- JWT 或随机字符串device_info JSON, -- 可选:记录设备/IPactive BOOLEAN DEFAULT true,created_at TIMESTAMP,expires_at TIMESTAMP); -
登录时:
- 先将该用户所有
active = true的会话置为false; - 再插入新会话。
- 先将该用户所有
-
每次 API 请求:
- 验证 Token 是否存在且
active = true; - 若否,返回
401并提示“账号已在其他设备登录”。
- 验证 Token 是否存在且
注意事项:
- 不要仅靠删除旧 Token 实现:若客户端缓存了旧 Token,无法主动通知其失效;
- 配合前端监听:可在 WebSocket 或轮询中检测会话状态,主动跳转登录页;
- 考虑“多角色”场景:某些系统允许“Web 端 + App 端”同时在线,需按业务定义“唯一”粒度。
8 扫码登录#
8.1 什么是扫码登录#
扫码登录通常见于移动端 APP 中,很多 PC 端的网站都提供了扫码登录的功能,无需在网页上输入任何账号和密码,只需要让移动端 APP (如微信、淘宝、QQ等等) 中已登录用户主动扫描 二维码 ,再确认登录,以使 PC 端的同款应用得以快速登录的方式就是 扫码登录 。
8.2 什么是二维码#
二维码 又称二维条码,常见的二维码为 QR Code,QR 全称 Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型。
通过上面所述,我们不难发现,扫码登录需要三端 (PC端、手机端、服务端) 来进行配合才能达到登录成功的效果;
8.3 扫码登录的认证流程图#

8.4 扫码登录的步骤详解 (待扫码阶段、待确认阶段、已确认阶段)#
待扫码阶段:
-
PC端:
打开某个网站 (如taobao.com) 或者某个 APP (如微信) 的扫码登录入口;就会携带 PC 端的设备信息向服务端发送一个获取二维码的请求;
-
服务端:
服务器收到请求后,随机生成一个 UUID 作为二维码 ID,并将 UUID 与
PC 端的设备信息关联起来存储在 Redis 服务器中,然后返回给 PC 端;同时设置一个过期时间,在过期后,用户登录二维码需要进行刷新重新获取。 -
PC 端:
收到二维码 ID 之后,将二维码 ID 以
二维码的形式展示,等待移动端扫码。并且此时的 PC 端开始轮询查询二维码状态,直到登录成功。如果移动端未扫描,那么一段时间后二维码会自动失效。
已扫码待确认阶段:
-
手机端:
打开手机端对应已登录的 APP (微信或淘宝等),开始扫描识别 PC 端展示的二维码;
移动端扫描二维码后,会自动获取到二维码 ID,并将移动端登录的信息凭证(Token)和二维码 ID 作为参数发送给服务端,此时手机必须是已登录(使用扫描登录的前提是移动端的应用为已登录状态,这样才可以共享登录态)。
-
服务端:
收到手机端发来的请求后,会将
Token 与二维码 ID关联,为什么需要关联呢?因为,当我们在使用微信时,移动端退出时,PC 端也应该随之退出登录,这个关联就起到这个作用。然后会生成一个临时 Token,这个 Token 会返回给移动端,一次性 Token 用作确认时的凭证。
已确认阶段:
-
手机端:
收到确认信息后,点击确认按钮,移动端携带上一步中获取的
临时 Token发送给服务端校验; -
服务端:
服务端校验完成后,会更新二维码状态,并且给 PC 端生成一个
正式的 Token,后续 PC 端就是持有这个 Token 访问服务端。 -
PC端:
轮询到二维码状态为已登录状态,并且会获取到了生成的 Token,完成登录,后续访问都基于 Token 完成。
9 一键登录(适用于原生APP)#
9.1 账号密码登录#
大家都知道,最传统的登录方式就是使用账号加密码登录,简单粗暴,一般也不会出现什么问题;
缺点:
- 但这种方式要求用户要记住自己的账号和密码,也就是有一个记忆成本。用户为了降低记忆成本,很可能会在不同平台使用同一套账号密码。从安全角度考虑,一旦某个平台的账号密码泄露了,会连累到该用户使用的其他平台。
- 另外,由于账号和个人身份无关,意味着同一个用户可以注册多个不同的账号,也就是可能会有恶意注册的情况发生。
直到手机卡的强制实名制才得以解决!
9.2 手机号验证码登录#
随着无线互联的发展以及手机卡实名制的推广,手机号俨然已成为特别的身份证明,与账号密码相比,手机号可以更好地验证用户的身份,防止恶意注册。
但是手机号注册还是需要一系列繁琐的操作:输入手机号、等待短信验证码、输入验证码、点击登录。整个流程少说二十秒,而且如果收不到短信,也就登录补了,这类问题有可能导致潜在的用户流失。
从安全角度考虑,还存在验证码泄漏的风险。如果有人知道了你的手机号,并且窃取到了验证码,那他也能登录你的账号了。
所以就有了一键登录操作!
9.3 什么是一键登录#
我们想一下,为什么我们需要验证码?验证码的作用就是确定这个手机号是你的,那除了使用短信,是否还有别的方式对手机号进行认证?
于是,就有了咱们的主角一键登录。
短信验证码的作用就是证明当前操作页面的用户与输入手机号的用户为相同的人,那么实际上只要我们能够获取到当前手机使用的手机卡号,直接使用这个号码进行登录,不需要额外的操作,这就是一键登录。
一键登录能不能做,取决于运营商是否开放相关服务;随着运营商开放了相关的服务,我们现在已经能够接入运营商提供的 SDK 并付费使用相关的服务。
一键登录流程图:

一键登录步骤详解:
- SDK 初始化: 调用 SDK 方法,传入平台配置的 AppKey 和 AppSecret
- 唤起授权页: 调用 SDK 唤起授权接口,SDK 会先向运营商发起获取手机号掩码的请求,请求成功后跳到授权页。授权页会显示手机号掩码以及运营商协议给用户确认。
- 同意授权并登录: 用户同意相关协议,点击授权页面的登录按钮,SDK 会请求本次取号的 Token,请求成功后将 Token 返回给客户端
- 取号: 将获取到的 Token 发送到自己的服务器,由服务端携带 Token 调用运营商一键登录的接口,调用成功就返回手机号码。服务端用手机号进行登录或注册操作,返回操作结果给客户端,完成一键登录。
三大运营商开放平台:
-
由于国内三大运营商各自有独立的 SDK,所以会导致兼容方面的工作会特别繁琐。如果要采用一键登录的方案,不妨采用第三方提供了号码认证服务,下列几家供应商都拥有手机号码认证能力:
注意:
在认证过程中,需要用户打开蜂窝网络,如果手机设备没有插入 SIM 卡、或者关闭蜂窝网络的情况下,是无法完成认证的。所以就算接入一键登录,还是要兼容传统的登录方式,允许用户在失败的情况下,仍能正常完成登录流程。
总结#
| 类别 | 术语 | 说明 |
|---|---|---|
| 认证/授权协议(标准) | OAuth 2.0、OIDC、SAML、CAS | 定义客户端、用户、服务之间如何安全交换身份和权限信息 |
| 会话管理机制(实现) | Cookie、Session、Token、JWT | 用于在认证后维持用户状态的技术手段 |
| 身份架构模式(设计范式) | 联合身份(Federated Identity)、单点登录(SSO) | 高层业务目标或系统集成模式 |
HTTP 基本认证适用于内部网络,或者对安全要求不是很高的网络;Session-Cookie适用于一般中大型的网站(移动端 APP 除外);Token和JWT都适用于市面上大部分的企业型网站,JWT 效能会优于 Token;单点登录适用于子系统较多的大型企业网站;OAuth 2.0适用于需要快速注册用户型的网站;扫码登录适用于已完成部署了三端的企业;一键登录适用于原生 APP。