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.pswp.cn/web/67458.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67458.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67458.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

微服務入門(go)

微服務入門(go) 和單體服務對比:里面的服務僅僅用于某個特定的業務 一、領域驅動設計(DDD) 基本概念 領域和子域 領域:有范圍的界限(邊界) 子域:劃分的小范圍 核心域…

深入解析 Linux 內核內存管理核心:mm/memory.c

在 Linux 內核的眾多組件中,內存管理模塊是系統性能和穩定性的關鍵。mm/memory.c 文件作為內存管理的核心實現,承載著頁面故障處理、頁面表管理、內存區域映射與取消映射等重要功能。本文將深入探討 mm/memory.c 的設計思想、關鍵機制以及其在內核中的作用,幫助讀者更好地理…

安卓通過網絡獲取位置的方法

一 方法介紹 1. 基本權限設置 首先需要在 AndroidManifest.xml 中添加必要權限&#xff1a; xml <uses-permission android:name"android.permission.INTERNET" /> <uses-permission android:name"android.permission.ACCESS_NETWORK_STATE" /&g…

【B站保姆級視頻教程:Jetson配置YOLOv11環境(二)SSH連接的三種方式】

B站同步視頻教程&#xff1a;https://www.bilibili.com/video/BV1m5wUeyEQD/ 在Jetson設備上配置YOLOv11環境時&#xff0c;SSH連接是實現遠程高效開發與管理的關鍵一環。不同的網絡環境和硬件配置可能會影響SSH連接的方式&#xff0c;本文將結合相關視頻內容&#xff0c;詳細…

視頻拼接,拼接時長版本

目錄 視頻較長&#xff0c;分辨率較大&#xff0c;這個效果很好&#xff0c;不耗用內存 ffmpeg imageio&#xff0c;適合視頻較短 視頻較長&#xff0c;分辨率較大&#xff0c;這個效果很好&#xff0c;不耗用內存 ffmpeg import subprocess import glob import os from nats…

Vue.js 什么是 Composition API?

Vue.js 什么是 Composition API&#xff1f; 今天我們來聊聊 Vue 3 引入的一個重要特性&#xff1a;組合式 API&#xff08;Composition API&#xff09;。如果你曾在開發復雜的 Vue 組件時感到代碼難以維護&#xff0c;那么組合式 API 可能正是你需要的工具。 什么是組合式 …

Selenium配合Cookies實現網頁免登錄

文章目錄 前言1 方案一&#xff1a;使用Chrome用戶數據目錄2 方案二&#xff1a;手動獲取并保存Cookies&#xff0c;后續使用保存的Cookies3 注意事項 前言 在進行使用Selenium進行爬蟲、網頁自動化操作時&#xff0c;登錄往往是一個必須解決的問題&#xff0c;但是Selenium每次…

計算機畢業設計Python+知識圖譜大模型AI醫療問答系統 健康膳食推薦系統 食譜推薦系統 醫療大數據 機器學習 深度學習 人工智能 爬蟲 大數據畢業設計

溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的學長聯系方式的名片&#xff01; 溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的學長聯系方式的名片&#xff01; 溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的學長聯系方式的名片&#xff01; 作者簡介&#xff1a;Java領…

關于el-table翻頁后序號列遞增的組件封裝

需求說明&#xff1a; 項目中經常會用到的一個場景&#xff0c;表格第一列顯示序號&#xff08;1、2、3...&#xff09;&#xff0c;但是在翻頁后要遞增顯示序號&#xff0c;例如10、11、12&#xff08;假設一頁顯示10條數據&#xff09;&#xff0c;針對這種情況&#xff0c;封…

Elasticsearch的索引生命周期管理

目錄 說明零、參考一、ILM的基本概念二、ILM的實踐步驟Elasticsearch ILM策略中的“最小年齡”是如何計算的&#xff1f;如何監控和調整Elasticsearch ILM策略的性能&#xff1f; 1. **監控性能**使用/_cat/thread_pool API基本請求格式請求特定線程池的信息響應內容 2. **調整…

AI大模型開發原理篇-3:詞向量和詞嵌入

簡介 詞向量是用于表示單詞意義的向量&#xff0c; 并且還可以被認為是單詞的特征向量或表示。 將單詞映射到實向量的技術稱為詞嵌入。在實際應用中&#xff0c;詞向量和詞嵌入這兩個重要的NLP術語通常可以互換使用。它們都表示將詞匯表中的單詞映射到固定大小的連續向量空間中…

[內網安全] 內網滲透 - 學習手冊

這是一篇專欄的目錄文檔&#xff0c;方便讀者系統性的學習&#xff0c;筆者后續會持續更新文檔內容。 如果沒有特殊情況的話&#xff0c;大概是一天兩篇的速度。&#xff08;實驗多或者節假日&#xff0c;可能會放緩&#xff09; 筆者也是一邊學習一邊記錄筆記&#xff0c;如果…

【學術會議征稿-第二屆生成式人工智能與信息安全學術會議(GAIIS 2025)】人工智能與信息安全的魅力

重要信息 時間&#xff1a;2025年2月21日-23日 地點&#xff1a;中國杭州 官網&#xff1a;http://www.ic-gaiis.org 簡介 2025年第二屆生成式人工智能與信息安全將于 2025年2月21日-23日在中國杭州舉行。主要圍繞“生成式人工智能與信息安全”的最新研究展開&#xff0c;…

Vscode的AI插件 —— Cline

簡介 vscode的一款AI輔助吃插件&#xff0c;主要用來輔助創建和編輯文件&#xff0c;探索大型項目&#xff0c;使用瀏覽器并執行終端命令&#xff08;需要多個tokens&#xff09;&#xff0c;可以使用模型上下文協議&#xff08;MCP&#xff09;來創建新工具并擴展自己(比較慢…

2024 CVPR Highlight Learning-Feedback

圖像增強 Towards Robust Event-guided Low-Light Image Enhancement: A Large-Scale Real-World Event-Image Dataset and Novel Approach 解決的主要問題是低光照條件下的圖像增強 通過多尺度整體融合分支提取事件和圖像的結構和紋理信息&#xff0c;并引入信噪比&#xff0…

小白一命速通JS中的windowglobal對象

筆者注意到JS中的window對象與global對象經常被混淆&#xff0c;盡管它們在相當一部分使用情況下可以等同&#xff0c;但是本質上仍然存在很多不同&#xff0c;下面是對于兩者的詳細拆解 1. window 對象 定義&#xff1a;window 對象表示 瀏覽器環境中的全局上下文。作用域&am…

機器學習2 (筆記)(樸素貝葉斯,集成學習,KNN和matlab運用)

樸素貝葉斯模型 貝葉斯定理&#xff1a; 常見類型 算法流程 優缺點 集成學習算法 基本原理 常見方法 KNN&#xff08;聚類模型&#xff09; 算法性質&#xff1a; 核心原理&#xff1a; 算法流程 優缺點 matlab中的運用 樸素貝葉斯模型 樸素貝葉斯模型是基于貝葉斯…

HTB:Active[RE-WriteUP]

目錄 連接至HTB服務器并啟動靶機 信息收集 使用rustscan對靶機TCP端口進行開放掃描 將靶機TCP開放端口號提取并保存 使用nmap對靶機TCP開放端口進行腳本、服務掃描 使用nmap對靶機TCP開放端口進行漏洞、系統掃描 使用nmap對靶機常用UDP端口進行開放掃描 使用nmap對靶機…

Git圖形化工具【lazygit】

簡要介紹一下偶然發現的Git圖形化工具——「lazygit」 概述 Lazygit 是一個用 Go 語言編寫的 Git 命令行界面&#xff08;TUI&#xff09;工具&#xff0c;它讓 Git 操作變得更加直觀和高效。 Github地址&#xff1a;https://github.com/jesseduffield/lazygit 主要特點 主要…

58.界面參數傳遞給Command C#例子 WPF例子

界面參數的傳遞&#xff0c;界面參數是如何從前臺傳送到后臺的。 param 參數是從界面傳遞到命令的。這個過程通常涉及以下幾個步驟&#xff1a; 數據綁定&#xff1a;界面元素&#xff08;如按鈕&#xff09;的 Command 屬性綁定到視圖模型中的 RelayCommand 實例。同時&#x…