当前位置: 首页 > news >正文

flask JWT 认证

目录

    • 项目概述
    • 后端实现详解
      • 1. 环境准备
      • 2. 核心组件
        • 用户模型和数据
        • JWT 令牌生成
        • 请求加载器(核心认证逻辑)
      • 3. API 端点
        • 登录接口
        • 受保护的接口
    • 前端实现详解
      • 1. 核心 JavaScript 功能
        • 通用请求函数
        • 登录功能
      • 2. 令牌管理
    • 完整代码
    • 运行步骤
      • 1. 启动后端服务
      • 2. 打开前端页面
      • 3. 测试功能
    • 关键特性
      • 1. Authorization 头认证
      • 2. 令牌生命周期管理
      • 3. 安全特性
      • 4. 用户体验

下面详细介绍如何使用 Flask 后端和 HTML 前端实现 JWT(JSON Web Token)认证系统。

项目概述

这是一个完整的 JWT 认证示例,包含:

  • Flask 后端 API
  • HTML 前端界面
    项目目录
    在这里插入图片描述

后端实现详解

1. 环境准备

首先安装虚拟环境和必要的 Python 库:

uv init
uv venv
source .venv/Script/activate
uv pip install flask flask-cors flask-login itsdangerous

2. 核心组件

用户模型和数据
class User(UserMixin):def __init__(self, id, username, email):self.id = idself.username = usernameself.email = email# 模拟用户数据库
users_db = {'admin': {'id': '1', 'username': 'admin', 'email': 'admin@example.com', 'password': 'password123'},'user': {'id': '2', 'username': 'user', 'email': 'user@example.com', 'password': 'password456'}
}
JWT 令牌生成
# JWT 序列化器
jwt_serializer = Serializer(app.config['SECRET_KEY'])# 生成 JWT 令牌
def generate_token(user_data):payload = {'user_id': user_data['id'],'username': user_data['username']}token = jwt_serializer.dumps(payload)return token
请求加载器(核心认证逻辑)
@login_manager.request_loader
def load_user_from_request(request):# 获取 Authorization 头authorization = request.headers.get('Authorization')if not authorization:return Nonetry:# 移除 'Bearer ' 前缀(如果有)if authorization.startswith('Bearer '):token = authorization[7:]else:token = authorization# 解析 JWT 令牌payload = jwt_serializer.loads(token, max_age=24*3600)  # 24小时有效期# 根据 payload 创建用户对象user = User(id=payload['user_id'],username=payload['username'],email=f"{payload['username']}@example.com")return userexcept Exception as e:return None

3. API 端点

登录接口
@app.route('/api/login', methods=['POST'])
def login():data = request.get_json()username = data.get('username')password = data.get('password')# 验证用户凭证if username in users_db and users_db[username]['password'] == password:user_data = users_db[username]# 生成 JWT 令牌token = generate_token(user_data)return jsonify({'success': True,'message': '登录成功!','access_token': token,'user': {'id': user_data['id'],'username': user_data['username'],'email': user_data['email']}})return jsonify({'success': False,'message': '用户名或密码错误!'}), 401
受保护的接口
@app.route('/api/profile', methods=['GET'])
def get_profile():if current_user.is_authenticated:return jsonify({'success': True,'user': {'id': current_user.id,'username': current_user.username,'email': current_user.email},'message': f'欢迎,{current_user.username}!'})else:return jsonify({'success': False,'message': '未授权访问,请先登录!'}), 401

前端实现详解

1. 核心 JavaScript 功能

通用请求函数
async function makeRequest(url, options = {}) {try {// 如果有令牌,自动添加 Authorization 头if (currentToken) {options.headers = {'Authorization': `Bearer ${currentToken}`,'Content-Type': 'application/json',...options.headers};} else {options.headers = {'Content-Type': 'application/json',...options.headers};}const response = await fetch(API_BASE + url, options);const data = await response.json();return { success: response.ok, data, status: response.status };} catch (error) {return { success: false, error: error.message };}
}
登录功能
async function login() {const username = document.getElementById('username').value;const password = document.getElementById('password').value;const result = await makeRequest('/api/login', {method: 'POST',body: JSON.stringify({ username, password })});if (result.success && result.data.access_token) {// 保存令牌到本地存储currentToken = result.data.access_token;localStorage.setItem('access_token', currentToken);updateLoginStatus(true);}showResponse('loginResponse', result);
}

2. 令牌管理

  • 存储:使用 localStorage 持久化存储 JWT 令牌
  • 自动加载:页面加载时检查本地存储的令牌
  • 自动添加:每个 API 请求自动添加 Authorization

完整代码

html 页面

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>JWT 认证示例</title><style>body {font-family: Arial, sans-serif;max-width: 800px;margin: 0 auto;padding: 20px;background-color: #f5f5f5;}.container {background: white;padding: 20px;border-radius: 8px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);margin-bottom: 20px;}.form-group {margin-bottom: 15px;}label {display: block;margin-bottom: 5px;font-weight: bold;}input, button {padding: 8px 12px;border: 1px solid #ddd;border-radius: 4px;font-size: 14px;}input {width: 200px;}button {background-color: #007bff;color: white;border: none;cursor: pointer;margin-right: 10px;}button:hover {background-color: #0056b3;}button:disabled {background-color: #6c757d;cursor: not-allowed;}.response {background-color: #f8f9fa;border: 1px solid #dee2e6;border-radius: 4px;padding: 10px;margin-top: 10px;white-space: pre-wrap;font-family: monospace;font-size: 12px;max-height: 300px;overflow-y: auto;}.success {color: #28a745;}.error {color: #dc3545;}.token-display {background-color: #e9ecef;padding: 10px;border-radius: 4px;word-break: break-all;font-family: monospace;font-size: 11px;}.status {padding: 5px 10px;border-radius: 4px;font-weight: bold;display: inline-block;margin-bottom: 10px;}.status.logged-in {background-color: #d4edda;color: #155724;}.status.logged-out {background-color: #f8d7da;color: #721c24;}</style>
</head>
<body><h1>JWT 认证示例 - Authorization 头演示</h1><!-- 登录状态显示 --><div class="container"><h2>登录状态</h2><div id="loginStatus" class="status logged-out">未登录</div><div id="tokenDisplay" class="token-display" style="display: none;"><strong>当前 JWT 令牌:</strong><br><span id="currentToken"></span></div></div><!-- 登录表单 --><div class="container"><h2>用户登录</h2><div class="form-group"><label>用户名:</label><input type="text" id="username" value="admin" placeholder="admin 或 user"></div><div class="form-group"><label>密码:</label><input type="password" id="password" value="password123" placeholder="password123 或 password456"></div><button onclick="login()">登录</button><button onclick="logout()">登出</button><div id="loginResponse" class="response"></div></div><!-- API 测试 --><div class="container"><h2>API 测试(需要 Authorization 头)</h2><button onclick="getProfile()" id="profileBtn" disabled>获取用户资料</button><button onclick="testHeaders()" id="headersBtn" disabled>测试请求头</button><div id="apiResponse" class="response"></div></div><!-- 请求示例 --><div class="container"><h2>请求示例代码</h2><div id="requestExample" class="response">
// 前端发送带 Authorization 头的请求示例:fetch('http://localhost:5000/api/profile', {method: 'GET',headers: {'Authorization': 'Bearer ' + localStorage.getItem('access_token'),'Content-Type': 'application/json'}
})
.then(response => response.json())
.then(data => console.log(data));</div></div><script>const API_BASE = 'http://localhost:5000';let currentToken = null;// 页面加载时检查本地存储的令牌window.onload = function() {const token = localStorage.getItem('access_token');if (token) {currentToken = token;updateLoginStatus(true);}};// 更新登录状态显示function updateLoginStatus(isLoggedIn) {const statusElement = document.getElementById('loginStatus');const tokenDisplay = document.getElementById('tokenDisplay');const currentTokenElement = document.getElementById('currentToken');const profileBtn = document.getElementById('profileBtn');const headersBtn = document.getElementById('headersBtn');if (isLoggedIn && currentToken) {statusElement.textContent = '已登录';statusElement.className = 'status logged-in';tokenDisplay.style.display = 'block';currentTokenElement.textContent = currentToken;profileBtn.disabled = false;headersBtn.disabled = false;} else {statusElement.textContent = '未登录';statusElement.className = 'status logged-out';tokenDisplay.style.display = 'none';profileBtn.disabled = true;headersBtn.disabled = true;}}// 通用请求函数async function makeRequest(url, options = {}) {try {// 如果有令牌,自动添加 Authorization 头if (currentToken) {options.headers = {'Authorization': `Bearer ${currentToken}`,'Content-Type': 'application/json',...options.headers};} else {options.headers = {'Content-Type': 'application/json',...options.headers};}console.log('🚀 发送请求:', {url: API_BASE + url,method: options.method || 'GET',headers: options.headers});const response = await fetch(API_BASE + url, options);const data = await response.json();return { success: response.ok, data, status: response.status };} catch (error) {return { success: false, error: error.message };}}// 显示响应function showResponse(elementId, result) {const element = document.getElementById(elementId);if (result.success) {element.className = 'response success';element.textContent = JSON.stringify(result.data, null, 2);} else {element.className = 'response error';element.textContent = result.error || JSON.stringify(result.data, null, 2);}}// 登录async function login() {const username = document.getElementById('username').value;const password = document.getElementById('password').value;const result = await makeRequest('/api/login', {method: 'POST',body: JSON.stringify({ username, password })});if (result.success && result.data.access_token) {// 保存令牌到本地存储currentToken = result.data.access_token;localStorage.setItem('access_token', currentToken);updateLoginStatus(true);console.log('✅ 登录成功,令牌已保存:', currentToken.substring(0, 50) + '...');}showResponse('loginResponse', result);}// 登出function logout() {currentToken = null;localStorage.removeItem('access_token');updateLoginStatus(false);document.getElementById('loginResponse').textContent = '已登出';document.getElementById('apiResponse').textContent = '';console.log('👋 已登出');}// 获取用户资料(需要 Authorization 头)async function getProfile() {if (!currentToken) {alert('请先登录!');return;}const result = await makeRequest('/api/profile');showResponse('apiResponse', result);}// 测试请求头async function testHeaders() {const result = await makeRequest('/api/test-headers');showResponse('apiResponse', result);}</script>
</body>
</html>

backend_jwt_example.py

from flask import Flask, request, jsonify
from flask_cors import CORS
from flask_login import LoginManager, UserMixin, login_user, current_user
from itsdangerous import URLSafeTimedSerializer as Serializer
import datetimeapp = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'# 启用 CORS
CORS(app, supports_credentials=True)# 初始化 LoginManager
login_manager = LoginManager()
login_manager.init_app(app)# 模拟用户数据
class User(UserMixin):def __init__(self, id, username, email):self.id = idself.username = usernameself.email = emailusers_db = {'admin': {'id': '1', 'username': 'admin', 'email': 'admin@example.com', 'password': 'password123'},'user': {'id': '2', 'username': 'user', 'email': 'user@example.com', 'password': 'password456'}
}# 存储有效的 access_token
valid_tokens = {}# JWT 序列化器
jwt_serializer = Serializer(app.config['SECRET_KEY'])# 生成 JWT 令牌
def generate_token(user_data):payload = {'user_id': user_data['id'],'username': user_data['username']}token = jwt_serializer.dumps(payload)return token# request_loader - 从 Authorization 头获取用户
@login_manager.request_loader
def load_user_from_request(request):# 获取 Authorization 头authorization = request.headers.get('Authorization')if not authorization:return Nonetry:# 移除 'Bearer ' 前缀(如果有)if authorization.startswith('Bearer '):token = authorization[7:]else:token = authorization# 解析 JWT 令牌payload = jwt_serializer.loads(token, max_age=24*3600)  # 24小时有效期# 根据 payload 创建用户对象user = User(id=payload['user_id'],username=payload['username'],email=f"{payload['username']}@example.com")print(f"✅ 成功验证用户: {user.username}")return userexcept Exception as e:print(f"❌ JWT 验证失败: {e}")return None# 登录接口
@app.route('/api/login', methods=['POST'])
def login():data = request.get_json()username = data.get('username')password = data.get('password')# 验证用户凭证if username in users_db and users_db[username]['password'] == password:user_data = users_db[username]# 生成 JWT 令牌token = generate_token(user_data)return jsonify({'success': True,'message': '登录成功!','access_token': token,'user': {'id': user_data['id'],'username': user_data['username'],'email': user_data['email']}})return jsonify({'success': False,'message': '用户名或密码错误!'}), 401# 受保护的接口 - 需要 Authorization 头
@app.route('/api/profile', methods=['GET'])
def get_profile():if current_user.is_authenticated:return jsonify({'success': True,'user': {'id': current_user.id,'username': current_user.username,'email': current_user.email},'message': f'欢迎,{current_user.username}!'})else:return jsonify({'success': False,'message': '未授权访问,请先登录!'}), 401# 测试接口 - 显示请求头信息
@app.route('/api/test-headers', methods=['GET'])
def test_headers():headers = dict(request.headers)authorization = request.headers.get('Authorization')return jsonify({'authorization_header': authorization,'all_headers': headers,'is_authenticated': current_user.is_authenticated,'current_user': current_user.username if current_user.is_authenticated else None})if __name__ == '__main__':print("\n=== JWT 认证示例启动 ===")print("测试用户:")print("  - 用户名: admin, 密码: password123")print("  - 用户名: user, 密码: password456")print("\n接口说明:")print("  - POST /api/login - 登录获取令牌")print("  - GET /api/profile - 获取用户资料(需要 Authorization 头)")print("  - GET /api/test-headers - 查看请求头信息")print("========================\n")app.run(debug=True, host='0.0.0.0', port=5000)

运行步骤

1. 启动后端服务

uv run backend_jwt_example.py

服务将在 http://localhost:5000 启动。

2. 打开前端页面

在浏览器中打开 http://localhost:5000/static/frontend_jwt_example.html 文件。

3. 测试功能

  1. 登录测试

    • 用户名:admin,密码:password123
    • 用户名:user,密码:password456
  2. API 测试

    • 登录成功后,点击"获取用户资料"按钮
    • 点击"测试请求头"查看认证信息
      在这里插入图片描述

关键特性

1. Authorization 头认证

  • 前端自动在请求头中添加 Authorization: Bearer <token>
  • 后端通过 request_loader 自动解析和验证令牌

2. 令牌生命周期管理

  • 令牌有效期:24小时
  • 自动过期处理
  • 本地存储持久化

3. 安全特性

  • CORS 支持跨域请求
  • 令牌签名验证
  • 自动过期检查

4. 用户体验

  • 实时登录状态显示
  • 令牌可视化
  • 详细的错误信息
  • 请求示例代码展示
http://www.xdnf.cn/news/1036225.html

相关文章:

  • 了解Redis的使用
  • 【AS32系列MCU调试教程】性能优化:Eclipse环境下AS32芯片调试效率提升
  • CSS预编译语言less
  • 键盘按键枚举 Key 说明文档
  • iOS swiftUI的实用举例
  • 人工智能学习15-Numpy-花式索引和索引技巧
  • linux常用基础命令_新
  • Java 数据类型选择题
  • 使用大模型预测短暂性脑缺血发作(TIA)的全流程系统技术方案大纲
  • Python Flask 框架学习笔记
  • Linux操作系统之运维常用命令
  • 华为OD机试_2025 B卷_字符串分割(Python,100分)(附详细解题思路)
  • aflplusplus:开源的模糊测试工具!全参数详细教程!Kali Linux教程!(四)
  • 22 - PSA模块
  • 解惑1、为何大容量电容滤低频,小容量电容滤高频
  • 数据库资源帖
  • 同旺科技 USB TO SPI / I2C适配器(专业版)--EEPROM读写——A
  • 代码随想录算法训练营day4
  • (15)python+ selenium自动化测试 - 回顾2
  • 采用微服务的预期收益是什么?我们如何衡量成功?
  • 大IPD之——学习华为市场洞察,为战略找到方向(四)
  • FastGPT实战:从0搭建AI知识库与MCP AI Agent系统
  • Java求职者面试题解析:Spring、Spring Boot、MyBatis框架与源码原理
  • SpringBoot自动化部署实战指南
  • 【Photoshop】使用内容识别去除水印文字
  • Vue3 + TypeScript + Element Plus 表格实例null检查方法
  • Java设计题:如何设计一个线程池
  • 浅拷贝 与 深拷贝
  • HTTP1.1
  • JUC核心解析系列(四)——同步工具类 (Synchronizers)深度解析