You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt中HttpOnly Cookie页面刷新后丢失,如何安全存储JWT?

问题分析与解决方案

你的核心问题出在对HttpOnly Cookie的理解偏差:HttpOnly Cookie是浏览器专门限制前端JS读取/修改的安全Cookie,完全只能由后端服务来设置和管理。你用Pinia的前端持久化逻辑去写HttpOnly Cookie,本质上是行不通的——前端JS根本没有权限操作HttpOnly Cookie,所以看似“页面刷新后Cookie丢失”,实际上这个Cookie从一开始就没被正确设置过。

下面是完整的修正方案:

登录接口的JWT令牌,应该由Django后端直接写入HttpOnly Cookie,而不是返回给前端让前端存储。

修改Django登录视图(示例):

from django.http import JsonResponse
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth import authenticate

def login_view(request):
    # 解析前端传入的账号密码
    data = request.json()
    username = data.get('username')
    password = data.get('password')
    
    user = authenticate(username=username, password=password)
    if user:
        # 生成JWT令牌
        refresh = RefreshToken.for_user(user)
        access_token = str(refresh.access_token)
        
        # 构造响应,将access_token写入HttpOnly Cookie
        response = JsonResponse({"message": "登录成功"})
        response.set_cookie(
            key="access_token",
            value=access_token,
            httponly=True,  # 核心:标记为HttpOnly,禁止前端JS访问
            secure=True,  # 生产环境必须开启,仅在HTTPS下传输
            samesite="Strict",
            max_age=3600,  # 和JWT过期时间保持一致(示例1小时)
            path="/"  # 确保全站接口都能携带该Cookie
        )
        return response
    else:
        return JsonResponse({"error": "账号密码错误"}, status=401)

同时配置Django跨域(如果前后端域名不同):
安装django-cors-headers后,在settings.py中添加:

INSTALLED_APPS = [
    # ...其他依赖
    'corsheaders',
]

MIDDLEWARE = [
    # ...其他中间件
    'corsheaders.middleware.CorsMiddleware',
]

CORS_ALLOW_CREDENTIALS = True  # 允许携带Cookie跨域
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",  # 你的Nuxt前端地址
]

二、前端(Nuxt+Pinia)修改:依赖后端验证状态

前端不再存储JWT令牌,而是通过后端设置的Cookie维持会话,用户状态通过后端接口验证。

1. 修改Pinia Auth Store

去掉前端持久化逻辑,添加状态验证和登出的action:

import { defineStore } from 'pinia'

export const useAuthStore = defineStore({
    id: 'auth',
    state: () => ({
        user: {
            isAuthenticated: false
        }
    }),

    actions: {
        // 检查用户登录状态:调用后端接口验证Cookie中的JWT
        async checkAuthStatus() {
            const response = await useFetch('auth/check-status', {
                baseURL: 'http://127.0.0.1:8000/',
                credentials: 'include',  // 关键:携带Cookie发送请求
                onResponse({ response }) {
                    this.user.isAuthenticated = response._data.is_authenticated
                },
                onResponseError() {
                    this.user.isAuthenticated = false
                }
            })
        },
        
        // 登出:调用后端接口清除Cookie
        async logout() {
            await useFetch('auth/logout', {
                method: 'POST',
                baseURL: 'http://127.0.0.1:8000/',
                credentials: 'include',
            })
            this.user.isAuthenticated = false
            navigateTo('/login')
        }
    }
})

2. 修改登录页面逻辑

登录时不需要存储token,只需确保请求携带Cookie,登录成功后直接跳转:

async function submit() { 
    const response = await useFetch('auth/jwt/create', {
        method: 'POST',
        baseURL: 'http://127.0.0.1:8000/',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(loginData),
        credentials: 'include',  // 允许接收后端设置的Cookie
        onResponseError({response}) {
            console.log('登录失败:' + response.status)
        },
    })
    if (response.value.ok) {
        navigateTo('/')
    }
}

3. 修改全局路由中间件

进入路由前先验证用户状态,避免依赖前端本地缓存:

import { useAuthStore } from "~/stores/auth"

export default defineNuxtRouteMiddleware(async (to, from) => {
    const authStore = useAuthStore()
    
    // 首次进入或刷新时,先调用后端接口验证状态
    if (!authStore.user.isAuthenticated) {
        await authStore.checkAuthStatus()
    }

    if (to.path !== '/login' && !authStore.user.isAuthenticated) {
        return navigateTo('/login')
    }

    if (to.path === '/login' && authStore.user.isAuthenticated) {
        return navigateTo('/')
    }
})

三、补充说明

  • 后端需要新增auth/check-status接口:用于验证Cookie中的JWT是否有效,返回{"is_authenticated": true/false}
  • 后端登出接口需要清除Cookie:response.delete_cookie("access_token", path="/")
  • 生产环境必须开启secure=True,确保Cookie仅在HTTPS下传输,避免被窃听

内容的提问来源于stack exchange,提问作者Shirony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 13:05:08