JWT + HttpOnly Cookie 简介

这是一种目前广泛使用的前后端分离认证方案,结合了 JWT 的无状态特性和 HttpOnly Cookie 的安全优势。

什么是 JWT ?

JWT(JSON Web Token) 是一种紧凑的、自包含的令牌格式,用于在客户端和服务器之间安全传输信息。

JWT 结构

header.payload.signature
部分 内容 示例
Header 算法类型、令牌类型 {"alg":"HS256","typ":"JWT"}
Payload 用户信息、过期时间等 {"userId":123,"role":"admin","exp":1700000000}
Signature 签名,防篡改 使用密钥对 header+payload 签名

传统 JWT 存放方式的问题

方式1:localStorage / sessionStorage

// 登录后存储
localStorage.setItem('token', jwtToken)

// 每次请求手动携带
axios.defaults.headers.common['Authorization'] = `Bearer ${jwtToken}`

风险:XSS 攻击可直接读取 localStorage,窃取 JWT。

方式2:普通 Cookie

document.cookie = `token=${jwtToken}`

风险:同样可被 JavaScript 读取,且易受 CSRF 攻击。


JWT + HttpOnly Cookie 方案

工作原理

用户登录 → 服务器生成 JWT → 通过 Set-Cookie 放入 HttpOnly Cookie → 
浏览器自动在后续请求中携带该 Cookie → 服务器验证 JWT

流程图

┌──────────┐          POST /login            ┌──────────┐
│          │ ──────────────────────────────> │          │
│  浏览器  │   { username, password }         │  服务器  │
│          │ <────────────────────────────── │          │
└──────────┘   Set-Cookie: token=JWT; HttpOnly; Secure; SameSite=Strict
     │                                            │
     │  后续请求自动携带 Cookie                     │
     │ ────────────────────────────────────────>  │
     │   Authorization: Bearer JWT (Cookie)       │ 验证JWT
     │ <────────────────────────────────────────  │
     │   返回数据                                  │

代码实现示例

后端(Node.js + Express)

const jwt = require('jsonwebtoken')

// 登录接口
app.post('/login', (req, res) => {
    const { username, password } = req.body

    // 验证用户名密码...
    const user = { id: 123, role: 'admin' }

    // 生成 JWT
    const token = jwt.sign(user, 'secret_key', { expiresIn: '7d' })

    // 通过 HttpOnly Cookie 返回
    res.cookie('token', token, {
        httpOnly: true,      // 禁止 JavaScript 读取
        secure: true,        // 仅 HTTPS
        sameSite: 'strict',  // 防止 CSRF

        maxAge: 7 * 24 * 60 * 60 * 1000  // 7天过期
    })

    res.json({ message: '登录成功' })
})

// 验证中间件
const authMiddleware = (req, res, next) => {
    const token = req.cookies.token  // 从 Cookie 中读取
    if (!token) return res.status(401).json({ error: '未登录' })

    try {
        const decoded = jwt.verify(token, 'secret_key')
        req.user = decoded
        next()
    } catch (err) {
        res.status(401).json({ error: '令牌无效' })
    }
}

// 受保护的接口
app.get('/profile', authMiddleware, (req, res) => {
    res.json({ user: req.user })
})

前端(Vue 示例)

// 登录 - 无需手动处理 token
async login(username, password) {
    await axios.post('/api/login', { username, password })
    // Cookie 由浏览器自动管理,无需前端存储
}

// 请求受保护资源 - Cookie 自动携带
async getProfile() {
    const res = await axios.get('/api/profile')
    return res.data
}

// 登出 - 清除 Cookie
async logout() {
    await axios.post('/api/logout')
}

优缺点对比

对比项 传统 localStorage + JWT HttpOnly Cookie + JWT
XSS 防护 ❌ 易被窃取 ✅ JavaScript 无法读取
CSRF 防护 ✅ 需手动携带 ❌ 需配合 SameSite 或 CSRF Token
前端便捷性 ❌ 需手动管理 ✅ 浏览器自动处理
跨域支持 ✅ 灵活 ⚠️ 需配置 CORS withCredentials
移动端/API 调用 ✅ 方便携带 ⚠️ 需额外处理

最佳实践组合

HttpOnly Cookie + Secure + SameSite=Strict + CSRF Token
配置 作用
HttpOnly 防止 XSS 窃取 JWT
Secure 仅 HTTPS 传输
SameSite=Strict 防止 CSRF 攻击
CSRF Token 额外一层 CSRF 防护(可选)

一句话总结

JWT + HttpOnly Cookie 就是把 JWT 放在浏览器自动管理的 HttpOnly Cookie 中,既保留了 JWT 的无状态优势,又避免了 XSS 攻击窃取令牌的风险,是目前前后端分离项目中最推荐的认证方案之一。