# Cookie、Session、Token、JWT
30 秒速记
- 四个东西不在一个层级:
Cookie是浏览器存储和自动携带机制,Session是服务端存状态的方案,Token是凭证,JWT是Token的一种格式 - 经典组合:
Session数据在服务端,session id装在Cookie里;JWT也可以装在Cookie里 Cookie三个安全属性:HttpOnly防脚本读、Secure只走HTTPS、SameSite挡跨站请求带CookieJWT校验别只验签名:限定算法,检查exp、iss、audRefresh Token比Access Token值钱,要轮换、能撤销
这四个概念最容易被并列着比,其实它们不在一个层级:Cookie 是载体,Session 是存储方案,Token 是凭证,JWT 是凭证的一种格式。 打个比方,Cookie 是钱包,session id 和 JWT 都是可以放进钱包的卡:session id 像储物柜号码牌,东西存在服务端;JWT 像盖了章的身份证明,信息就写在卡上。所以「用 Session 还是 JWT」是在比存储方案,「放 Cookie 还是放请求头」是在比怎么传,这两件事要分开说。
理解「Cookie、Session、Token、JWT」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。
工程中还要验证退出码、资源清理和异常输入。属于 security 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。
把四个概念放在一次请求里看,层级就很清楚:
# 方案 A:Session + Cookie
Set-Cookie: sid=s%3Aabc123.sig; HttpOnly; Secure; SameSite=Lax ← 登录响应
Cookie: sid=s%3Aabc123.sig ← 之后每次请求,浏览器自动带
# 服务端拿 abc123 去 Redis 查 { uid: 42, role: 'admin' }
# 方案 B:JWT + Authorization 头
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjQyfQ.sig ← 前端代码手动带
# 服务端不查库,验签名后直接得到 { uid: 42 }
# 方案 C:JWT + Cookie
Cookie: access_token=eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjQyfQ.sig
选型可以拆成两个独立问题。第一个是状态存哪:存服务端(Session)好撤销、好控制,但多实例要共享存储;存客户端(JWT)好扩容,但难撤销。第二个是怎么传:Cookie 浏览器自动带,配上 HttpOnly 能防脚本偷,但要处理 CSRF;请求头手动带,不怕 CSRF,但 Token 要放在脚本能读到的地方,怕 XSS。
校验 JWT 时只验签名是不够的。要固定允许的算法(比如只认 HS256 或 RS256),防止有人把 alg 改成 none 或者用公钥冒充 HMAC 密钥;还要检查 exp 没过期、iss 是自己的签发方、aud 是给当前服务的。Refresh Token 有效期长,泄露损失更大,每次刷新都换一张新的,旧的作废,发现旧的被再次使用就说明泄露了,把这个用户的整条令牌链都撤销。
面试官追问
追问 1页面刷新后还是登录状态,能说明用的是 JWT 吗?
不能,Session 加 Cookie、JWT 放 localStorage 都能做到。打开 Network 看请求带的是 Cookie: connect.sid=... 还是 Authorization: Bearer eyJ...,以 eyJ 开头基本就是 JWT。
追问 2JWT 放进 Cookie 里,那它算 Cookie 认证还是 Token 认证?
这么问本身就混了层级。凭证是 JWT,传输载体是 Cookie,服务端不查会话存储,只验签名。安全上要同时考虑 Cookie 的 CSRF 问题。
追问 3设了 HttpOnly 是不是就不怕 XSS 了?
不是。HttpOnly 只让脚本读不到 Cookie,可攻击脚本可以直接在你页面里发请求,浏览器照样自动带上 Cookie。它能防偷走凭证,防不了借用凭证,根本还是要堵住 XSS。
追问 4接口要同时给浏览器和后台脚本调,用哪种方案?
脚本没有浏览器的 Cookie 机制,用 Authorization 头带 Token 更通用。浏览器端可以继续用 Cookie,服务端中间件两种来源都认,统一解析成 req.user。
# 什么是认证(Authentication)
30 秒速记
- 认证(
Authentication)回答「你是谁」:证明你就是这个账号的主人 - 类比:指纹打卡,指纹和录入的对得上才算你本人
- 互联网常见方式:账号密码、短信验证码、邮箱登录链接、扫码、第三方登录
- 验证码和邮箱链接的逻辑是:能收到 = 你控制这个手机号或邮箱 = 账号主人
- 别和授权混:授权是「你能做什么」,先认证再授权,失败分别是
401和403
认证就是确认「你是你」,系统要先弄清楚请求是谁发的。 最好懂的例子是指纹打卡,按上去和系统里录入的一致,就认定是你本人。网站上换成账号密码、短信验证码、邮箱登录链接这些方式,后两种的逻辑其实是「谁能收到验证码,谁就是这个手机号的主人」。面试里常和授权一起问,记住一句:认证管你是谁,授权管你能干什么,接口层面认证失败回 401,认证过了但没权限回 403。
- 通俗地讲就是验证当前用户的身份,证明“你是你自己”(比如:你每天上下班打卡,都需要通过指纹打卡,当你的指纹和系统里录入的指纹相匹配时,就打卡成功)
- 互联网中的认证:
- 用户名密码登录
- 邮箱发送登录链接
- 手机号接收验证码
- 只要你能收到邮箱/验证码,就默认你是账号的主人
面试官追问
追问 1用户给了相册权限,能不能当他已经登录?
不能,那是授权,而且是授权给 App 访问手机能力,和账号身份没关系。要看个人资料,还是得先登录认证。
追问 2短信验证码登录,服务端至少要做哪些校验?
验证码要和手机号绑定、设有效期(比如 5 分钟)、用一次就作废,再限制错误次数和发送频率,不然 4 位、6 位验证码都能被暴力试出来。
追问 3401 和 403 怎么区分?
401 是「我不知道你是谁」,没登录或凭证无效,前端该引导登录;403 是「知道你是谁,但你没这个权限」,重新登录也没用。
追问 4同时支持密码、短信、邮箱三种登录,后端怎么设计比较干净?
三种入口各自完成身份校验,校验通过后走同一个签发凭证的函数,后续接口完全不关心用户是怎么登录的。
# 什么是授权(Authorization)
30 秒速记
- 授权(
Authorization)回答「你能做什么」,发生在认证之后 - 两种场景别混:用户授权第三方应用访问自己的资源(
OAuth、小程序获取头像),以及系统内部的权限控制(角色、菜单、按钮) - 授权要按资源分开给,给了头像不等于给了位置
- 实现上可以用
Session、Token、OAuth,它们是载体,权限模型(RBAC之类)才是核心 - 前端隐藏按钮只是体验,真正的权限判断必须在服务端
授权管的是「你能做什么」,认证确认身份之后才轮到它。 日常说授权其实有两种:一种是用户把自己的资源开放给第三方,比如小程序弹窗问能不能获取头像昵称,或者用微信登录第三方网站,这就是 OAuth 那一套;另一种是系统内部的权限,比如普通员工看不到财务菜单。不管哪种,都要按资源单独授权,而且最终判断一定在服务端,前端隐藏按钮只是为了不让用户点了才报错。
- 用户授予第三方应用访问该用户某些资源的权限
- 你在安装手机应用的时候,APP 会询问是否允许授予权限(访问相册、地理位置等权限)
- 你在访问微信小程序时,当登录时,小程序会询问是否允许授予权限(获取昵称、头像、地区、性别等个人信息)
- 实现授权的方式有:
cookie、session、token、OAuth
面试官追问
追问 1用户已经登录了,小程序能直接读他的头像和位置吗?
不能,登录只证明他是谁。头像、位置这些要单独申请授权,用户拒绝了也得给一条能继续用的路。
追问 2之前用户授权过头像,新功能要定位,能复用之前的授权吗?
不能,授权是按资源给的,位置要单独申请。偷偷扩大范围既违反平台规范,用户知道了也会直接卸载。
追问 3管理后台前端把没权限的按钮都隐藏了,后端还要校验吗?
必须校验。用户可以直接用 curl 或者改前端代码调接口,前端隐藏只是体验优化,每个接口在服务端都要查一次当前用户的角色权限。
追问 4用户已登录,访问定位页却报无权限,怎么判断是认证还是授权问题?
先看接口返回码:401 是身份没认出来,查凭证;403 是身份对但没权限,查角色配置或者用户是否拒绝了位置授权。别一律让用户重新登录。
# 什么是凭证(Credentials)
30 秒速记
- 凭证(
Credentials)= 证明你身份的那个东西,认证和授权都靠它 - 登录前是账号密码、验证码;登录后服务端换给你一个凭证,比如
session id或Token - 游客模式能看,登录模式能点赞收藏,区别就在请求里有没有带有效凭证
