JWT鉴权的实现:从原理到 Django + Vue3

一、什么是 JWT?

1 JWT 的基本概念

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息。它由三部分组成,用点号(.)分隔:

css 复制代码
Header.Payload.Signature
AI写代码

一个典型的 JWT 长这样:

复制代码
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InRlc3R1c2VyIiwiZXhwIjoxNzAwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
AI写代码

2 JWT 的三部分详解

Header(头部)

Header 通常包含两个信息:算法类型和令牌类型。

json 复制代码
{
  "alg": "HS256",
  "typ": "JWT"
}
AI写代码
  • alg:签名算法,常用 HS256、RS256 等
  • typ:令牌类型,固定为 "JWT"

这部分经过 Base64Url 编码后形成第一段。

Payload(负载)

Payload 是实际要传递的数据,也叫声明(Claims)。JWT 有三种类型的声明:

类型 说明 示例
标准声明 JWT 标准预定义的声明 iss(签发者)、exp(过期时间)、sub(主题)
公共声明 可以自由使用,但要避免冲突 nameemail
私有声明 双方约定的私有信息 user_idrole

一个典型的 Payload:

json 复制代码
{
  "user_id": 1,
  "username": "testuser",
  "exp": 1700000000,
  "iat": 1699913600
}
AI写代码

注意:Payload 只是 Base64Url 编码,不是加密,所以不要把敏感信息(如密码)放在这里。

Signature(签名)

Signature 是对 Header 和 Payload 的签名,用于验证 JWT 是否被篡改。

scss 复制代码
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)
AI写代码
  • 前两部分拼接后,用密钥(secret)进行签名
  • 服务器验证时,用同样的密钥重新计算签名,对比是否一致
  • 如果 JWT 被篡改,签名就会对不上

二、为什么选择 JWT 而不是 Session?

1 传统 Session 认证的问题

在传统的 Web 应用中,用户登录后,服务器会在内存或数据库中创建一个 Session,然后把 Session ID 通过 Cookie 返回给客户端。客户端后续请求会自动带上这个 Cookie,服务器通过 Session ID 找到对应的 Session 数据。

这种方式在单体应用中没问题,但在前后端分离架构下会遇到几个问题:

问题 Session 方案 JWT 方案
跨域问题 Cookie 无法跨域,需要额外配置 CORS 通过 Authorization Header 传递,天然支持跨域
服务器压力 每次请求都要查询 Session,服务器压力大 无状态,服务器不需要存储 Session
水平扩展 多台服务器需要共享 Session(Redis) 每台服务器都能独立验证 Token
移动端支持 移动端 Cookie 管理复杂 Token 存储灵活,LocalStorage、内存都行

2 JWT 的优势

  1. 无状态:服务器不需要存储 Token,减轻服务器压力
  2. 跨域友好:通过 HTTP Header 传递,天然支持跨域
  3. 移动端友好:移动端存储 Token 比管理 Cookie 简单
  4. 信息丰富:Token 本身包含用户信息,减少数据库查询

3 JWT 的劣势

当然,JWT 也不是完美的,也有一些需要注意的地方:

  1. 无法主动失效:Token 一旦签发,在过期前无法主动撤销(除非用黑名单)
  2. Token 过大:如果 Payload 里信息太多,Token 会很长
  3. 安全风险:如果密钥泄露,攻击者可以伪造任意 Token

三、Django 后端实现

1 安装依赖

首先安装 djangorestframework-simplejwt

复制代码
pip install djangorestframework-simplejwt
AI写代码

2 配置 Django Settings

settings.py 中添加配置:

ini 复制代码
# 注册应用
INSTALLED_APPS = [
    # ... 其他应用
    'rest_framework_simplejwt',
]
​
# DRF 配置鉴权方式
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
}
​
# JWT 配置
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': datetime.timedelta(days=15),  # 访问令牌有效期
    'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=15),  # 刷新令牌有效期
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
}
AI写代码

配置说明

  • ACCESS_TOKEN_LIFETIME:Access Token 的有效期,我设置的是 15 天
  • REFRESH_TOKEN_LIFETIME:Refresh Token 的有效期,用于刷新 Access Token
  • USER_ID_FIELD:用户模型的 ID 字段
  • USER_ID_CLAIM:Token 中存储用户 ID 的字段名

3 实现登录接口

user/views.py 中实现登录视图:

python 复制代码
from rest_framework.views import APIView
from rest_framework.permissions import AllowAny
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth.hashers import check_password
from django.http import JsonResponse
import json
​
class LoginView(APIView):
    permission_classes = [AllowAny]  # 允许任何人登录
​
    def post(self, request):
        try:
            data = json.loads(request.body)
            username = data.get('username')
            password = data.get('password')
​
            # 查询用户
            user = SysUser.objects.get(username=username)
            
            # 验证密码
            if not check_password(password, user.password):
                return JsonResponse({'code': 500, 'info': '用户名或者密码错误!'})
​
            # 使用 simplejwt 生成 Token
            refresh = RefreshToken.for_user(user)
            token = str(refresh.access_token)
​
        except SysUser.DoesNotExist:
            return JsonResponse({'code': 500, 'info': '用户名或者密码错误!'})
        except Exception as e:
            print(e)
            return JsonResponse({'code': 500, 'info': '用户名或者密码错误!'})
​
        # 返回 Token
        return JsonResponse({
            'code': 200, 
            'token': token, 
            'info': '登录成功!'
        })
AI写代码

代码解析

  1. permission_classes = [AllowAny]:登录接口不需要认证,任何人都可以访问
  2. check_password(password, user.password):Django 提供的密码验证函数,会自动处理密码哈希
  3. RefreshToken.for_user(user):为用户生成 Refresh Token
  4. str(refresh.access_token):从 Refresh Token 中提取 Access Token
  5. 返回的 Token 格式:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

4 保护需要认证的接口

对于需要登录才能访问的接口,使用 IsAuthenticated 权限:

python 复制代码
from rest_framework.permissions import IsAuthenticated
​
class UserInfoView(APIView):
    permission_classes = [IsAuthenticated]  # 需要认证
​
    def get(self, request):
        user = request.user  # JWT 认证后,request.user 就是当前用户
        return JsonResponse({
            'code': 200,
            'data': {
                'username': user.username,
                'email': user.email,
            }
        })
AI写代码

关键点

  • permission_classes = [IsAuthenticated]:只有携带有效 Token 的请求才能访问
  • request.user:JWT 认证中间件会自动解析 Token,把用户信息注入到 request.user

四、Vue3 前端实现

1 Axios 请求拦截器

src/unit/request.ts 中配置 Axios 拦截器,自动添加 Token:

typescript 复制代码
import axios, { type AxiosError, type AxiosResponse } from 'axios'
import router from "@/router";
import { ElMessage } from "element-plus";
​
const httpServer = axios.create({
    baseURL: 'http://localhost:8000/',
    timeout: 300000
})
​
// 请求拦截器:自动添加 Token
httpServer.interceptors.request.use(
    (config) => {
        const token = window.localStorage.getItem('token');
        if (token) {
            if (!config.headers) {
                config.headers = config.headers || {}
            }
            // 去除 token 中可能存在的空白字符
            const cleanToken = token.replace(/\s+/g, '');
            // 添加 Authorization Header
            config.headers['Authorization'] = `Bearer ${cleanToken}`;
        }
        return config;
    },
    (error) => Promise.reject(error)
);
AI写代码

代码解析

  1. localStorage 获取 Token
  2. 如果 Token 存在,添加到请求头的 Authorization 字段
  3. 格式必须是 Bearer <token>,注意 Bearer 后面有个空格
  4. replace(/\s+/g, ''):去除 Token 中的空白字符,防止格式错误

2 响应拦截器:处理 Token 过期

go 复制代码
// 响应拦截器:处理错误
httpServer.interceptors.response.use(
  (response: AxiosResponse) => {
    return response;
  },
  (error: AxiosError) => {
    if (error.response) {
      const status = error.response.status;
      switch (status) {
        case 401:
          // Token 过期或无效
          localStorage.removeItem('token')  // 先删除无效 Token
          router.push('/login')              // 跳转到登录页
          window.location.reload()            // 刷新页面
          ElMessage.error('登录已过期,请重新登录')
          break;
        case 403:
          // 权限不足
          ElMessage.error('权限不足')
          break;
        case 500:
          // 服务器错误
          ElMessage.error('服务器错误')
          break;
      }
    }
    return Promise.reject(error);
  }
);
AI写代码

关键点

  • 401 状态码表示 Token 无效或过期
  • 收到 401 时,先删除本地 Token,然后跳转登录页
  • window.location.reload():刷新页面,清除所有状态

3 登录流程实现

在登录页面中,调用登录接口并保存 Token:

javascript 复制代码
const handleLogin = async () => {
  try {
    const res = await post('/api/user/login', {
      username: loginForm.username,
      password: loginForm.password,
    })
​
    if (res.code === 200) {
      // 保存 Token 到 localStorage
      localStorage.setItem('token', res.token)
      
      ElMessage.success('登录成功')
      
      // 跳转到首页
      router.push('/')
    } else {
      ElMessage.error(res.info || '登录失败')
    }
  } catch (error) {
    ElMessage.error('登录失败,请检查网络')
  }
}
AI写代码

4 退出登录

javascript 复制代码
const handleLogout = () => {
  // 删除 Token
  localStorage.removeItem('token')
  
  // 跳转到登录页
  router.push('/login')
  
  ElMessage.success('已退出登录')
}
AI写代码

五、完整的认证流程

1 流程图

复制代码

2 认证流程详解

①登录阶段

  • 用户在前端输入用户名和密码
  • 前端调用登录接口,发送用户名密码
  • 后端验证用户名密码,生成 JWT Token
  • 前端接收 Token,存储到 localStorage

②请求阶段

  • 前端发起 API 请求
  • Axios 请求拦截器自动从 localStorage 获取 Token
  • 在请求头中添加 Authorization: Bearer {token}
  • 后端接收请求,验证 Token 签名
  • 验证通过后,从 Token 中解析用户信息
  • 后端返回数据

③Token 过期处理

  • 如果 Token 过期,后端返回 401
  • 前端响应拦截器捕获 401
  • 删除本地 Token,跳转登录页

六总结

JWT 是一种非常适合前后端分离架构的鉴权方案,它具有无状态、跨域友好、易于扩展等优点。

核心要点

  • JWT 由 Header、Payload、Signature 三部分组成
  • 使用 Bearer {token} 格式在请求头中传递 Token
  • Token 只是 Base64 编码,不要放敏感信息
  • 生产环境必须使用 HTTPS 和强密钥
  • 实现 Token 刷新机制提升用户体验
相关推荐
2601_9620652517 小时前
从零创建一个 Django 项目
后端·python·django
千里码aicood19 小时前
Django 基于Django的电脑推荐与分析可视化系统设计与实现
python·django·电脑
汤姆yu19 小时前
基于Django的学习资料共享与智能推荐系统
后端·python·django·毕业设计·学习资料共享
Mr数据杨1 天前
【Codex】用学生入学评估模块建立新生能力画像
android·java·javascript·django·codex·项目开发
Mr数据杨1 天前
【Codex】用部门管理模块维护组织架构与管理层级
架构·django·codex·项目开发
Mr数据杨1 天前
【Codex】用附件管理模块统一维护系统文件资源
django·codex·项目开发
IT毕设实战小研1 天前
基于安卓的空气质量查询系统
android·大数据·vue.js·爬虫·python·django·课程设计
Mr数据杨1 天前
【Codex】用座位划分模块优化班级空间与课堂管理
django·codex·项目开发
Mr数据杨1 天前
【Codex】用系统配置工作台集中维护平台运行参数
django·codex·项目开发
Mr数据杨2 天前
【Codex】用学生生活日常模块记录校园生活过程
django·生活·codex·项目开发