news 2026/6/9 21:16:57

fastapi双token机制登录实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
fastapi双token机制登录实现

fastapi双token机制登录实现

一、整体架构

二、代码实现

from datetime import datetime, timedelta, timezone import uuid from redis import asyncio from fastapi import HTTPException, Depends,FastAPI,Response,Request from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from jose import jwt, JWTError from pydantic import BaseModel from tortoise.contrib.fastapi import register_tortoise from app.config.model_conf.settings import TORTOISE_ORM from app.models.users import Users api_login = FastAPI() class user_login(BaseModel): username: str password: str class user_login_success(BaseModel): token: str token_type: str="Bearer" ACCESS_TOKEN_EXPIRE_MINUTES = 15 REFRESH_TOKEN_EXPIRE_DAYS = 7 SECRET_KEY = "CHANGE_ME_TO_32RANDOM_STRING" ALGORITHM = "HS256" REDIS_URL = "redis://192.168.88.14:6379/0" BLK_PREFIX = "black:jti:" REF_PREFIX = "refresh:" def create_access_token(username: str, jti: str) -> str: expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) return jwt.encode({"username": username, "jti": jti, "exp": expire}, SECRET_KEY, algorithm=ALGORITHM) # redis初始化 async def create_redis(): redis = await asyncio.from_url( REDIS_URL, decode_responses=True, max_connections=100, ) return redis @api_login.post("/login",response_model=user_login_success) async def login(user:user_login,response: Response,redis=Depends(create_redis)): user_info = Users.get(uername=user.username,password=user.password) if not user_info: raise HTTPException(status_code=401,detail="用户名或密码错误") # 生成access_token jti = str(uuid.uuid4()) access_token = create_access_token(user.username,jti) # 生成refresh_token,存储redis和cookie refresh_token = str(uuid.uuid4()) await redis.setex(REF_PREFIX+refresh_token,REFRESH_TOKEN_EXPIRE_DAYS*60*60*24,user.username) response.set_cookie( key="refresh_token", value=refresh_token, max_age=REFRESH_TOKEN_EXPIRE_DAYS*60*60*24, httponly=True, samesite="lax", secure=False, # 生产 True ) return {"token": access_token} # 解码token,验证token是否合法 def decode_token(token: str): try: return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) except JWTError: return None # 获取当前登录用户信息 http_bearer = HTTPBearer(auto_error=True) async def get_current_user( cred: HTTPAuthorizationCredentials = Depends(http_bearer), redis=Depends(create_redis) ): # 解码token username_jti = decode_token(cred.credentials) print(username_jti) if not username_jti: raise HTTPException(status_code=401,detail="token无效") if not username_jti.get("username") or not username_jti.get("jti"): raise HTTPException(status_code=401,detail="token无效") # 验证用户是否已经退出 if await redis.exists(BLK_PREFIX+username_jti.get("jti")): raise HTTPException(status_code=401,detail="token已失效") return username_jti.get("username") # 重新签发access_token和refresh_token @api_login.post("/refresh") async def refresh(request: Request,response: Response,redis=Depends(create_redis),current_user: str = Depends(get_current_user)): # refresh_token存在,access_token失效,重新生成token # 判断并生成refresh_token refresh_token=request.cookies.get("refresh_token") if not refresh_token: raise HTTPException(status_code=401,detail="缺少 refresh_token") if not await redis.exists(REF_PREFIX+refresh_token): raise HTTPException(status_code=401,detail="refresh_token不存在") new_token = str(uuid.uuid4()) await redis.delete(REF_PREFIX+refresh_token) await redis.setex(REF_PREFIX+new_token,REFRESH_TOKEN_EXPIRE_DAYS*60*60*24,current_user) response.set_cookie( key="refresh_token", value=new_token, max_age=REFRESH_TOKEN_EXPIRE_DAYS*60*60*24, httponly=True, samesite="lax", secure=False, ) # 生成access_token new_jti = str(uuid.uuid4()) access_token = create_access_token(current_user,new_jti) return {"access_token":access_token} # 注销,依赖当前登录的用户,拉黑+删除token,redis @api_login.post("/logout") async def logout(request: Request,redis=Depends(create_redis), username_jti: str = Depends(get_current_user), cred: HTTPAuthorizationCredentials = Depends(http_bearer), ): # 退出,请求头获取token,拉黑当前请求的token header_token = decode_token(cred.credentials) await redis.setex(BLK_PREFIX+header_token.get("jti"),ACCESS_TOKEN_EXPIRE_MINUTES*60,1) # 从cookie中获取refresh_token,删除reids的refresh_token refresh_token = request.cookies.get("refresh_token") if refresh_token: await redis.delete(REF_PREFIX+refresh_token) else: raise HTTPException(status_code=401,detail="缺少refresh_token") return {"msg": "注销成功"} class user_register(BaseModel): msg: str @api_login.post("/register",response_model=user_register) async def register(user:user_login): # 1. 用户名重复检查 if await Users.filter(username=user.username).exists(): raise HTTPException(status_code=409, detail="用户已存在") if await Users.create(username=user.username, password=user.password): return {"msg": "注册成功,跳转到登录界面"} return {"msg": "注册失败"} # 数据库参数配置 register_tortoise( api_login, config=TORTOISE_ORM, ) if __name__ == "__main__": import uvicorn uvicorn.run(api_login, host="127.0.0.1", port=8000)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 7:14:33

高性能数据存储实战指南:LevelDB在分布式系统中的深度应用

高性能数据存储实战指南:LevelDB在分布式系统中的深度应用 【免费下载链接】leveldb LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values. 项目地址: https://gitcode.com/GitH…

作者头像 李华
网站建设 2026/6/8 17:26:01

Boot镜像修复神器:Magisk Patcher深度使用指南

Boot镜像修复神器:Magisk Patcher深度使用指南 【免费下载链接】Boot.img修补工具-MagiskPatcher 本仓库提供了一个名为“Boot.img 修补工具 - Magisk Patcher”的资源文件。该工具主要用于修补有锁的BOOT镜像文件,帮助用户在需要的情况下对Boot.img进行…

作者头像 李华
网站建设 2026/6/8 11:58:09

Wan2.2-T2V-A14B如何应对长序列视频生成中的时序断裂问题?

Wan2.2-T2V-A14B如何应对长序列视频生成中的时序断裂问题? 在影视预演的会议室里,导演盯着屏幕皱起眉头:“这个角色前一秒还在雨中跳舞,怎么下一秒就换了身衣服?而且……她是谁?” 这并非演员失误&#xff…

作者头像 李华
网站建设 2026/6/9 17:22:31

1.GPIO

1. GPIO介绍 GPIO 是 “General-Purpose Input/Output” 的缩写,即通用输入 / 输出接口,是嵌入式系统(如单片机、MCU)中最基础的硬件接口之一。 核心特点 通用性:可灵活配置为 “输入” 或 “输出” 模式&#xff0c…

作者头像 李华
网站建设 2026/6/9 17:20:46

GameFramework框架完整解析:Unity游戏开发的终极解决方案

GameFramework框架完整解析:Unity游戏开发的终极解决方案 【免费下载链接】GameFramework This is literally a game framework, based on Unity game engine. It encapsulates commonly used game modules during development, and, to a large degree, standardis…

作者头像 李华
网站建设 2026/6/9 17:21:11

AI视频工具普及,为何内容团队加班更多了?

随着AI视频生成工具的快速普及,一个看似矛盾的现象正在内容行业蔓延:技术本应解放生产力,但许多团队的加班时长却不降反增。据2023年行业白皮书显示,超过60%的受访团队表示,在引入AI工具后,内容生产的“隐性…

作者头像 李华