一、概念全部串起来

  1. HTTP 无状态:服务器不记得你是谁,每次请求都是全新的
  2. 登录:前端传账号密码,后端验证正确
  3. JWT:后端根据用户信息加密生成的一串长字符串(Token)
  4. 作用:当作用户的临时身份证
  5. 流程

前端登录 → 后端返回JWT Token 前端每次请求需要权限的接口 → 请求头带上Token 后端拦截验证Token → 有效就放行,无效/过期直接报错401

二、完整全套代码

先安装依赖

pip install pyjwt python-multipart fastapi uvicorn

1、完整 JWT 工具封装(生成 Token、解析 Token、异常处理)

单独写好通用工具,所有接口都能用
 

import jwt
from datetime import datetime, timedelta
from fastapi import Depends, HTTPException, Header

# ===================== JWT 基础配置 =====================
# 密钥(非常重要!!自己随便改一串复杂字符串,绝对不能泄露给别人)
SECRET_KEY = "my_private_secret_key_123456789_abcdef"
# 加密算法
ALGORITHM = "HS256"
# Token过期时间:7天
ACCESS_TOKEN_EXPIRE_DAYS = 7


# ===================== 1. 生成Token(登录成功调用) =====================
def create_token(user_id: int, username: str):
    """
    根据用户id、用户名生成JWT Token
    """
    # 计算过期时间
    expire_time = datetime.utcnow() + timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS)

    # Token里面存储的用户信息(载荷,不要放密码敏感信息)
    payload = {
        "user_id": user_id,
        "username": username,
        "exp": expire_time  # 过期时间字段,jwt自带识别
    }

    # 加密生成token字符串
    token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
    return token


# ===================== 2. 验证Token(所有需要登录的接口调用) =====================
def verify_token(token: str):
    """
    解析token,返回里面的用户信息
    token无效、过期、篡改 全部抛出异常
    """
    try:
        # 解密token
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except jwt.ExpiredSignatureError:
        # Token过期
        raise HTTPException(status_code=401, detail="Token已过期,请重新登录")
    except jwt.PyJWTError:
        # Token无效、伪造、被篡改
        raise HTTPException(status_code=401, detail="Token无效,请重新登录")


# ===================== 3. 依赖项:自动校验Token(接口直接用Depends即可) =====================
async def get_current_user(Authorization: str = Header(None)):
    """
    FastAPI依赖注入,所有需要登录的接口,加这个参数就自动鉴权
    前端请求头格式:Authorization: Bearer 你的token字符串
    """
    # 1. 判断有没有传请求头
    if not Authorization:
        raise HTTPException(status_code=401, detail="未登录,请携带Token")

    # 2. 拆分 Bearer 和 token
    # 格式:Bearer xxxxxxx
    if not Authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Token格式错误")

    # 截取真正的token字符串
    token = Authorization[7:]

    # 3. 验证token,返回用户信息
    user_info = verify_token(token)
    return user_info

2、登录接口(验证账号密码 + 返回 Token)

 user_router 路由

from fastapi import APIRouter
from pydantic import BaseModel

user_router = APIRouter()

# 前端登录提交的参数模型:账号+密码
class Login(BaseModel):
    username: str
    password: str


@user_router.post("/login")
def login(login_data: Login):
    """
    用户登录接口
    1. 校验账号密码
    2. 正确则生成JWT Token返回
    """
    # 这里以后替换成:数据库查询账号密码
    # 模拟数据库用户
    db_user = {
        "user_id": 1,
        "username": "test",
        "password": "123456"
    }

    # 账号密码校验
    if login_data.username == db_user["username"] and login_data.password == db_user["password"]:
        # 生成token
        token = create_token(user_id=db_user["user_id"], username=db_user["username"])

        return {
            "code": 200,
            "msg": "登录成功",
            "token": token
        }
    else:
        return {
            "code": 400,
            "msg": "账号或密码错误"
        }

3、需要登录才能访问的接口(鉴权演示)

只要在参数里加 user = Depends(get_current_user)
接口就自动强制校验登录,没 Token 直接 401 报错
 

@user_router.get("/user/info")
def get_user_info(user = Depends(get_current_user)):
    """
    需要登录才能访问的接口
    自动校验Token,并且从Token里拿到当前登录用户信息
    """
    # user 就是我们从token解析出来的:{"user_id":1,"username":"test"}
    return {
        "code": 200,
        "msg": "获取用户信息成功",
        "data": user
    }

4、文件上传接口,加上登录权限(实战)

上传接口,加上登录校验,必须登录才能上传文件

from fastapi import UploadFile, File
from typing import List
import os

@user_router.post("/file/uploads/")
async def files_upload(
        files: List[UploadFile] = File(..., alias="file"),
        user = Depends(get_current_user)  # 加上这一行,强制登录
):
    files_name_type = []

    for file in files:
        files_name_type.append(file.filename + " " + file.content_type)
        filename = os.path.basename(file.filename)
        with open(filename, 'wb') as f:
            f.write(await file.read())

    return {
        "filename": files_name_type,
        "current_user": user  # 顺便返回当前登录用户
    }

三、完整流程走一遍(Request、Response 全过程)

1、第一步:调用登录接口(POST /login)

Request 请求

{
    "username": "test",
    "password": "123456"
}

Response 后端返回

{
    "code": 200,
    "msg": "登录成功",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InRlc3QiLCJleHAiOjE3NDUyNjQwOTR9.xxxxxxxxxxxx"
}

这一长串字符串就是 JWT Token

2、第二步:访问需要登录的接口(比如获取用户信息、文件上传)

Postman 携带 Token 正确方式(重点!90% 人都错这里)

  • 进入 Headers 请求头
  • 新增一条
    • Key:Authorization
    • Value:Bearer 复制你登录返回的token

      注意空格!Bearer 后面必须有一个空格

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxxxxx

三种错误情况,后端都会直接返回 401 Response

  • 没带 Authorization 请求头

    json

    {"detail": "未登录,请携带Token"}
    
  • 格式不对,少了 Bearer

    json

    {"detail": "Token格式错误"}
    
  • Token 写错、过期、伪造

    json

    {"detail": "Token无效,请重新登录"}

Token 正确时

后端自动解析出用户 user_idusername,接口正常执行,返回数据。

四、JWT 三段结构详细拆解(彻底弄懂这个字符串)

你拿到的 Token 长这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InRlc3QiLCJleHAiOjE3NDUyNjQwOTR9.abcdefghijk

. 分割成 3 段

  1. Header 头部声明加密算法 HS256
  2. Payload 载荷存放用户公开信息:user_id、username、过期时间

    注意:这里是明文可解码,不要存密码、手机号等敏感数据

  3. Signature 签名用密钥加密生成的校验串作用:防止别人篡改第二段内容只要改了 payload 任意字符,签名就失效,后端直接判定 Token 无效。

五、结合之前所有知识点串总结

  1. Request 请求

    • 登录:请求体传账号密码
    • 鉴权:请求头 HeadersAuthorization: Bearer Token
    • 文件上传:form-data 传文件
  2. Response 响应

    • 登录成功:返回token字符串
    • 鉴权成功:正常返回业务数据
    • 鉴权失败:401状态码 + 未登录 / Token 错误提示
  3. 无状态原理后端不需要数据库存 Token,所有用户信息都加密在 Token 本身里,每次请求解码即可,这就是 JWT 最大优点。

六、Postman 完整测试步骤(照着点一次就全会)

  1. 先调用 /user/login,拿到 token
  2. 打开需要鉴权的接口 /user/info
  3. 切换到 Headers
  4. Key 填 Authorization
  5. Value 填 Bearer 粘贴你的token
  6. Send 发送,正常返回用户信息
  7. 你把 token 随便改几个字符再发,就会返回 401 无效
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐