FastAPI JWT 登录认证实战:Access Token、Refresh Token 与权限保护
在前后端分离项目中用户登录后如何保持身份状态是后端开发必须解决的问题。传统 Web 项目经常使用 Session用户登录成功后服务器保存会话数据并向浏览器返回 Session ID。但在移动端、多服务部署和前后端分离场景中JWTJSON Web Token是一种更常见的身份认证方案。本文将使用 FastAPI 实现一套基础的 JWT 登录认证机制内容包括用户密码验证Access Token 的生成与解析Refresh Token 的设计接口身份保护Token 过期与刷新退出登录与 Token 撤销实际项目中的安全注意事项。一、JWT 是什么JWT 是一种可以在客户端和服务器之间传递声明信息的 Token 格式。一个 JWT 通常由三部分组成Header.Payload.Signature例如eyJhbGciOiJIUzI1NiJ9 . eyJzdWIiOiIxMDAxIiwiZXhwIjoxNzIwMDAwMDAwfQ . xxxxxxxxxxxxxxxx三部分分别表示Header使用的签名算法和 Token 类型Payload用户 ID、过期时间、Token 类型等声明Signature服务器根据密钥生成的签名。JWT 的 Payload 只是经过 Base64URL 编码并不是加密内容。因此不应该在其中保存密码、身份证号、聊天内容等敏感信息。JWT 签名的主要作用是防止内容被篡改而不是隐藏内容。二、JWT 登录认证的基本流程一次完整的 JWT 登录过程如下用户提交账号和密码 ↓ 服务器验证用户信息 ↓ 生成 Access Token 和 Refresh Token ↓ 客户端保存 Token ↓ 请求接口时携带 Access Token ↓ 服务器验证签名和有效期 ↓ 允许或拒绝访问客户端一般通过请求头携带 TokenAuthorization: Bearer access_tokenAccess Token 过期后客户端可以使用 Refresh Token 获取新的 Access Token而不需要用户立即重新输入密码。三、安装项目依赖安装 FastAPI、JWT 和密码哈希相关依赖pip install fastapi uvicorn pyjwt bcrypt项目可以先使用一个文件演示jwt_demo/ └── main.py启动命令uvicorn main:app --reload四、不要明文保存用户密码用户密码不能直接保存到数据库中应该保存密码经过哈希计算后的结果。可以使用bcrypt处理密码import bcrypt def hash_password(password: str) - str: password_bytes password.encode(utf-8) hashed bcrypt.hashpw( password_bytes, bcrypt.gensalt(), ) return hashed.decode(utf-8) def verify_password( plain_password: str, hashed_password: str, ) - bool: return bcrypt.checkpw( plain_password.encode(utf-8), hashed_password.encode(utf-8), )注册用户时保存哈希结果password_hash hash_password( example_password )用户登录时通过verify_password()判断输入密码是否正确。密码哈希与普通加密不同。系统不需要还原用户的原始密码只需要验证用户本次输入是否与之前保存的密码一致。五、配置 JWT 密钥JWT 签名密钥不能直接写死在代码仓库中应该通过环境变量读取import os JWT_SECRET os.environ[JWT_SECRET] JWT_ALGORITHM HS256启动服务前配置环境变量export JWT_SECRET请替换成足够长的随机字符串 uvicorn main:app --reload生产环境中的密钥应该具有足够的随机性不提交到 Git 仓库不直接输出到日志定期进行安全检查通过密钥管理服务或安全配置系统保存。如果密钥泄露攻击者就可能伪造合法 Token。六、生成 Access TokenAccess Token 用于访问需要登录的接口其有效时间通常较短。from datetime import ( datetime, timedelta, timezone, ) from uuid import uuid4 import jwt ACCESS_TOKEN_EXPIRE_MINUTES 30 def create_access_token(user_id: int) - str: now datetime.now(timezone.utc) payload { sub: str(user_id), type: access, iat: now, exp: now timedelta( minutesACCESS_TOKEN_EXPIRE_MINUTES ), jti: str(uuid4()), } return jwt.encode( payload, JWT_SECRET, algorithmJWT_ALGORITHM, )这里使用了几个常见字段字段含义subToken 对应的用户typeToken 类型iatToken 签发时间expToken 过期时间jtiToken 的唯一编号建议使用 UTC 时间处理 Token 有效期减少不同服务器时区产生的问题。七、生成 Refresh TokenRefresh Token 只用于申请新的 Access Token其有效期通常更长REFRESH_TOKEN_EXPIRE_DAYS 7 def create_refresh_token(user_id: int) - str: now datetime.now(timezone.utc) payload { sub: str(user_id), type: refresh, iat: now, exp: now timedelta( daysREFRESH_TOKEN_EXPIRE_DAYS ), jti: str(uuid4()), } return jwt.encode( payload, JWT_SECRET, algorithmJWT_ALGORITHM, )虽然两种 Token 都使用 JWT 格式但必须通过type字段进行区分。如果后端没有检查 Token 类型攻击者可能把有效期较长的 Refresh Token 当成 Access Token 使用从而绕过原本的有效期设计。八、实现用户登录接口首先定义请求和响应模型from pydantic import BaseModel class LoginRequest(BaseModel): username: str password: str class TokenResponse(BaseModel): access_token: str refresh_token: str token_type: str bearer为了简化示例使用字典模拟数据库fake_users { demo: { id: 1001, username: demo, password_hash: hash_password( 123456 ), is_active: True, } }实现登录接口from fastapi import FastAPI, HTTPException app FastAPI() app.post( /auth/login, response_modelTokenResponse, ) def login(request: LoginRequest): user fake_users.get(request.username) if not user: raise HTTPException( status_code401, detail用户名或密码错误, ) if not verify_password( request.password, user[password_hash], ): raise HTTPException( status_code401, detail用户名或密码错误, ) if not user[is_active]: raise HTTPException( status_code403, detail用户已被禁用, ) return TokenResponse( access_tokencreate_access_token( user[id] ), refresh_tokencreate_refresh_token( user[id] ), )无论用户名不存在还是密码错误接口都返回相同提示可以避免向外部暴露账号是否存在。九、解析和验证 TokenFastAPI 提供了OAuth2PasswordBearer可以从请求头中提取 Bearer Tokenfrom fastapi.security import OAuth2PasswordBearer oauth2_scheme OAuth2PasswordBearer( tokenUrl/auth/login )然后实现当前用户解析逻辑from fastapi import Depends, HTTPException from jwt import ( ExpiredSignatureError, InvalidTokenError, ) def get_current_user_id( token: str Depends(oauth2_scheme), ) - int: try: payload jwt.decode( token, JWT_SECRET, algorithms[JWT_ALGORITHM], ) if payload.get(type) ! access: raise HTTPException( status_code401, detailToken 类型错误, ) user_id payload.get(sub) if not user_id: raise HTTPException( status_code401, detailToken 缺少用户信息, ) return int(user_id) except ExpiredSignatureError: raise HTTPException( status_code401, detailToken 已过期, ) except ( InvalidTokenError, TypeError, ValueError, ): raise HTTPException( status_code401, detail无效的 Token, )jwt.decode()会验证签名和过期时间。验证失败时接口应该返回401 Unauthorized。十、保护需要登录的接口有了get_current_user_id()就可以通过依赖注入保护接口app.get(/users/me) def get_current_user( user_id: int Depends( get_current_user_id ), ): return { user_id: user_id, message: 身份验证成功, }客户端请求时必须携带 Access Tokencurl \ -H Authorization: Bearer access_token \ http://localhost:8000/users/me如果 Token 不存在、签名错误或已经过期服务器会拒绝访问。十一、实现 Token 刷新接口定义刷新请求模型class RefreshRequest(BaseModel): refresh_token: str刷新接口需要确认传入的是 Refresh Tokenapp.post(/auth/refresh) def refresh_access_token( request: RefreshRequest, ): try: payload jwt.decode( request.refresh_token, JWT_SECRET, algorithms[JWT_ALGORITHM], ) if payload.get(type) ! refresh: raise HTTPException( status_code401, detailToken 类型错误, ) user_id payload.get(sub) if not user_id: raise HTTPException( status_code401, detailToken 缺少用户信息, ) return { access_token: create_access_token( int(user_id) ), token_type: bearer, } except ExpiredSignatureError: raise HTTPException( status_code401, detailRefresh Token 已过期, ) except ( InvalidTokenError, TypeError, ValueError, ): raise HTTPException( status_code401, detail无效的 Refresh Token, )实际项目中刷新时还应该检查用户是否仍然存在用户是否已被禁用Refresh Token 是否被撤销用户密码是否已经修改当前设备是否仍然可信。不能只验证 JWT 签名后就无条件签发新 Token。十二、退出登录为什么不能只删除前端 TokenJWT 的特点之一是服务器可以不保存会话状态。这也意味着只要 Token 没有过期服务器通常就会认为它有效。用户在前端点击退出登录只是删除了当前设备保存的 Token并不能让已经泄露的 Token 立即失效。如果业务要求退出后立即失效可以使用 Redis 保存 Token 黑名单。退出时将 Access Token 的jti写入 Redis并设置与 Token 剩余有效期相同的过期时间jwt:blacklist:jti 1验证 Token 时检查jti payload.get(jti) if redis_client.exists( fjwt:blacklist:{jti} ): raise HTTPException( status_code401, detailToken 已失效, )当 Token 自然过期后对应的黑名单记录也可以自动删除。另一种方案是保存用户的 Token 版本号。修改密码、退出所有设备或封禁账号时提高版本号让之前签发的 Token 全部失效。十三、同言翻译场景中的身份认证设计对于具有个人账号、历史会话和跨设备使用需求的应用身份认证不仅关系到接口能否访问也关系到用户数据是否会被错误读取。以同言翻译为例用户可能需要查看自己的翻译记录、管理术语配置或在不同设备间同步会话。后端接口需要通过 Access Token 确认请求者身份并在查询数据时同时校验资源归属关系。下面这种写法只根据会话 ID 查询数据存在越权风险app.get(/sessions/{session_id}) def get_session(session_id: int): return query_session(session_id)即使接口要求登录用户仍然可能通过修改session_id访问其他人的会话。更安全的方式是同时使用当前用户 ID 查询app.get(/sessions/{session_id}) def get_session( session_id: int, user_id: int Depends( get_current_user_id ), ): session query_user_session( user_iduser_id, session_idsession_id, ) if not session: raise HTTPException( status_code404, detail会话不存在, ) return session对于同言翻译这类涉及语音、文本和会话内容的应用仅验证“用户是否登录”是不够的还必须验证“当前用户是否有权访问这份数据”。此外可以为敏感操作增加更严格的安全措施例如修改密码前重新验证身份导出历史记录时进行二次确认Refresh Token 按设备分别管理异常登录后使旧 Token 失效对重要接口记录安全审计日志不在 JWT 中保存翻译原文或会话内容。十四、Access Token 应该保存在哪里不同客户端需要采用不同的 Token 保存策略。浏览器应用常见方式包括内存变量HttpOnly CookiesessionStoragelocalStorage。将 Token 保存到localStorage实现简单但如果页面存在 XSS 漏洞恶意脚本可能读取 Token。使用HttpOnly Cookie可以阻止 JavaScript 直接读取 Cookie但需要额外处理 CSRF、防跨站请求和 Cookie 安全属性。如果使用 Cookie通常应该合理配置HttpOnly Secure SameSite没有一种方案适合所有项目需要结合前端架构、跨域方式和安全要求进行选择。移动端应用移动端应使用系统提供的安全存储能力不建议把 Token 直接保存在普通配置文件或明文数据库中。十五、生产环境中的安全建议1. Access Token 不要设置得过长Access Token 有效期越长泄露后的风险持续时间越长。可以使用短期 Access Token 长期 Refresh Token在用户体验和安全性之间取得平衡。2. Refresh Token 应支持撤销Refresh Token 的有效期较长一旦泄露攻击者可能不断申请新的 Access Token。生产环境中可以在数据库或 Redis 中保存 Refresh Token 的状态并记录Token 唯一编号所属用户登录设备签发时间过期时间是否已经撤销。3. 使用 HTTPS如果使用明文 HTTPToken 可能在传输过程中被截获。生产环境必须使用 HTTPSWebSocket 则应该使用wss://。4. 不要在日志中记录完整 Token排查问题时可以记录 Token 的jti、用户 ID或部分摘要但不应该输出完整 Token。5. 为登录接口增加限流攻击者可能持续尝试不同密码因此登录接口应该增加IP 限流账号维度限流连续失败次数限制验证码或其他人机验证异常登录告警。6. 修改密码后撤销旧 Token如果用户修改密码但旧 Token 仍然可以继续使用那么已经泄露的 Token 不会自动失效。可以通过 Token 版本号、黑名单或会话记录实现统一撤销。十六、JWT 常见误区误区一JWT 中的数据是加密的JWT Payload 通常只是编码任何获得 Token 的人都可以解析其中内容。误区二使用 JWT 就完全不需要服务器状态如果需要退出登录、设备管理、Token 撤销和风险控制服务器仍然可能需要保存部分状态。误区三Refresh Token 可以访问业务接口Refresh Token 只能用于刷新身份凭证。业务接口必须检查 Token 类型只接受 Access Token。误区四只要签名正确就代表用户可以访问所有数据签名正确只能说明 Token 是服务器签发的。具体资源是否属于当前用户仍然需要在业务层进行权限校验。误区五JWT 可以替代所有权限系统JWT 负责传递身份信息但角色权限、资源权限、数据归属和操作范围仍然需要单独设计。十七、总结使用 FastAPI 实现 JWT 登录认证核心流程包括对用户密码进行安全哈希登录成功后生成 Access Token 和 Refresh Token客户端通过 Bearer Token 访问接口服务器验证签名、类型和过期时间Access Token 过期后使用 Refresh Token 更新对重要接口进行资源归属和权限检查通过黑名单或 Token 版本实现主动撤销。JWT 能够让前后端分离项目更方便地传递身份信息但它并不是“生成一个字符串”这么简单。真正可靠的认证系统还需要综合考虑密钥管理、Token 存储、退出登录、设备管理、越权访问、接口限流和安全审计。身份认证解决的是“你是谁”权限校验解决的是“你能做什么”。只有同时做好这两部分才能真正保护用户数据和系统接口。

相关新闻

VLA 已经能输出动作,为什么机器人仍需要多时间尺度闭环

VLA 已经能输出动作,为什么机器人仍需要多时间尺度闭环

VLA 已经能输出动作,为什么机器人仍需要多时间尺度闭环 TL;DR 场景:端到端 VLA 可以共享视觉、语言与动作表示、减少人工设计的中间接口,但不能取消传感器时钟、机械动力学、关节限位、网络故障、控制稳定性和事故责任。结论:可…

2026/10/14 3:46:45 阅读更多 →
HarmonyOS应用开发实战:猫猫大作战-PushKit 的接收与处理

HarmonyOS应用开发实战:猫猫大作战-PushKit 的接收与处理

前言 PushKit 是 HarmonyOS 的远程推送服务,支持服务端向用户设备发送推送消息。对于游戏应用,远程推送可用于活动通知(“周末双倍积分!”)、好友邀请(“你的好友超过了你的得分!”&#xff09…

2026/10/11 8:12:09 阅读更多 →
为什么90%的AI路径系统在雨天/晚高峰崩盘?资深LBS架构师首度公开时空约束建模的6个隐性变量

为什么90%的AI路径系统在雨天/晚高峰崩盘?资深LBS架构师首度公开时空约束建模的6个隐性变量

更多请点击: https://codechina.net 第一章:AI 配送路线优化 在城市物流日益复杂的背景下,AI 驱动的配送路线优化正成为提升时效性与降低碳排放的关键技术。传统基于规则或简单启发式算法的路径规划难以应对实时交通变化、动态订单涌入及多约…

2026/10/5 18:36:33 阅读更多 →

最新新闻

SQLite+FTS5+BM25实现本地AI上下文检索增强(RAG)

SQLite+FTS5+BM25实现本地AI上下文检索增强(RAG)

1. 项目概述:从“context-mode”这个词开始,我们到底在谈什么?“context-mode”这个词乍一看像某个新出的编程模式、IDE插件开关,或是某款AI工具里的隐藏功能标签——但翻遍主流开发文档、VS Code官方设置、JetBrains全家桶配置项…

2026/10/14 9:07:49 阅读更多 →
Qt5 + python-docx 零依赖生成Word文档实战指南

Qt5 + python-docx 零依赖生成Word文档实战指南

简介:本资源是一套面向Qt与Python混合编程初学者及中级开发者的实战项目,聚焦于不依赖Microsoft Office环境下的Word文档读写能力构建。通过Qt5 GUI界面调用python-docx库,实现文档创建、段落编辑、表格插入、图片嵌入及样式控制等核心功能&a…

2026/10/14 9:07:49 阅读更多 →
限流降级实战指南:从算法选型到系统稳定性治理的完整路径

限流降级实战指南:从算法选型到系统稳定性治理的完整路径

1. 从一次线上事故说起:限流降级到底在防什么做后端的人,几乎都经历过那种半夜三点被报警电话叫醒的时刻。上个月我就刚处理过一次:某个核心接口的RT从正常的50ms一路飙到3000ms,紧接着依赖它的上游服务开始超时堆积,然…

2026/10/14 9:07:49 阅读更多 →
一套ERP从需求调研到正式上线的交付流程拆解

一套ERP从需求调研到正式上线的交付流程拆解

杭州管家婆软件(纵横软件杭州杭纵)——本地 28 年企业数字化服务伙伴产品矩阵:管家婆云辉煌系列|财贸系列|分销系列|辉煌小工单(与管家婆同厂出品)|生态圈产品&#xff5…

2026/10/14 9:07:49 阅读更多 →
跨境商业咨询服务的评估体系与技术创新应用

跨境商业咨询服务的评估体系与技术创新应用

1. 行业背景与榜单价值跨境商业咨询服务行业正在经历结构性变革。过去三年间,全球电商渗透率从14%跃升至22%,品牌出海服务市场规模年均复合增长率达到27.3%。这个背景下,专业咨询机构的价值链已经从单纯的战略规划,延伸到本地化运…

2026/10/14 9:07:49 阅读更多 →
SpringBoot体育馆预约系统设计与性能优化实践

SpringBoot体育馆预约系统设计与性能优化实践

1. 项目背景与核心价值体育馆场地预约系统是体育场馆数字化管理的核心工具。过去三年我参与过7个同类系统的开发实施,发现传统电话预约和纸质登记方式存在三大痛点:预约冲突频发(平均每周3-5起)、场地利用率不足(高峰时…

2026/10/14 9:06:48 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →