一、认证与授权基础
1.1 什么是认证?说白了就是「你是谁」
认证,英文叫 Authentication。我习惯把它理解为「验明正身」的过程。
你登录网站时输入用户名和密码,系统去数据库里比对一下——嗯,对得上,你就是你。这就是认证。
举个例子:你拿着身份证过高铁闸机,刷脸通过。机器确认「这张脸和身份证上是同一个人」——这就是认证。
在 Web 世界里,常见的认证方式有:
- 密码认证:最传统,用户名+密码
- 短信/邮箱验证码:一次性临时凭证
- 生物特征:指纹、人脸识别
- OAuth 第三方登录:用微信、GitHub 账号登录
我的经验: 千万别把密码明文存数据库。我曾经接手过一个项目,用户表里 password 字段存的居然是明文……吓得我连夜加了 bcrypt 哈希。你想想看,要是数据库被拖库,那画面太美不敢看。
1.2 什么是授权?说白了就是「你能干什么」
认证和授权,这两个词经常被搞混。我面试时也常问这个问题。
认证是确认身份,授权是确认权限。
举个生活中的例子:你走进公司大门,刷工牌——这是认证。但你能进财务室吗?能进机房吗?这取决于你的工牌权限——这就是授权。
在系统里,授权通常表现为:
- 普通用户只能看自己的订单
- 管理员可以删除任何人的订单
- 超级管理员可以修改系统配置
常见的授权模型有:
| 模型 | 说明 | 适用场景 |
|---|---|---|
| ACL(访问控制列表) | 每个资源绑定一个权限列表 | 小系统、文件系统 |
| RBAC(基于角色的访问控制) | 用户 -> 角色 -> 权限 | 企业系统、后台管理 |
| ABAC(基于属性的访问控制) | 根据用户、资源、环境等属性动态判断 | 大型复杂系统 |
注意: 认证通过不代表授权通过。我见过不少新手在写接口时,只检查了「是否登录」,却没检查「是否有权限」。结果普通用户能调管理员接口……这属于严重的安全漏洞。
1.3 Cookie 与 Session 机制详解
这是 Web 认证的「老前辈」了。虽然现在 Token 很流行,但 Cookie + Session 依然在很多传统项目里服役。
1.3.1 什么是 Cookie?
Cookie 是浏览器存储在本地的小型文本文件。每次请求时,浏览器会自动把 Cookie 带上。
特点:
- 大小限制:一般 4KB 左右
- 可设置过期时间
- 可以设置 HttpOnly(禁止 JS 读取)
- 可以设置 Secure(仅 HTTPS 传输)
1.3.2 什么是 Session?
Session 是服务器端存储的用户会话数据。服务器生成一个唯一的 Session ID,通过 Cookie 发给浏览器。
工作流程:
- 用户登录 -> 服务器创建 Session,生成 Session ID
- 服务器把 Session ID 写入 Cookie,返回给浏览器
- 浏览器后续请求自动带上这个 Cookie
- 服务器根据 Session ID 查找对应的 Session 数据
// Node.js 中使用 express-session 的示例
const session = require('express-session');
app.use(session({
secret: 'my-secret-key', // 用于签名 Session ID
resave: false, // 是否每次请求都重新保存
saveUninitialized: false, // 是否保存未初始化的 Session
cookie: {
httpOnly: true, // 禁止 JS 读取
secure: true, // 仅 HTTPS
maxAge: 24 * 60 * 60 * 1000 // 过期时间:1天
}
}));
// 登录成功后
app.post('/login', (req, res) => {
// 验证用户名密码...
req.session.userId = user.id;
req.session.role = user.role;
res.send('登录成功');
});
// 后续请求中
app.get('/profile', (req, res) => {
if (!req.session.userId) {
return res.status(401).send('请先登录');
}
// 正常处理...
});
核心要点: Session 数据存在服务器,浏览器只存一个 ID。所以 Session 方案天然是「有状态」的——服务器得记住每个用户的状态。
1.3.3 Cookie + Session 的优缺点
| 优点 | 缺点 |
|---|---|
| 实现简单,成熟稳定 | 服务器需要存储 Session,占用内存 |
| 可以主动销毁 Session(强制下线) | 分布式环境下需要共享 Session(Redis) |
| Cookie 可设置 HttpOnly,相对安全 | 存在 CSRF 攻击风险 |
| 浏览器自动管理,开发者省心 | 移动端 App 不天然支持 Cookie |
避坑指南: 我曾经在一个分布式项目里踩过 Session 共享的坑。A 服务器登录了,请求打到 B 服务器,Session 找不到……后来用 Redis 做集中存储才解决。如果你用 Node.js,推荐用 connect-redis 来存 Session。
1.4 Token 认证模式介绍
Token 认证,说白了就是「我给你一个令牌,你拿着它来证明身份」。
和 Session 不同,Token 是无状态的。服务器不存 Token,只负责验证 Token 的合法性。
1.4.1 Token 的工作流程
- 用户登录 -> 服务器验证身份 -> 生成 Token 返回
- 客户端保存 Token(localStorage / 内存 / Cookie)
- 每次请求时,在 HTTP Header 里带上 Token
- 服务器验证 Token 签名 -> 解析出用户信息
// Token 认证的典型流程(伪代码)
// 登录接口
app.post('/login', (req, res) => {
const { username, password } = req.body;
// 验证用户名密码...
const user = { id: 123, role: 'admin' };
// 生成 Token
const token = jwt.sign(
{ userId: user.id, role: user.role },
'secret-key',
{ expiresIn: '7d' }
);
res.json({ token });
});
// 受保护接口
app.get('/profile', (req, res) => {
// 从 Header 中获取 Token
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).send('Token 缺失');
}
try {
// 验证 Token
const decoded = jwt.verify(token, 'secret-key');
req.user = decoded;
// 正常处理...
} catch (err) {
return res.status(401).send('Token 无效或已过期');
}
});
1.4.2 Token 和 Session 的核心区别
| 对比项 | Session | Token |
|---|---|---|
| 状态 | 有状态(服务器存数据) | 无状态(服务器不存数据) |
| 存储位置 | 服务器内存 / Redis | 客户端(浏览器 / App) |
| 扩展性 | 需要共享 Session | 天然支持分布式 |
| 跨域 | 受限于 Cookie 同源策略 | 可以跨域使用 |
| 主动失效 | 可以(删除 Session) | 较麻烦(需要黑名单) |
| 移动端支持 | 不友好 | 天然支持 |
我的建议: 如果你做的是传统 Web 应用,用户量不大,用 Session 完全够用。如果你做的是前后端分离、移动端 App、或者微服务架构,Token 是更好的选择。我个人现在新项目基本都用 Token,省心。
1.4.3 Token 的常见类型
- JWT(JSON Web Token):最流行,自包含用户信息,可签名可加密
- Opaque Token:不透明 Token,就是一个随机字符串,需要查数据库才知道是谁
- Bearer Token:一种使用方式,放在 Authorization: Bearer <token> 中
注意: JWT 虽然方便,但有个坑——签发后无法主动失效。我曾经遇到一个场景:用户修改密码后,旧的 JWT 依然有效,直到过期。解决方案是维护一个 Token 黑名单,或者用短过期时间 + 刷新 Token 机制。这个我们后面章节会详细讲。
小结
这一章我们聊了:
- 认证是「你是谁」,授权是「你能干什么」
- Cookie + Session 是有状态的经典方案
- Token 是无状态的现代方案,适合分布式和移动端
下一章我们会深入 JWT 的细节——它的结构、签名算法、以及如何在实际项目中安全使用。嗯,到时候我会分享一些我踩过的坑,保证让你少走弯路。