FastAPI + GraphQL + SQLAlchemy 实现博客系统

本文将详细介绍如何使用 FastAPI、GraphQL(Strawberry)和 SQLAlchemy 实现一个带有认证功能的博客系统。
在这里插入图片描述

技术栈

  • FastAPI:高性能的 Python Web 框架
  • Strawberry:Python GraphQL 库
  • SQLAlchemy:Python ORM 框架
  • JWT:用于用户认证

系统架构

1. 数据模型(Models)

使用 SQLAlchemy 定义数据模型,以用户模型为例:

class UserModel(Base):"""SQLAlchemy model for the users table"""__tablename__ = "users"id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)username: Mapped[str] = mapped_column(String(50), unique=True, index=True)email: Mapped[str] = mapped_column(String(100), unique=True, index=True)hashed_password: Mapped[str] = mapped_column(String(200))nickname: Mapped[str] = mapped_column(String(50), nullable=True)is_active: Mapped[bool] = mapped_column(Boolean, default=True)is_admin: Mapped[bool] = mapped_column(Boolean, default=False)created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)# 关联关系posts = relationship("PostModel", back_populates="author")def verify_password(self, password: str) -> bool:"""验证密码"""return pwd_context.verify(password, self.hashed_password)

2. GraphQL Schema

使用 Strawberry 定义 GraphQL schema,包括查询和变更:

from models.types import (UserRead,      # 用户信息读取类型UserCreate,    # 用户创建输入类型LoginInput,    # 登录输入类型LoginResponse, # 登录响应类型RegisterResponse, # 注册响应类型Token,        # Token类型PostRead,     # 文章读取类型PostCreate,   # 文章创建输入类型PageInput,    # 分页输入类型Page,         # 分页响应类型PageInfo      # 分页信息类型
)@strawberry.type
class Query:@strawberry.fielddef hello(self) -> str:"""测试接口"""return "Hello World"@strawberry.fielddef me(self, info) -> Optional[UserRead]:"""获取当前用户信息- 需要认证- 返回 None 表示未登录- 返回 UserRead 类型表示当前登录用户信息"""if not info.context.get("user"):return Nonereturn info.context["user"].to_read()@strawberry.fielddef my_posts(self, info, page_input: Optional[PageInput] = None) -> Page[PostRead]:"""获取当前用户的文章列表- 需要认证- 支持分页查询- 返回带分页信息的文章列表参数:- page_input: 可选的分页参数- page: 页码(默认1)- size: 每页大小(默认10)返回:- items: 文章列表- page_info: 分页信息- total: 总记录数- page: 当前页码- size: 每页大小- has_next: 是否有下一页- has_prev: 是否有上一页"""# 认证检查if not info.context.get("user"):raise ValueError("Not authenticated")# 数据库操作db = SessionLocal()try:# 设置分页参数page = page_input.page if page_input else 1size = page_input.size if page_input else 10# 查询总数total = db.query(func.count(PostModel.id)).filter(PostModel.author_id == info.context["user"].id).scalar()# 查询分页数据posts = (db.query(PostModel).options(joinedload(PostModel.author))  # 预加载作者信息.filter(PostModel.author_id == info.context["user"].id).order_by(PostModel.created_at.desc())  # 按创建时间倒序.offset((page - 1) * size).limit(size).all())# 构建分页信息page_info = PageInfo(total=total,page=page,size=size,has_next=total > page * size,has_prev=page > 1)return Page(items=[post.to_read() for post in posts],page_info=page_info)finally:db.close()@strawberry.fielddef user_posts(self, username: str, page_input: Optional[PageInput] = None) -> Page[PostRead]:"""获取指定用户的文章列表- 公开接口,无需认证- 支持分页查询- 返回带分页信息的文章列表参数:- username: 用户名- page_input: 可选的分页参数"""# ... 实现类似 my_posts@strawberry.type
class Mutation:@strawberry.mutationdef login(self, login_data: LoginInput) -> LoginResponse:"""用户登录- 公开接口,无需认证- 验证用户名密码- 生成访问令牌参数:- login_data:- username: 用户名- password: 密码返回:- token: 访问令牌- user: 用户信息"""db = SessionLocal()try:# 查找用户user = db.query(UserModel).filter(UserModel.username == login_data.username).first()# 验证密码if not user or not user.verify_password(login_data.password):raise ValueError("Incorrect username or password")# 生成访问令牌access_token = create_access_token(data={"sub": str(user.id)})token = Token(access_token=access_token)return LoginResponse(token=token, user=user.to_read())finally:db.close()@strawberry.mutationdef register(self, user_data: UserCreate) -> RegisterResponse:"""用户注册- 公开接口,无需认证- 检查用户名和邮箱是否已存在- 创建新用户- 生成访问令牌参数:- user_data:- username: 用户名- password: 密码- email: 邮箱返回:- token: 访问令牌- user: 用户信息"""# ... 实现代码@strawberry.mutationdef create_post(self, post_data: PostCreate, info) -> PostRead:"""创建文章- 需要认证- 创建新文章- 设置当前用户为作者参数:- post_data:- title: 标题- content: 内容返回:- 创建的文章信息"""# ... 实现代码schema = strawberry.Schema(query=Query, mutation=Mutation)

认证实现

1. JWT Token 生成

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:"""创建访问令牌"""to_encode = data.copy()if expires_delta:expire = datetime.utcnow() + expires_deltaelse:expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)to_encode.update({"exp": expire})encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)return encoded_jwt

2. 认证中间件

在 FastAPI 应用中实现认证中间件,用于解析和验证 token:

async def get_context(request: Request):"""GraphQL 上下文处理器,用于认证"""auth_header = request.headers.get("Authorization")context = {"user": None}if auth_header and auth_header.startswith("Bearer "):token = auth_header.split(" ")[1]token_data = verify_token(token)if token_data:db = SessionLocal()try:user = db.query(UserModel).filter(UserModel.id == int(token_data["sub"])).first()if user:context["user"] = userfinally:db.close()return context

3. 认证流程

  1. 用户登录:
mutation Login {login(loginData: {username: "admin",password: "111111"}) {token {accessToken}user {idusernameemail}}
}
  1. 服务器验证用户名密码,生成 JWT token

  2. 后续请求中使用 token:

    • 在请求头中添加:Authorization: Bearer your_token
    • 中间件解析 token 并验证
    • 将用户信息添加到 GraphQL context
  3. 在需要认证的操作中检查用户:

if not info.context.get("user"):raise ValueError("Not authenticated")

API 权限设计

1. 公开接口(无需认证)

  • hello: 测试接口
  • login: 用户登录
  • register: 用户注册
  • userPosts: 获取指定用户的文章列表

2. 私有接口(需要认证)

  • me: 获取当前用户信息
  • myPosts: 获取当前用户的文章列表
  • createPost: 创建新文章

使用示例

1. 登录获取 Token

mutation Login {login(loginData: {username: "admin",password: "111111"}) {token {accessToken}}
}

2. 使用 Token 访问私有接口

在 GraphQL Playground 中设置 HTTP Headers:

{"Authorization": "Bearer your_token"
}

然后可以查询私有数据:

query MyPosts {myPosts(pageInput: {page: 1,size: 10}) {items {idtitlecontent}}
}

安全考虑

  1. 密码安全

    • 使用 bcrypt 进行密码哈希
    • 从不存储明文密码
  2. Token 安全

    • 使用 JWT 标准
    • 设置合理的过期时间
    • 使用安全的签名算法
  3. 数据访问控制

    • 严格的权限检查
    • 用户只能访问自己的数据

总结

本项目展示了如何使用现代化的技术栈构建一个安全的 GraphQL API:

  1. 使用 FastAPI 提供高性能的 Web 服务
  2. 使用 Strawberry 实现 GraphQL API
  3. 使用 SQLAlchemy 进行数据库操作
  4. 实现了完整的认证机制
  5. 遵循了最佳安全实践

当然图片上传一类的,还要跟以前一样写,但现在我们只写了一个/api接口就完成了项目所有接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/11318.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实验9 JSP访问数据库(二)

实验9 JSP访问数据库(二) 目的: 1、熟悉JDBC的数据库访问模式。 2、掌握预处理语句的使用 实验要求: 1、使用Tomcat作为Web服务器 2、通过JDBC访问数据库,实现增删改查功能的实现 3、要求提交实验报告,将代…

扣子平台音频功能:让声音也能“智能”起来。扣子免费系列教程(14)

在数字化时代,音频内容的重要性不言而喻。无论是在线课程、有声读物,还是各种多媒体应用,音频都是传递信息、增强体验的关键元素。扣子平台的音频功能,为开发者和内容创作者提供了一个强大而灵活的工具,让音频的使用和…

小程序项目-购物-首页与准备

前言 这一节讲一个购物项目 1. 项目介绍与项目文档 我们这里可以打开一个网址 https://applet-base-api-t.itheima.net/docs-uni-shop/index.htm 就可以查看对应的文档 2. 配置uni-app的开发环境 可以先打开这个的官网 https://uniapp.dcloud.net.cn/ 使用这个就可以发布到…

Unity游戏(Assault空对地打击)开发(3) 摄像机的控制

详细步骤 打开My Assets或者Package Manager。 选择Unity Registry。 搜索Cinemachine,找到 Cinemachine包,点击 Install按钮进行安装。 关闭窗口,新建一个FreeLook Camera,如下。 接着新建一个对象Pos,拖到Player下面…

UE编辑器工具

如何自己制作UE小工具提高工作效率 在虚幻编辑器用户界面中,可以使用各种各样的可视化工具来设置项目,设计和构建关卡,创建游戏性交互等等。但有些时候,当你确定了需要编辑器执行的操作后,可能想要通过编程方式调用它…

PVE 中 Debian 虚拟机崩溃后,硬盘数据怎么恢复

问题 在 PVE 中给 Debian 虚拟机新分配硬盘后,通过 Debian 虚拟机开启 Samba 共享该硬盘。如果这个 Debian 虚拟机崩溃后,怎么恢复 Samba 共享硬盘数据。 方法 开启 Samba 共享相关知识:挂载硬盘和开启Samba共享。 新建一个虚拟机&#xf…

微信登录模块封装

文章目录 1.资质申请2.combinations-wx-login-starter1.目录结构2.pom.xml 引入okhttp依赖3.WxLoginProperties.java 属性配置4.WxLoginUtil.java 后端通过 code 获取 access_token的工具类5.WxLoginAutoConfiguration.java 自动配置类6.spring.factories 激活自动配置类 3.com…

Games104——网络游戏的进阶架构

这里写目录标题 前言位移移动插值内插(Interpolation)外插(Extrapolation) 命中判定Hit Registration在客户端去判定 在服务器端去判定延迟补偿掩体问题躲进掩体走出掩体 技能前摇本地暴击效果 基础MMO框架分布式架构一致性哈希服…

SQL Server查询计划操作符(7.3)——查询计划相关操作符(5)

7.3. 查询计划相关操作符 38)Flow Distinct:该操作符扫描其输入并对其去重。该操作符从其输入得到每行数据时即将其返回(除非其为重复数据行,此时,该数据行会被抛弃),而Distinct操作符在产生任何输出前将消费所有输入。该操作符为逻辑操作符。该操作符具体如图7.2-38中…

Jenkins未在第一次登录后设置用户名,第二次登录不进去怎么办?

Jenkins在第一次进行登录的时候,只需要输入Jenkins\secrets\initialAdminPassword中的密码,登录成功后,本次我们没有修改密码,就会导致后面第二次登录,Jenkins需要进行用户名和密码的验证,但是我们根本就没…

Pyecharts之图表样式深度定制

在数据可视化的世界里,图表的样式定制对于提升数据展示效果和用户体验至关重要。Pyecharts 提供了丰富的样式定制功能,能让我们创建出独具特色的可视化作品。本篇将深入探讨如何使用 Pyecharts 为图表添加线性渐变色、径向渐变色,以及如何添加…

DeepSeek Janus-Pro:多模态AI模型的突破与创新

近年来,人工智能领域取得了显著的进展,尤其是在多模态模型(Multimodal Models)方面。多模态模型能够同时处理和理解文本、图像等多种类型的数据,极大地扩展了AI的应用场景。DeepSeek(DeepSeek-V3 深度剖析:…

利用Spring Batch简化企业级批处理应用开发

1. 引言 1.1 批处理的重要性 在现代企业系统中,批处理任务用于处理大量数据,如报表生成、数据迁移、日终结算等。这些任务通常不需要实时响应,但需要高效、可靠地完成。批处理可以显著提高系统性能,减少实时系统的负载,并确保数据的完整性和一致性。 1.2 Spring Batch简…

Linux环境下的Java项目部署技巧:环境安装

安装 JDK: 第上传 jdk 压缩安装包到服务器 将压缩安装包解压缩: tar -xvf jdk-8uXXX-linux-x64.tar.gz 配置环境变量: 编辑 /etc/profile 文件,在文件末尾添加以下内容: export JAVA_HOME/path/to/jdk //JAVA_HOME…

w186格障碍诊断系统spring boot设计与实现

🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…

PhotoShop中JSX编辑器安装

1.使用ExtendScript Tookit CC编辑 1.安装 打开CEP Resource链接: CEP-Resources/ExtendScript-Toolkit at master Adobe-CEP/CEP-Resources (github.com) 将文件clone到本地或者下载到本地 点击AdobeExtendScriptToolKit_4_Ls22.exe安装,根据弹出的…

如何配置Java JDK

步骤1:点击资源,点击Java下载 https://www.oracle.com/ 步骤2:点击java下载、JDK23下载,下载第一行第一个 步骤3:解压到一个空文件夹下,复制lib地址 步骤4:在设置里面搜索“高级系统设置”;点击…

【Qt】Qt老版本解决中文乱码

修改Pro文件 Qt 老版本(Qt5 MSVC2017,其他版本的可以试一试)出现中文乱码,只要字符串中有中文就会乱码: 工具->选项->文本编辑器: 修改Pro文件 以上修改后若还乱码,则在项目中的 .pro 文件添加以…

【AI论文】Sigma:对查询、键和值进行差分缩放,以实现高效语言模型

摘要:我们推出了Sigma,这是一个专为系统领域设计的高效大型语言模型,其独特之处在于采用了包括DiffQKV注意力机制在内的新型架构,并在我们精心收集的系统领域数据上进行了预训练。DiffQKV注意力机制通过根据查询(Q&…

【人工智能】 在本地运行 DeepSeek 模型:Ollama 安装指南

持续更新。。。。。。。。。。。。。。。 【人工智能】 在本地运行 DeepSeek 模型:Ollama 安装指南 安装 Ollama安装 DeepSeek 模型选择版本 ,版本越高,参数越多 性能越好使用 DeepSeek 模型 安装 Ollama 访问 Ollama 官网: 前往 https://oll…