0%

一、微前端要解决什么问题

微前端(Micro-Frontends)是一种将前端应用拆分为多个独立子应用的架构模式,每个子应用可以独立开发、独立测试、独立部署,最终由主应用聚合为一个整体。

阅读全文 »

HTTP(Hyper Text Transfer Protocol,超文本传输协议)是 Web 开发中最重要的协议之一。本文将从基础到进阶,全面讲解 HTTP 协议的核心知识点。

阅读全文 »

目录

  1. 开篇:一个故事看懂鉴权全貌
  2. 前端鉴权概览:四种方式的横向对比
  3. Cookie:浏览器的小纸条
  4. localStorage & sessionStorage:前端本地存储
  5. Cookie + Session 认证:经典的登录方案
  6. Token 认证:现代 API 的首选
  7. JWT:自带信息的令牌
  8. OAuth 2.0 / SSO:第三方登录与单点登录
  9. 面试高频问题汇总
  10. 总结回顾

开篇:一个故事看懂鉴权全貌

想象你去一家公司拜访朋友:

  1. 前台登记(认证):你出示身份证,前台核实你的身份,确认”你真的是你”
  2. 领取临时工牌(凭证):前台给你一张临时工牌,上面有你的照片和有效期
  3. 刷卡进出门(授权):不同门禁需要不同的权限——你能进大厅但不一定能进机房
  4. 集团分公司通用(SSO):如果你有集团总部的工牌,旗下子公司也认——不需要每家都登记一次

对应到计算机世界:

现实世界计算机世界
出示身份证输入用户名密码
前台核实Authentication(认证)
临时工牌Token / Session ID
不同门禁权限Authorization(授权)
集团工牌通用SSO(单点登录)
你把工牌借给朋友会话劫持 / Token 泄露

❗ 面试常问:认证(Authentication)和授权(Authorization)的区别?
认证是”你是谁”,授权是”你能做什么”。认证在前,授权在后。很多系统只需要认证(如公开 API 的限流),但授权一定依赖认证。


前端鉴权概览:四种方式的横向对比

HTTP 是无状态协议——每次请求都是独立的,服务器记不住”上次和你聊过”。要让前后端互信,必须主动维护一个状态。前端常见的鉴权方式有四种:

方式状态存放是否跨域CSRF 防护服务端开销典型场景
Cookie + Session服务端(Session 内存/Redis)❌ 需额外防护传统 MPA(多页应用)
Token(含 JWT)客户端(localStorage/内存)✅ 是✅ 天然防御低(无状态)SPA、移动端 API
OAuth 2.0第三方认证服务器✅ 是中等第三方登录(Google、微信)
SSO统一认证中心跨站点中等公司内部多系统打通

这四种方式并非互斥,实际项目中常常组合使用:

  • 内部系统用 SSO + Cookie Session
  • 对外 API 用 JWT
  • 第三方登录用 OAuth 2.0,内部仍用 Session 或 JWT 管理登录态

Cookie:浏览器的小纸条

Cookie 是服务端塞给浏览器的一小块数据,浏览器会把它存起来,下次请求同一域名时自动带上。

1
2
3
4
5
6
7
8
sequenceDiagram
participant B as 浏览器
participant S as 服务端
B->>S: 首次请求
S->>B: 响应 Set-Cookie: sessionId=abc123
Note over B: 浏览器保存 Cookie
B->>S: 后续请求自动带上 Cookie
S->>S: 解析 Cookie 识别身份
属性说明面试频率
Expires / Max-Age过期时间。不设置则为会话 Cookie(关闭浏览器即失效)⭐⭐
Domain指定哪些域名可以访问。如 .example.com 则子域名都可用⭐⭐
Path指定路径生效范围,默认 /
Secure仅在 HTTPS 下传输⭐⭐⭐
HttpOnlyJS 无法读取(document.cookie 拿不到),防 XSS⭐⭐⭐⭐
SameSite现代 CSRF 防护核心,见下方详解⭐⭐⭐⭐⭐

Cookie 由服务端通过 HTTP 响应头 Set-Cookie 设置:

1
Set-Cookie: sessionId=abc123; Expires=Wed, 21 Oct 2026 07:28:00 GMT; Path=/; Domain=.example.com; Secure; HttpOnly; SameSite=Lax

配置说明:

  • 服务端返回响应时,在 Set-Cookie 中指定属性,多个属性用 ; 分隔
  • 浏览器收到后,会按照这些属性规则存储和携带 Cookie
  • 前端无法主动设置 HttpOnly、Secure、SameSite 等安全属性——这些必须由服务端在响应头中指定。如果前端想在客户端创建 cookie,只能用 document.cookie,且无法添加 HttpOnly 等安全属性

JS 操作 Cookie

1
2
3
4
5
6
7
8
9
// 设置(只能设置 name=value、Domain、Path、Expires,无法设置 HttpOnly / Secure / SameSite)
document.cookie = 'token=abc123; Path=/; Domain=.example.com'

// 读取
console.log(document.cookie) // "token=abc123; otherKey=otherValue"
// ⚠️ 如果 cookie 设置了 HttpOnly,这里不会显示该 cookie

// 删除:把 Expires 设为过去的时间
document.cookie = 'token=; Expires=Thu, 01 Jan 1970 00:00:00 GMT'

✅ SameSite 属性(现代标准,必知)

SameSite 是 Chrome 51+ 引入的 Cookie 安全属性,用于限制第三方请求是否携带 Cookie,是目前最有效的 CSRF 防御手段之一。

行为使用场景
Strict完全禁止第三方请求携带,即使是点击链接跳转也不带银行转账等极高安全场景
Lax(默认)允许 GET 方式的顶级导航携带(点击链接、GET 表单),POST 等方式不携带大多数网站推荐
None不限制,但必须配合 Secure(仅 HTTPS)跨域请求需要携带 Cookie 时

CSRF 的攻击原理是:攻击者在自己的页面构造请求,让浏览器带着你的 Cookie 向目标网站发起请求(比如转账)。因为 Cookie 自动携带,服务端无法区分这个请求是用户主动发起的还是被诱骗发起的。

防御手段(从推荐到兜底):

  1. SameSite Cookie(首选):设置 SameSite=Lax。现代浏览器默认行为,阻止 POST 等危险请求跨站携带 Cookie。配置简单、覆盖面广,后端加一行响应头即可——这是当前最推荐的防御方案。

  2. 校验 Referer / Origin 头:服务端检查请求来源是否为合法域名。缺点:某些场景(如 HTTPS→HTTP 跳转)浏览器可能不发送 Referer 头。

  3. CSRF Token:服务端在页面中嵌入一个随机 token,提交请求时携带。攻击者无法获取这个 token,因此请求被拒绝。缺点:SPA 需要额外的后端配合。

  4. 关键操作加验证码:转账、改密等操作强制用户输入验证码,从根本上阻止 CSRF。

localStorage 如何避免 XSS 风险?

XSS(跨站脚本攻击)是指攻击者在页面中注入恶意脚本,从而读取页面中的一切数据,包括 localStorage。

防御手段:

  1. 永远不要在 localStorage 中存敏感信息(如身份证号、银行卡号)。如果必须存 token,要接受 XSS 会导致 token 泄露的风险。

  2. 严格防范 XSS(这是根本):

    • 对用户输入进行转义和过滤(避免 <script> 标签被直接渲染)
    • 使用 Content-Security-Policy(CSP)头限制脚本来源
    • React / Vue 等现代框架默认对插值做转义,但 v-html / dangerouslySetInnerHTML 仍存在风险
  3. 使用 HttpOnly Cookie 替代 localStorage 存 token(但如果你的 SPA 需要跨域 API,这条路走不通)

  4. 最佳实践:Access Token 放内存,Refresh Token 放 HttpOnly Cookie——XSS 能读到内存中的 Access Token,但有效期短(15 分钟);拿不到 Refresh Token 就无法续期,损失可控。

  • 大小限制:单个域名下约 4KB
  • 数量限制:每个域名约 20~50 个(因浏览器而异)
  • 同源限制:不可跨域,子域名通过 Domain 可共享
  • 自动携带:同域名的每个请求都会带上,影响性能(尤其静态资源多时)
  • 不安全:明文传输(除非 Secure),可被 XSS 读取(除非 HttpOnly)

现代浏览器默认开启 Cookie,禁用的情况极少。如果真禁用了,使用 URL 参数传递 Session ID 是最常见的替代方案。但要注意:URL 传递 Session ID 容易被截取或泄露(通过 Referer 头),所以主流做法是要求浏览器开启 Cookie,否则提示用户。

✅ 过时内容清理:Flash 沙箱、IE6 时代的 Cookie 问题已全部删除。


localStorage & sessionStorage:前端本地存储

⚠️ 重要区分Session(服务端会话)sessionStorage(浏览器存储) 除了名字相似,没有任何关系!Session 存在服务器内存/Redis 中,sessionStorage 存在浏览器磁盘中。Session 的过期由服务端控制,sessionStorage 关闭即清理。

三兄弟对比表(面试必考)

特性CookielocalStoragesessionStorage
容量~4KB~5MB+~5MB+
生命周期可设置,默认关闭浏览器即失效永久保存(除非手动清除)当前标签页会话,关闭即清除
HTTP 携带✅ 每次请求自动带上❌ 不参与通信❌ 不参与通信
跨域❌ 绑定域名❌ 同源策略❌ 同源策略
可访问性同域名所有标签页同源所有标签页仅当前标签页
XSS 风险HttpOnly 可防御⚠️ JS 直接读取,风险高⚠️ JS 直接读取,风险高
API 易用性需要自行封装原生接口友好原生接口友好

Storage 的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 存
localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIs...');
sessionStorage.setItem('tempData', JSON.stringify({ id: 1 }));

// 取
const token = localStorage.getItem('token');
const temp = JSON.parse(sessionStorage.getItem('tempData'));

// 删
localStorage.removeItem('token');
sessionStorage.clear(); // 清空全部

// storage 事件:不同标签页间通信
window.addEventListener('storage', (e) => {
console.log('新的 key:', e.key);
console.log('旧值:', e.oldValue, '新值:', e.newValue);
});

❗ 面试题:localStorage 如何跨标签页通信?
通过 storage 事件。当同一源下的某个页面修改 localStorage 时,其他页面会触发该事件。

Token 存哪最安全?(面试高频)

存储位置XSS 防护CSRF 防护跨域场景
localStorage❌ 直接暴露SPA 常用,需做好 XSS 防护
sessionStorage❌ 直接暴露标签页隔离需求
Cookie(HttpOnly + SameSite)✅(SameSite=Lax)最安全但限制跨域
内存变量刷新即丢失

最佳实践:将 Access Token 放在内存中,Refresh Token 放在 HttpOnly Cookie 中。这样即使 XSS 也只能拿到短期有效的 Access Token,拿不到可刷新的 Refresh Token。


⚠️ 名称澄清:此处的 Session(会话) 是服务端概念,和浏览器的 sessionStorage 没有任何关系。Session 存在服务器内存/Redis 中,由服务端管理其生命周期。

工作原理

1
2
3
4
5
6
7
8
9
10
11
sequenceDiagram
participant B as 浏览器
participant S as 服务器
B->>S: 1. POST /login (username + password)
S->>S: 2. 验证账号密码
S->>S: 3. 创建 Session (内存/Redis)
S->>B: 4. Set-Cookie: "JSESSIONID=xxx; HttpOnly;Secure;SameSite=Lax"
Note over B: 5. 浏览器保存 Cookie
B->>S: 6. 后续请求自动携带 Cookie
S->>S: 7. 根据 SessionID 查找 Session
S->>B: 8. 返回业务数据

关键概念

  • Session:服务端内存中的一块空间,存储用户信息(ConcurrentHashMap / Redis)
  • Session ID:唯一标识,通过 Cookie 传给客户端
  • Cookie:浏览器端的存储载体,自动携带 Session ID

Session 的生命周期

Session 存在于服务端,它的有效期由服务端配置决定,与”浏览器是否关闭”没有直接关系:

  • 超时过期:最常见的策略。用户一段时间无操作(如 Tomcat 默认 30 分钟),服务端自动销毁 Session。Node.js (express-session) 默认也是 10 分钟无操作即过期。
  • 主动销毁:用户点击”退出登录”,服务端删除 Session。
  • Cookie 中的 Session ID 是会话 Cookie(默认),浏览器关闭后丢失。但**丢失的是”钥匙”而不是”锁”**——Session 数据在服务端还会保留一段时间,直到 GC(垃圾回收)清理。

举例:你用手机浏览器登录了知乎,Session 数据存在知乎服务器。你关掉浏览器,知乎服务端的 Session 并不会立即消失——它还会保留十几分钟到半小时,等超时后再被清理。这就是为什么”关浏览器”不等于”退出登录”。

Session 的分布式问题(面试高频)

当服务扩展到多台服务器时,Session 存在不同机器上怎么办?

方案说明优缺点
粘性会话(Sticky Session)负载均衡将同一用户始终转发到同一台机器简单但有单点风险,某台挂掉则 Session 丢失
Session 复制各服务器间同步 Session消耗网络带宽,节点多时性能差
集中式 Session(推荐)所有 Session 存入 Redis✅ 主流方案,弹性伸缩,节点故障不影响
无状态 JWT不再需要服务端存 Session彻底解决分布式问题,但引入 JWT 的其他问题

❗ 面试题:多服务器部署时 Session 怎么处理?
标准答案是用 Redis 集中存储 Session,所有服务节点从同一个 Redis 读取 Session 数据。

  • 安全性:Session 服务端存储 > Cookie 客户端存储

  • 存储类型:Session 支持任意对象 > Cookie 仅支持字符串

  • 存储大小:Session 远大于 Cookie 的 4KB

  • 生命周期

    • Session:存在服务端,生命周期由服务端控制。最常见的超时策略是”一段时间无操作则过期”(如 30 分钟),而非”浏览器关闭”。虽然 Cookie 中的 Session ID 默认是会话 Cookie(关浏览器就没了),但Session 本身的有效期和服务端配置相关(比如 Tomcat 的 session-timeout、PHP 的 session.gc_maxlifetime)。浏览器关闭只丢失了 Session ID 这个”钥匙”,Session 数据本身还会在服务器保留一段时间直到 GC 回收。
    • Cookie:由客户端控制过期,可设置为长期有效(对应用户的”记住我”功能)

Token 认证:现代 API 的首选

Token 解决了什么问题?

  1. CSRF 防护:Token 由前端手动携带(放在 Authorization Header),浏览器不会自动带,从根本上避免 CSRF
  2. 跨域友好:Token 不存在同源限制,适合 SPA 和移动端
  3. 服务器无状态:Token 自身包含用户信息,服务器不需要存储会话
  4. 适合分布式:无状态意味着任何服务器节点都能验证 Token

Access Token + Refresh Token 机制

这是现代 Token 认证的标准模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sequenceDiagram
participant C as 客户端
participant I as IdP(认证服务)
participant S as 业务服务

Note over C,I: 1. 登录阶段
C->>I: POST /login (username + password)
I->>C: { access_token, refresh_token }

Note over C,S: 2. 正常请求
C->>S: GET /api/data (Authorization: Bearer access_token)
S->>C: 200 OK + data

Note over C,S: 3. Access Token 过期
C->>S: GET /api/data (Authorization: Bearer access_token)
S->>C: 401 Unauthorized

Note over C,I: 4. 刷新 Token
C->>I: POST /refresh (refresh_token)
I->>C: { access_token: new, refresh_token: new }
C->>S: GET /api/data (Authorization: Bearer new_token)
S->>C: 200 OK + data

为什么需要两个 Token? 职责分离:

  • Access Token:短期有效(15 分钟~2 小时),用于请求资源,丢失了影响有限
  • Refresh Token:长期有效(天级别),仅用于获取新的 Access Token,通常存在服务端以增加安全性

面试重点:无痛刷新 Token(前端实战)

当用户同时发起多个请求时,如果 Access Token 恰好过期,不能每个请求都去刷新 Token。标准做法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import axios from 'axios'

let isRefreshing = false // 是否正在刷新 Token
let pendingRequests = [] // 等待重试的请求队列

const request = axios.create({
baseURL: '/api',
timeout: 30000,
})

// 请求拦截器:自动带 Token
request.interceptors.request.use((config) => {
const token = localStorage.getItem('access_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})

// 响应拦截器:Token 过期自动刷新
request.interceptors.response.use(
(response) => response.data,
(error) => {
if (!error.response) return Promise.reject(error)

const { config, response: { status, data } } = error

// Access Token 过期(根据后端约定判断)
if (status === 401 && data?.code === 'TOKEN_EXPIRED') {
if (!isRefreshing) {
isRefreshing = true
return refreshToken()
.then(({ access_token }) => {
// 更新本地 Token
localStorage.setItem('access_token', access_token)
// 重试所有等待的请求
pendingRequests.forEach(cb => cb(access_token))
pendingRequests = []
// 重试当前请求
config.headers.Authorization = `Bearer ${access_token}`
return request(config)
})
.catch(() => {
// Refresh Token 也失效了,跳登录页
redirectToLogin()
})
.finally(() => {
isRefreshing = false
})
} else {
// 正在刷新,把当前请求加入队列等待
return new Promise((resolve) => {
pendingRequests.push((newToken) => {
config.headers.Authorization = `Bearer ${newToken}`
resolve(request(config))
})
})
}
}
return Promise.reject(error)
}
)

function refreshToken() {
const refreshToken = localStorage.getItem('refresh_token')
return axios.post('/auth/refresh', { refresh_token: refreshToken })
.then(res => res.data.data)
}

通俗理解:这相当于”排队机制”——第一个请求发现过期了,就先去”取新令牌”;其他请求在窗口等着,等第一个拿回新令牌后,大家继续办业务。

Token 的存储位置取舍(面试高频)

方案安全风险实际应用
localStorageXSS 直接泄露最常见但也最危险
Cookie(HttpOnly + SameSite)XSS 安全 ✅,但跨域麻烦同域下的 SPA
内存(变量)+ Refresh Cookie✅ 刷新丢失 Access Token,但 Refresh Token 在 Cookie 中安全最佳实践

JWT:自带信息的令牌

JWT(JSON Web Token,读作 “jot”)是一种规范化的 Token 格式,定义在 RFC 7519。相当于”会自己说话的工牌”——不用去后台查数据库,看工牌本身就知道你是谁。

JWT 的结构

JWT 由三个用 . 分隔的 Base64URL 字符串组成:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

三部分分别是:

1. Header(头部)

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}
  • alg:签名算法(HS256、RS256 等)
  • typ:类型,固定为 JWT

2. Payload(载荷)

1
2
3
4
5
6
7
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622,
"role": "admin"
}
  • 标准字段:iss(签发人)、sub(主题)、aud(受众)、exp(过期时间)、iat(签发时间)等
  • 自定义字段:可按需添加业务信息
  • ⚠️ Payload 是明文 Base64 编码,不是加密!不要存敏感信息

3. Signature(签名)

1
2
3
4
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)

签名的作用:防止数据篡改。服务端收到 JWT 后,用同样的 secret 对 header+payload 重新签名,对比是否一致。

通俗理解:JWT 像一张”防伪标签”——你可以看到上面的字(payload),但你想改上面的字,签名就对不上了,会被发现。

JWT 工作流程

1
2
3
4
5
6
7
8
9
10
11
12
sequenceDiagram
participant C as 客户端
participant A as 认证服务
participant B as 业务服务

C->>A: 用户名密码登录
A->>A: 验证身份,生成 JWT(签名)
A->>C: 返回 JWT
Note over C: 保存 JWT(localStorage / Cookie)
C->>B: 请求 API(Authorization: Bearer JWT)
B->>B: 验证签名 → 从 Payload 读用户信息
B->>C: 返回数据

HS256 vs RS256 签名算法选择

算法类型密钥场景
HS256对称加密同一密钥签名和验签单体应用、认证和业务服务在一起
RS256非对称加密私钥签名,公钥验签微服务(认证服务独立)、第三方信任

JWT 面试高频问题

1. JWT 如何注销?

JWT 无状态=无法从服务端主动使其失效。解决方案:

方案说明优缺点
客户端删除前端删除本地 JWT简单,但 JWT 在有效期内仍然可用(游离态)
黑名单维护一个”提前注销”的 JWT 列表✅ 实用,黑名单通常很小(只维护未过期但需要注销的)
短有效期设置短的过期时间(如 15 分钟)配合 Refresh Token,即使无法注销,影响窗口也小
修改用户密钥用户注销时修改其在服务端的 secret使之前签发的所有 JWT 失效,但会使所有”已登录”用户重新登录

最佳实践:短有效期 + 黑名单(只维护未过期的已注销 Token)+ Refresh 联动。

2. JWT 被盗怎么办?

防御措施说明
HTTPS传输层加密,防止中间人窃取
短有效期即使被盗,窗口期很短
客户端指纹生成 JWT 时绑定客户端特征(如 User-Agent、IP),被盗后无法在其他环境使用
存储在 Cookie 中并设置 HttpOnly防止 XSS 窃取
Refresh Token 轮换(Rotation)每次使用 Refresh Token 都发放一个新的,旧的立即失效

3. JWT 的安全攻击(现代必知)

攻击类型原理防御
“none” 算法攻击攻击者将 alg 改成 none,服务端若未禁用 none 算法则会跳过签名验证服务端必须禁用 none 算法
算法混淆攻击服务端期望 RS256(非对称),但攻击者用 HS256(对称),用已知的公钥作为 secret 签名固定算法类型,或始终使用非对称加密
密钥暴力破解使用弱密钥的 HS256 可能被暴力破解使用强密钥,优先 RS256

4. JWT 适合所有场景吗?

不适合的场合

  • 需要服务端主动控制的场景(如强制下线、踢人)
  • payload 过大会影响请求效率
  • 敏感信息不能放 payload

适合的场合

  • ✅ 分布式微服务(无状态,任意节点可验证)
  • ✅ 移动端(不支持 Cookie 的场景)
  • ✅ 第三方 API(携带用户身份)
  • ✅ 单点登录
对比维度Session CookieJWT
状态存放服务端客户端
伸缩性需统一 Session 存储(Redis)天然支持,任意节点可验签
注销能力✅ 服务端可直接删除 Session❌ 需黑名单或短有效期
CSRF❌ 需额外防护✅ 天然防御
跨域❌ 受 Cookie 同源限制✅ 不受限
实时性✅ 可随时修改或撤销❌ 修改权限后需等待 Token 过期
额外查询每次需要查 Session❌ 不需要,但载荷过大影响传输效率

OAuth 2.0 / SSO:第三方登录与单点登录

SSO(单点登录)的核心思想

一次登录,全网通行。你在公司内部系统中只需登录一次,就可以访问 HR 系统、代码仓库、日志平台等所有内部系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sequenceDiagram
participant U as 用户
participant A as 系统A
participant B as 系统B
participant SSO as SSO 认证中心

U->>A: 访问系统A(未登录)
A->>SSO: 重定向到 SSO 中心
U->>SSO: 输入账号密码登录
SSO->>SSO: 创建全局会话(Cookie)
SSO->>A: 返回 token,A 创建本地会话
A->>U: 系统A首页

U->>B: 访问系统B(未登录)
B->>SSO: 重定向到 SSO 中心
SSO->>SSO: 检测到已登录(全局 Cookie)
SSO->>B: 直接返回 token(无需再输密码)
B->>U: 系统B首页

OAuth 2.0(面试重点)

OAuth 2.0 的本意是授权(Authorization)——允许第三方应用在用户授权下访问用户在其他平台的数据。最常见的应用场景是”使用微信/Google 账号登录”。

四种角色

角色现实类比例子
Resource Owner(资源所有者)你本人用户
Client(客户端)想借你书的同学第三方应用(”来记账” App)
Authorization Server(授权服务器)图书馆管理员微信/Google 认证服务
Resource Server(资源服务器)图书馆书库API 服务器(存储用户数据)

四种授权模式

模式安全等级适用场景
授权码模式(Authorization Code)⭐⭐⭐⭐⭐最常用,有后端的 Web 应用
隐式模式(Implicit)— 已弃用⭐⭐纯前端 SPA(不再推荐,用 PKCE 替代)
密码模式(Password)⭐⭐高度信任的第一方应用(如自家 App)
客户端凭证模式(Client Credentials)⭐⭐⭐⭐服务间调用(无用户参与)

授权码流程详解(标准版)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sequenceDiagram
participant U as 用户(浏览器)
participant A as 第三方应用
participant G as Google 认证服务器

U->>A: 点击"使用 Google 登录"
A->>U: 重定向到 Google 授权页
U->>G: 询问"是否授权给该应用?"
U->>G: 点击"同意"
G->>U: 重定向回第三方应用(附带授权码 code)
U->>A: 将 code 交给前端 → 后端
A->>G: 后端用 code + client_secret 换取 access_token
G->>A: 返回 access_token
A->>G: 用 access_token 请求用户信息
G->>A: 返回用户信息(姓名、邮箱等)
A->>U: 登录成功

为什么需要授权码这个中间步骤?
因为 Access Token 不能暴露在浏览器——授权码是一次性的,由后端服务器凭 client_secret 去换取 Token。这样即使恶意脚本截取了授权码,没有 client_secret 也换不到 Token。

PKCE(Proof Key for Code Exchange)— 移动端必用

纯前端应用(SPA、移动 App)无法安全保存 client_secret,因此有了 PKCE 扩展:

1
2
3
4
5
6
7
8
9
10
11
sequenceDiagram
participant A as 移动 App
participant G as 认证服务器

A->>A: 生成随机 code_verifier(长字符串)
A->>A: code_challenge = SHA256(code_verifier)
A->>G: 请求授权(附带 code_challenge)
G->>A: 返回授权码 code
A->>G: 用 code + code_verifier 换 Token
G->>G: 验证 SHA256(code_verifier) == code_challenge
G->>A: 返回 access_token

OAuth 2.0 vs OpenID Connect (OIDC)

对比维度OAuth 2.0OpenID Connect (OIDC)
目的授权(允许访问资源)认证(确认身份)+ 授权
产出Access TokenAccess Token + ID Token(JWT 格式)
用户身份不标准化,需要额外 API 获取标准化的 JWT 包含用户信息
协议层级基础协议OAuth 2.0 之上的认证层

通俗理解:OAuth 2.0 是”我给你一把钥匙,你可以进我的储物间”;OIDC 是”我不仅给你钥匙,还给你一张身份证,证明你确实是你”。

OAuth 2.0 vs OpenID

  • OpenID 1.0/2.0:只做认证,不授权。允许用同一身份登录不同网站,但无权访问你的数据
  • OAuth 2.0:做授权。允许第三方访问你的数据(如读取你的 Google 日历)
  • OIDC:在 OAuth 2.0 之上加了一层认证,是目前的主流方案

你可以理解为:OAuth 解决”你能做什么”(授权),OpenID 解决”你是谁”(认证)。


面试高频问题汇总

按面试出现频率排列,方便快速复习:

⭐⭐⭐⭐⭐(必考)

  1. Cookie、localStorage、sessionStorage 的区别?

    • 容量:4KB vs 5MB vs 5MB
    • 生命周期:可设置 vs 永久 vs 关闭即清理
    • HTTP 携带:自动带 vs 不参与通信 vs 不参与通信
    • 安全性:HttpOnly 可防 XSS vs JS 直接读取风险高
  2. Session 和 JWT 的区别?各有什么优缺点?

    • Session:有状态,服务端存 → 易注销,需统一存储(Redis),CSRF 需防护
    • JWT:无状态,客户端存 → 难注销,天然防 CSRF,跨域友好,但无法主动失效
    • 见 JWT vs Session Cookie 对比表
  3. JWT 由哪几部分组成?如何保证不被篡改?

    • Header + Payload + Signature
    • 服务端用 secret 重新对 header+payload 签名,对比是否一致

⭐⭐⭐⭐(高频)

  1. Token 过期如何处理?Refresh Token 的作用?

    • 401 → 用 Refresh Token 换新 Access Token → 重试原请求
    • Refresh Token 长期有效,负责身份认证;Access Token 短期有效,负责资源访问
    • 职责分离:Access 丢失影响小,Refresh 通常存在服务端
  2. 如何防止 Token 被盗?

    • HTTPS 传输、短有效期、客户端指纹绑定、HttpOnly Cookie 存储、Refresh Token 轮换
  3. SSO 的实现原理?

    • 一个统一的认证中心,各子系统不再各自认证
    • 同域 SSO:共享 Cookie;跨域 SSO:CAS(Central Authentication Service)/ OAuth
    • 关键点:全局会话 vs 局部会话

⭐⭐⭐(中等频率)

  1. OAuth 2.0 授权码流程是怎样的?为什么需要授权码?

    • 用户同意 → 返回 code → 后端用 code+secret 换 Token
    • 授权码一次性的,防止 Token 暴露在浏览器端
  2. SameSite Cookie 解决了什么问题?

    • 解决 CSRF 攻击。Lax 允许 GET 导航携带,Strict 完全禁止,None 不限(需 HTTPS)
  3. 前端如何安全地存储 Token?

    • Access Token 放内存变量,Refresh Token 放 HttpOnly Cookie
    • 纯 localStorage 方案必须做好 XSS 防御

⭐⭐(了解即可)

  1. OIDC 和 OAuth 2.0 的关系?

    • OIDC = OAuth 2.0 + 身份认证层(ID Token),解决 OAuth 不标准化用户身份的问题
  2. 什么是 PKCE?为什么移动端需要它?

    • 纯前端应用无法安全保存 client_secret,用 code_verifier 证明请求合法性
  3. JWT 中的 “none” 算法攻击?

    • 攻击者将 alg 改为 none 绕过签名验证,服务端必须禁用 none

总结回顾

用一句话串联全文:

Cookie 是浏览器”帮你带”纸条,所以有 CSRF 风险;localStorage 是你”自己放”纸条,所以有 XSS 风险;Session 是服务端记小本本,有状态但好注销;JWT 是把小本本的内容印在工牌上,无状态但难回收;它们组合起来加上OAuth 2.0 和 SSO,就构成了前端鉴权的完整拼图。

核心原则

  1. HTTPS 是基础 —— 没有 HTTPS,所有鉴权方案都白搭
  2. 没有绝对安全,只有合适的权衡 —— JWT 方便但有注销问题,Session 安全但扩展性差
  3. 纵深防御 —— 不要依赖单一防御手段(仅用 SameSite 不够,XSS 防护也要做好)
  4. 最小权限 —— 只给必要的 Token 权限,有效期尽量短

最后更新:2026 年 6 月 · 前端鉴权知识点终稿

垃圾回收机制是由引擎来负责的。JS 引擎有很多种(各个浏览器都不同),其垃圾回收机制在一些细节及优化上也略有不同。本文以一些通用的回收算法作为切入,再由市场占有率大的 V8 引擎发展至今对该机制的优化为例。JavaScript 是门魅力无限的语言,关于它的 GC(垃圾回收)方面,你了解多少呢?想来大部分人是因为面试才去看一些面试题从而了解的垃圾回收,那在正式开始之前,给大家列几个小问题,大家可以先想一下答案,带着问题及答案再去看文章。

  • 什么是垃圾回收机制?
  • 垃圾是怎样产生的?
  • 为什么要进行垃圾回收?
  • 垃圾回收是怎样进行的?
  • V8 引擎对垃圾回收进行了哪些优化?
    阅读全文 »

只是用户看到仅仅只是浏览器本身,却很少能看到浏览器最核心的部分—浏览器内核。从第一款 libwww(Library WorldWideWeb)浏览器发展至今已经经历了无数竞争与淘汰了。现在国内常见的浏览器有:IE、Firefox、QQ 浏览器、Safari、Opera、Google Chrome、百度浏览器、搜狗浏览器、猎豹浏览器、360 浏览器、UC 浏览器、遨游浏览器、世界之窗浏览器等。但目前最为主流浏览器有五大款,分别是 IE、Firefox、Google Chrome、Safari、Opera。

阅读全文 »


title: 前端安全
categories:

  • 前端安全

    tags:

  • 前端安全

  • 浏览器

  • 算法

  • XSS

  • CSRF

  • 网络安全


本文全面介绍了前端安全的各个方面,包括加密算法、常见攻击方式及其防御措施,以及现代前端安全最佳实践。

阅读全文 »

CDN(Content Delivery Network,内容分发网络)是现代互联网基础设施的重要组成部分。本文将从概念到实践,全面讲解 CDN 的工作原理、关键技术及应用场景。

阅读全文 »

AJAX 技术使开发者能够专注于互联网中数据的传输,而不再拘泥于数据传输的载体。通过它,我们获取数据的方式变得更加灵活,可控和优雅。但 AJAX 技术并不是一把万能钥匙,互联网中的数据隐私和数据安全(例如银行账号和密码)非常重要,为了保护用户数据的隐私与安全,浏览器使用同源策略限制了 AJAX 技术获取数据的范围和能力。但有时我们不得不想办法绕过同源策略,实现跨域请求资源。因此跨域技术一直成为开发者们经久不衰的讨论话题。

阅读全文 »

DNS(Domain Name System,域名系统)是互联网最重要的基础设施之一,它负责将人类可读的域名转换为机器可识别的 IP 地址。本文将从基础到进阶,全面讲解 DNS 的工作原理、记录类型及安全机制。

阅读全文 »