使用 Python FastAPI 与 Tortoise ORM 实现账号密码登录和手机验证码登录
1. 项目概述与准备工作本文将详细介绍如何使用 Python 的 FastAPI 框架结合 Tortoise ORM 实现两种常见的用户登录方式传统的账号密码登录和现代化的手机验证码登录。我们将从环境搭建、数据库设计、核心逻辑实现到完整 API 测试一步步构建一个安全、可扩展的认证系统。1.1 技术栈说明FastAPI: 现代、高性能的 Python Web 框架用于构建 API。Tortoise ORM: 异步友好的 Python ORM支持 SQLite、PostgreSQL、MySQL 等数据库。Pydantic: 用于数据验证和设置管理。JWT (JSON Web Tokens): 用于生成和验证访问令牌。Redis(可选): 用于缓存验证码和会话信息提升性能。1.2 环境准备首先创建项目目录并安装必要的依赖mkdir fastapi-auth-demo cd fastapi-auth-demo python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install fastapi uvicorn tortoise-orm pydantic python-jose[cryptography] passlib[bcrypt] python-multipart redis2. 数据库模型设计使用 Tortoise ORM 定义用户表同时支持密码和手机号两种认证方式。2.1 用户模型 (models.py)from tortoise import fields, models from tortoise.contrib.pydantic import pydantic_model_creator class User(models.Model): 用户模型 id fields.IntField(pkTrue) username fields.CharField(max_length50, uniqueTrue, nullTrue) email fields.CharField(max_length100, uniqueTrue, nullTrue) phone fields.CharField(max_length20, uniqueTrue, nullTrue) # 密码相关字段 hashed_password fields.CharField(max_length255, nullTrue) 用户信息 full_name fields.CharField(max_length100, nullTrue) is_active fields.BooleanField(defaultTrue) created_at fields.DatetimeField(auto_now_addTrue) updated_at fields.DatetimeField(auto_nowTrue) class Meta: table users def str(self): return fUser(id{self.id}, username{self.username}) 创建 Pydantic 模型用于序列化/验证 User_Pydantic pydantic_model_creator(User, nameUser) UserIn_Pydantic pydantic_model_creator(User, nameUserIn, exclude_readonlyTrue)2.2 验证码记录模型class VerificationCode(models.Model): 手机验证码记录 id fields.IntField(pkTrue) phone fields.CharField(max_length20, indexTrue) code fields.CharField(max_length6) created_at fields.DatetimeField(auto_now_addTrue) expires_at fields.DatetimeField() is_used fields.BooleanField(defaultFalse) class Meta: table verification_codes def str(self): return fVerificationCode(phone{self.phone}, code{self.code})/code/pre 3. 核心配置与工具函数 3.1 配置文件 (config.py) from pydantic import BaseSettings class Settings(BaseSettings): 应用配置 app_name: str FastAPI Auth Demo debug: bool True JWT 配置 secret_key: str your-secret-key-change-in-production algorithm: str HS256 access_token_expire_minutes: int 30 数据库配置 database_url: str sqlite://db.sqlite3 Redis 配置 (用于缓存验证码) redis_url: str redis://localhost:6379/0 短信服务配置 (示例) sms_api_key: str your-sms-api-key class Config: env_file .env settings Settings() 3.2 密码哈希工具 (auth.py) from passlib.context import CryptContext 密码哈希上下文 pwd_context CryptContext(schemes[bcrypt], deprecatedauto) def verify_password(plain_password: str, hashed_password: str) - bool: 验证密码 return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password: str) - str: 生成密码哈希 return pwd_context.hash(password) 3.3 JWT 工具函数 from datetime import datetime, timedelta from typing import Optional from jose import JWTError, jwt from config import settings def create_access_token(data: dict, expires_delta: Optional[timedelta] None): 创建 JWT 访问令牌 to_encode data.copy() if expires_delta: expire datetime.utcnow() expires_delta else: expire datetime.utcnow() timedelta(minutessettings.access_token_expire_minutes) to_encode.update({exp: expire}) encoded_jwt jwt.encode(to_encode, settings.secret_key, algorithmsettings.algorithm) return encoded_jwt def verify_token(token: str): 验证 JWT 令牌 try: payload jwt.decode(token, settings.secret_key, algorithms[settings.algorithm]) return payload except JWTError: return None 4. 账号密码登录实现 4.1 请求/响应模型 from pydantic import BaseModel, EmailStr from typing import Optional class PasswordLoginRequest(BaseModel): 密码登录请求 username: Optional[str] None email: Optional[EmailStr] None phone: Optional[str] None password: str class Config: schema_extra { example: { username: john_doe, password: securepassword123 } } class TokenResponse(BaseModel): 令牌响应 access_token: str token_type: str bearer user_id: int username: Optional[str] None 4.2 密码登录 API from fastapi import APIRouter, Depends, HTTPException, status from tortoise.exceptions import DoesNotExist from models import User from schemas import PasswordLoginRequest, TokenResponse from auth import verify_password, create_access_token router APIRouter(prefix/auth, tags[authentication]) router.post(/login/password, response_modelTokenResponse) async def password_login(login_data: PasswordLoginRequest): 账号密码登录 支持用户名、邮箱或手机号登录 user None 根据提供的标识查找用户 if login_data.username: try: user await User.get(usernamelogin_data.username) except DoesNotExist: pass elif login_data.email: try: user await User.get(emaillogin_data.email) except DoesNotExist: pass elif login_data.phone: try: user await User.get(phonelogin_data.phone) except DoesNotExist: pass 用户不存在或密码错误 if not user or not user.hashed_password: raise HTTPException( status_codestatus.HTTP_401_UNAUTHORIZED, detail用户名或密码错误 ) 验证密码 if not verify_password(login_data.password, user.hashed_password): raise HTTPException( status_codestatus.HTTP_401_UNAUTHORIZED, detail用户名或密码错误 ) 检查用户是否激活 if not user.is_active: raise HTTPException( status_codestatus.HTTP_403_FORBIDDEN, detail用户账户已被禁用 ) 创建访问令牌 access_token create_access_token( data{sub: str(user.id), username: user.username} ) return TokenResponse( access_tokenaccess_token, user_iduser.id, usernameuser.username )/code/pre 5. 手机验证码登录实现 5.1 验证码发送与验证 import random import redis from datetime import datetime, timedelta from fastapi import HTTPException, status from models import VerificationCode from config import settings 初始化 Redis 客户端 redis_client redis.from_url(settings.redis_url, decode_responsesTrue) async def generate_verification_code(phone: str) - str: 生成并存储验证码 生成6位数字验证码 code str(random.randint(100000, 999999)) 存储到数据库 expires_at datetime.utcnow() timedelta(minutes5) await VerificationCode.create( phonephone, codecode, expires_atexpires_at ) 同时缓存到 Redis (可选提升性能) redis_key fverify_code:{phone} redis_client.setex(redis_key, 300, code) # 5分钟过期 这里应该调用短信服务发送验证码 await send_sms(phone, f您的验证码是{code}5分钟内有效) return code async def verify_phone_code(phone: str, code: str) - bool: 验证手机验证码 先检查 Redis 缓存 redis_key fverify_code:{phone} cached_code redis_client.get(redis_key) if cached_code and cached_code code: # 验证成功清除缓存 redis_client.delete(redis_key) # 标记数据库中的验证码为已使用 await VerificationCode.filter(phonephone, codecode, is_usedFalse).update(is_usedTrue) return True 如果缓存没有检查数据库 now datetime.utcnow() verification await VerificationCode.filter( phonephone, codecode, is_usedFalse, expires_at__gtnow ).first() if verification: # 验证成功 verification.is_used True await verification.save() # 清除可能的缓存 redis_client.delete(redis_key) return True return False/code/pre 5.2 手机登录 API from pydantic import BaseModel from fastapi import APIRouter, HTTPException, status class PhoneLoginRequest(BaseModel): 手机登录请求 phone: str code: str class SendCodeRequest(BaseModel): 发送验证码请求 phone: str router.post(/login/phone/send-code) async def send_verification_code(request: SendCodeRequest): 发送手机验证码 验证手机号格式 (这里简单示例实际需要更严格的验证) if not request.phone.startswith(1) or len(request.phone) ! 11: raise HTTPException( status_codestatus.HTTP_400_BAD_REQUEST, detail手机号格式不正确 ) 检查用户是否存在如果不存在则创建 user, created await User.get_or_create( phonerequest.phone, defaults{ username: fuser_{request.phone}, is_active: True } ) 生成并发送验证码 code await generate_verification_code(request.phone) 在实际项目中这里应该调用短信服务 为了演示我们返回验证码生产环境不要这样做 return { message: 验证码已发送, phone: request.phone, code: code, # 仅用于演示生产环境移除 user_created: created } router.post(/login/phone, response_modelTokenResponse) async def phone_login(login_data: PhoneLoginRequest): 手机验证码登录 验证验证码 if not await verify_phone_code(login_data.phone, login_data.code): raise HTTPException( status_codestatus.HTTP_401_UNAUTHORIZED, detail验证码错误或已过期 ) 获取或创建用户 user await User.get_or_none(phonelogin_data.phone) if not user: # 如果用户不存在创建新用户 user await User.create( phonelogin_data.phone, usernamefuser_{login_data.phone}, is_activeTrue ) 检查用户是否激活 if not user.is_active: raise HTTPException( status_codestatus.HTTP_403_FORBIDDEN, detail用户账户已被禁用 ) 创建访问令牌 access_token create_access_token( data{sub: str(user.id), phone: user.phone} ) return TokenResponse( access_tokenaccess_token, user_iduser.id, usernameuser.username )/code/pre 6. 用户注册与数据库初始化 6.1 用户注册 API from pydantic import BaseModel, EmailStr from fastapi import HTTPException, status from auth import get_password_hash class UserRegisterRequest(BaseModel): 用户注册请求 username: str email: EmailStr phone: str password: str full_name: Optional[str] None router.post(/register, status_codestatus.HTTP_201_CREATED) async def register_user(user_data: UserRegisterRequest): 用户注册 检查用户名是否已存在 if await User.exists(usernameuser_data.username): raise HTTPException( status_codestatus.HTTP_400_BAD_REQUEST, detail用户名已存在 ) 检查邮箱是否已存在 if await User.exists(emailuser_data.email): raise HTTPException( status_codestatus.HTTP_400_BAD_REQUEST, detail邮箱已存在 ) 检查手机号是否已存在 if await User.exists(phoneuser_data.phone): raise HTTPException( status_codestatus.HTTP_400_BAD_REQUEST, detail手机号已存在 ) 创建用户 hashed_password get_password_hash(user_data.password) user await User.create( usernameuser_data.username, emailuser_data.email, phoneuser_data.phone, hashed_passwordhashed_password, full_nameuser_data.full_name, is_activeTrue ) return { message: 用户注册成功, user_id: user.id, username: user.username }/code/pre 6.2 数据库初始化 from tortoise import Tortoise from config import settings async def init_db(): 初始化数据库 await Tortoise.init( db_urlsettings.database_url, modules{models: [models]} ) await Tortoise.generate_schemas() print(数据库初始化完成) async def close_db(): 关闭数据库连接 await Tortoise.close_connections() 7. 主应用与路由集成 7.1 主应用文件 (main.py) from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager from routers import auth_router from database import init_db, close_db asynccontextmanager async def lifespan(app: FastAPI): 应用生命周期管理 启动时初始化数据库 await init_db() yield 关闭时清理资源 await close_db() app FastAPI( titleFastAPI 认证系统, description使用 Tortoise ORM 实现账号密码和手机验证码登录, version1.0.0, lifespanlifespan ) 配置 CORS app.add_middleware( CORSMiddleware, allow_origins[], # 生产环境应限制具体域名 allow_credentialsTrue, allow_methods[], allow_headers[*], ) 注册路由 app.include_router(auth_router) app.get(/) async def root(): return {message: FastAPI 认证系统已启动} if name main: import uvicorn uvicorn.run(app, host0.0.0.0, port8000) 7.2 项目结构 以下是完整的项目目录结构 fastapi-auth-demo/ ├── main.py # 主应用入口 ├── config.py # 配置文件 ├── models.py # 数据库模型 ├── schemas.py # Pydantic 模型 ├── auth.py # 认证工具函数 ├── routers.py # API 路由 ├── database.py # 数据库初始化 ├── requirements.txt # 依赖文件 └── .env # 环境变量文件 项目结构说明 main.pyFastAPI 应用主入口包含应用配置、中间件和路由注册 config.py应用配置文件使用 Pydantic 管理环境变量 models.py数据库模型定义包含用户和验证码模型 schemas.pyPydantic 模型定义用于请求/响应数据验证 auth.py认证相关工具函数包括密码哈希和 JWT 处理 routers.pyAPI 路由定义包含登录、注册等接口 database.py数据库初始化和连接管理 requirements.txt项目依赖包列表 .env环境变量配置文件

相关新闻

C++ <functional>深度解析:从函数对象到现代函数式编程实践

C++ <functional>深度解析:从函数对象到现代函数式编程实践

1. 项目概述&#xff1a;为什么我们需要深入理解<functional>如果你写过一段时间的 C&#xff0c;尤其是接触过标准库算法&#xff08;比如std::sort,std::for_each&#xff09;或者多线程&#xff08;比如std::thread&#xff09;&#xff0c;那你大概率已经和<funct…

2026/7/23 4:07:48 阅读更多 →
近期量化工具推荐,AI检查要跟着核心问题走

近期量化工具推荐,AI检查要跟着核心问题走

工具推荐看起来像是在比较工具&#xff0c;实际更应该先比较问题。对于想把手工交易规则转成量化表达的读者来说&#xff0c;真正需要先回答的是&#xff1a;现在最需要被解决的卡点是什么。只有这个问题清楚了&#xff0c;工具才有选择依据。工具要跟着当前任务走如果读者只是…

2026/7/23 4:07:48 阅读更多 →
口碑好的氮化硅陶瓷供应商

口碑好的氮化硅陶瓷供应商

最近后台收到不少做新能源、半导体、高端装备的朋友提问&#xff1a;找氮化硅陶瓷供应商总踩雷&#xff0c;要么参数不达标用俩月就磨坏&#xff0c;要么非标定制拖三个月交不了货&#xff0c;到底哪些供应商口碑靠谱&#xff1f; 其实选氮化硅供应商不能只看报价&#xff0c;核…

2026/7/23 4:06:48 阅读更多 →

最新新闻

我用码道10分钟写了个终端2048游戏,能玩!

我用码道10分钟写了个终端2048游戏,能玩!

我用码道10分钟写了个终端2048游戏&#xff0c;能玩&#xff01;> 本文记录了使用华为云码道&#xff08;CodeArts&#xff09;AI编程助手&#xff0c;从零开发一个终端2048游戏的完整过程。所有代码均由码道自动生成并验证通过。## 为什么是2048&#xff1f;2014年&#xf…

2026/7/23 4:45:02 阅读更多 →
红黑树(Red-Black Tree)深度笔记 —— 从插入原理到代码调优

红黑树(Red-Black Tree)深度笔记 —— 从插入原理到代码调优

namespace Jianyi 本文用于系统复盘红黑树插入的实现&#xff0c;覆盖动机、原理、代码、易错点、复杂度与面试延伸。附带对自己实现代码的 Debug 记录&#xff08;真实踩坑&#xff0c;不是编的&#xff09;。 目录 1. 为什么会有红黑树&#xff1f;&#xff08;解决什么问题&…

2026/7/23 4:45:02 阅读更多 →
OpenCV直方图均衡化与比较:图像增强与相似度评估实战

OpenCV直方图均衡化与比较:图像增强与相似度评估实战

1. 项目概述&#xff1a;直方图均衡化与比较的核心价值在数字图像处理的实际项目中&#xff0c;我们常常会遇到图像对比度不足、细节模糊&#xff0c;或者需要量化评估两幅图像相似度的问题。比如&#xff0c;在监控视频分析中&#xff0c;由于光照变化&#xff0c;夜间拍摄的画…

2026/7/23 4:45:02 阅读更多 →
如何保证 Redis 分布式锁的高可用和高性能?

如何保证 Redis 分布式锁的高可用和高性能?

做后端开发时&#xff0c;分布式锁是一个绕不开的话题。比如&#xff1a;秒杀时&#xff0c;同一个商品库存不能被多个请求同时扣成负数定时任务部署了多台机器&#xff0c;但同一时刻只能有一台执行用户重复点击支付按钮&#xff0c;不能生成多笔订单多个服务同时更新同一份缓…

2026/7/23 4:45:02 阅读更多 →
Tiva TM4C123x ROM API实战:CRC、Flash与FPU模块深度解析与应用

Tiva TM4C123x ROM API实战:CRC、Flash与FPU模块深度解析与应用

1. 项目概述在嵌入式开发领域&#xff0c;尤其是基于ARM Cortex-M内核的微控制器项目里&#xff0c;我们常常需要处理数据校验、非易失性存储和浮点运算这些基础但至关重要的任务。对于使用TI Tiva TM4C123x系列MCU的开发者来说&#xff0c;芯片内部ROM固件库中集成的硬件驱动A…

2026/7/23 4:45:01 阅读更多 →
虚幻引擎Pak文件解析:从原理到实战,掌握资源提取与逆向分析

虚幻引擎Pak文件解析:从原理到实战,掌握资源提取与逆向分析

1. 项目概述&#xff1a;为什么我们需要一个Pak文件解析工具&#xff1f;如果你在虚幻引擎项目开发或逆向分析中打过交道&#xff0c;那么对.pak文件一定不会陌生。这个后缀的文件&#xff0c;是虚幻引擎用于打包游戏资源——包括模型、贴图、音频、蓝图、关卡数据等所有内容—…

2026/7/23 4:44:01 阅读更多 →

日新闻

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;从单点好评到指数级传播&#xff1a;AI副业主理人必须掌握的4层口碑渗透模型&#xff08;含ROI测算表&#xff09; 当AI副业主理人不再仅满足于单次服务交付&#xff0c;而是主动构建可复用、可裂变、可…

2026/7/23 0:00:25 阅读更多 →
AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;AI写作开头钩子设计&#xff1a;为什么你的AI文案完读率不足18%&#xff1f;——基于2,346篇A/B测试报告的归因分析 在对2,346篇跨行业AI生成文案的A/B测试数据进行聚类分析后&#xff0c;我们发现&#xff1…

2026/7/23 0:01:26 阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南&#xff1a;免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:01:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中&#xff0c;我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源&#xff0c;还是配置文件、证书等&#xff0c;都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下&#xff0c;但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP&#xff08;轻量级目录访问协议&#xff09;作为企业级身份认证的黄金标准&#xff0c;已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时&#xff0c;发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”&#xff0c;而是以可解释、可审计、可迭代的方式&#xff0c;赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 12:54:44 阅读更多 →

月新闻