<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>python</title>
	<atom:link href="https://www.luoyuly.icu/category/python/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.luoyuly.icu</link>
	<description></description>
	<lastBuildDate>Tue, 20 Jan 2026 08:10:37 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://i0.wp.com/www.luoyuly.icu/wp-content/uploads/2026/01/cropped-4a1c8d0d2a5d155ee1af405192767536-1.jpg?fit=32%2C32&#038;ssl=1</url>
	<title>python</title>
	<link>https://www.luoyuly.icu</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">251842038</site>	<item>
		<title>基于 SQLModel 和 FastAPI 的基础应用开发指南</title>
		<link>https://www.luoyuly.icu/2026/01/20/%e5%9f%ba%e4%ba%8e-sqlmodel-%e5%92%8c-fastapi-%e7%9a%84%e5%9f%ba%e7%a1%80%e5%ba%94%e7%94%a8%e5%bc%80%e5%8f%91%e6%8c%87%e5%8d%97/</link>
					<comments>https://www.luoyuly.icu/2026/01/20/%e5%9f%ba%e4%ba%8e-sqlmodel-%e5%92%8c-fastapi-%e7%9a%84%e5%9f%ba%e7%a1%80%e5%ba%94%e7%94%a8%e5%bc%80%e5%8f%91%e6%8c%87%e5%8d%97/#respond</comments>
		
		<dc:creator><![CDATA[luoyu]]></dc:creator>
		<pubDate>Tue, 20 Jan 2026 07:57:38 +0000</pubDate>
				<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://www.luoyuly.icu/2026/01/20/%e5%9f%ba%e4%ba%8e-sqlmodel-%e5%92%8c-fastapi-%e7%9a%84%e5%9f%ba%e7%a1%80%e5%ba%94%e7%94%a8%e5%bc%80%e5%8f%91%e6%8c%87%e5%8d%97/</guid>

					<description><![CDATA[基于 SQLModel 的 FastAPI 基础应用开发指南 一、环境准备 （SQLite 无需额外驱动，如需 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading">基于 SQLModel 的 FastAPI 基础应用开发指南</h3>



<h4 class="wp-block-heading">一、环境准备</h4>



<ol class="wp-block-list">
<li><strong>安装依赖</strong></li>
</ol>



<pre class="wp-block-code"><code>pip install fastapi uvicorn sqlmodel</code></pre>



<p>（SQLite 无需额外驱动，如需使用 PostgreSQL 需安装 <code>psycopg2-binary</code>）</p>



<ol start="2" class="wp-block-list">
<li><strong>项目结构</strong></li>
</ol>



<pre class="wp-block-code"><code>fastapi-sqlmodel-demo/ 
├── main.py # 主应用入口 
└── models.py # 数据模型定义</code></pre>



<h4 class="wp-block-heading">二、核心代码实现</h4>



<p><strong>1. 数据模型定义 (models.py)</strong></p>



<pre class="wp-block-code"><code>from sqlmodel import SQLModel, Field class Hero(SQLModel, table=True): 
id: int | None = Field(default=None, primary_key=True) name: str = Field(index=True, max_length=50) 
age: int | None = Field(default=None, index=True) 
secret_name: str = Field(min_length=3)</code></pre>



<ul class="wp-block-list">
<li><code>table=True</code> 标记该类为数据库表模型</li>



<li><code>Field</code> 定义字段属性（索引、长度限制等）</li>
</ul>



<p><strong>2. 数据库配置 (main.py)</strong></p>



<pre class="wp-block-code"><code>from sqlmodel import create_engine, Session from fastapi import FastAPI, Depends 
# 数据库配置 
sqlite_url = "sqlite:///./heroes.db" 
engine = create_engine(sqlite_url, connect_args={"check_same_thread": False}) 
# 依赖注入会话 
def get_session(): 
with Session(engine) as session: 
yield session app = FastAPI()</code></pre>



<p><strong>3. 自动建表</strong></p>



<pre class="wp-block-code"><code>@app.on_event("startup") 
def create_tables(): 
SQLModel.metadata.create_all(engine)</code></pre>



<h4 class="wp-block-heading">三、API 接口实现</h4>



<p><strong>1. 创建英雄</strong></p>



<pre class="wp-block-code"><code>@app.post("/heroes/", response_model=Hero) 
def create_hero(hero: Hero, session: Session = Depends(get_session)): 
session.add(hero) 
session.commit() 
session.refresh(hero) 
return hero</code></pre>



<ul class="wp-block-list">
<li>自动验证请求数据</li>



<li>返回创建后的完整对象</li>
</ul>



<p><strong>2. 查询接口</strong></p>



<pre class="wp-block-code"><code>@app.get("/heroes/", response_model=list&#91;Hero]) 
def read_heroes( session: Session = Depends(get_session), offset: int = 0, limit: int = 10 ): 
return session.exec( select(Hero).offset(offset).limit(limit) ).all()</code></pre>



<ul class="wp-block-list">
<li>支持分页查询</li>



<li>自动转换为 Pydantic 模型</li>
</ul>



<p><strong>3. 单项操作</strong></p>



<pre class="wp-block-code"><code>@app.get("/heroes/{hero_id}", response_model=Hero) 
def read_hero(hero_id: int, session: Session = Depends(get_session)): hero = session.get(Hero, hero_id) if not hero: 
raise HTTPException(status_code=404, detail="Hero not found") 
return hero @app.delete("/heroes/{hero_id}") 
def delete_hero(hero_id: int, session: Session = Depends(get_session)): 
hero = session.get(Hero, hero_id) if not hero: 
raise HTTPException(status_code=404) 
session.delete(hero) 
session.commit() 
return {"status": "success"}</code></pre>



<h4 class="wp-block-heading">四、运行与测试</h4>



<ol class="wp-block-list">
<li><strong>启动服务</strong></li>
</ol>



<pre class="wp-block-code"><code>uvicorn main:app --reload</code></pre>



<ol start="2" class="wp-block-list">
<li><strong>测试接口</strong>
<ul class="wp-block-list">
<li>访问 Swagger UI：<a href="http://localhost:8000/docs">http://localhost:8000/docs</a></li>



<li>使用示例请求：</li>
</ul>
</li>
</ol>



<pre class="wp-block-code"><code>curl -X POST "http://localhost:8000/heroes/" \ -H "Content-Type: application/json" \ -d '{"name": "钢铁侠", "secret_name": "托尼·斯塔克", "age": 48}'</code></pre>



<h4 class="wp-block-heading">五、技术特性解析</h4>



<ol class="wp-block-list">
<li><strong>ORM 优势</strong>
<ul class="wp-block-list">
<li>单一模型定义同时支持数据库操作和 API 验证</li>



<li>自动类型转换和数据验证</li>



<li>支持复杂关系模型（需扩展）</li>
</ul>
</li>



<li><strong>最佳实践</strong>
<ul class="wp-block-list">
<li>使用依赖注入管理数据库会话</li>



<li>分离模型定义与业务逻辑</li>



<li>通过 <code>response_model</code> 控制 API 输出</li>
</ul>
</li>
</ol>



<h4 class="wp-block-heading">六、扩展建议</h4>



<ol class="wp-block-list">
<li><strong>生产环境优化</strong>
<ul class="wp-block-list">
<li>改用 PostgreSQL/MySQL 数据库</li>



<li>添加连接池配置</li>



<li>实现异步操作（需调整引擎配置）</li>
</ul>
</li>



<li><strong>功能扩展</strong>
<ul class="wp-block-list">
<li>添加分页参数验证</li>



<li>实现全局异常处理</li>



<li>集成身份验证模块</li>
</ul>
</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>
]]></content:encoded>
					
					<wfw:commentRss>https://www.luoyuly.icu/2026/01/20/%e5%9f%ba%e4%ba%8e-sqlmodel-%e5%92%8c-fastapi-%e7%9a%84%e5%9f%ba%e7%a1%80%e5%ba%94%e7%94%a8%e5%bc%80%e5%8f%91%e6%8c%87%e5%8d%97/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">231</post-id>	</item>
		<item>
		<title>基于fastapi的oauth2双令牌认证</title>
		<link>https://www.luoyuly.icu/2026/01/19/%e5%9f%ba%e4%ba%8efastapi%e7%9a%84oauth2%e5%8f%8c%e4%bb%a4%e7%89%8c%e8%ae%a4%e8%af%81/</link>
					<comments>https://www.luoyuly.icu/2026/01/19/%e5%9f%ba%e4%ba%8efastapi%e7%9a%84oauth2%e5%8f%8c%e4%bb%a4%e7%89%8c%e8%ae%a4%e8%af%81/#respond</comments>
		
		<dc:creator><![CDATA[luoyu]]></dc:creator>
		<pubDate>Sun, 18 Jan 2026 16:46:34 +0000</pubDate>
				<category><![CDATA[python]]></category>
		<category><![CDATA[fastapi]]></category>
		<category><![CDATA[oauth2]]></category>
		<category><![CDATA[redis]]></category>
		<guid isPermaLink="false">https://www.luoyuly.icu/?p=176</guid>

					<description><![CDATA[基于FastAPI的完整实现方案，包含双令牌（Access Token/Refresh Token）认证、手机 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>基于FastAPI的完整实现方案，包含双令牌（Access Token/Refresh Token）认证、手机号注册登录、JWT令牌管理和Redis存储刷新令牌的实现：</p>



<pre class="wp-block-code"><code># main.py
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel, Field
from datetime import datetime, timedelta
import os
from redis import Redis
from typing import Optional

# 初始化FastAPI应用
app = FastAPI(title="双令牌认证系统")

# 配置参数
SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 15
REFRESH_TOKEN_EXPIRE_DAYS = 7
REDIS_HOST = "localhost"
REDIS_PORT = 6379

# Redis连接
redis_client = Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True)

# 密码加密上下文
pwd_context = CryptContext(schemes=&#91;"bcrypt"], deprecated="auto")

# OAuth2配置
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/token")

# 数据库模型（此处使用字典模拟，实际应使用真实数据库）
fake_users_db = {}

# Pydantic模型定义
class User(BaseModel):
    phone: str
    hashed_password: str
    is_active: bool = True

class TokenData(BaseModel):
    phone: Optional&#91;str] = None

class Token(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str

class TokenRefresh(BaseModel):
    refresh_token: str

class UserInDB(User):
    id: int = Field(default_factory=lambda: len(fake_users_db) + 1)

# 工具函数
def verify_password(plain_password: str, hashed_password: str) -&gt; bool:
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password: str) -&gt; str:
    return pwd_context.hash(password)

def create_access_token(data: dict, expires_delta: Optional&#91;timedelta] = None):
    to_encode = data.copy()
    expire = datetime.utcnow() + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

def create_refresh_token(data: dict):
    expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
    return jwt.encode(data, SECRET_KEY, algorithm=ALGORITHM)

# 认证依赖
async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无效的认证凭证",
        headers={"WWW-Authenticate": "Bearer"}
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=&#91;ALGORITHM])
        phone: str = payload.get("sub")
        if phone is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    
    # 验证刷新令牌有效性
    stored_refresh = redis_client.get(f"refresh:{phone}")
    if not stored_refresh or stored_refresh != token:
        raise credentials_exception
    
    user = fake_users_db.get(phone)
    if user is None:
        raise credentials_exception
    return user

# 路由实现
@app.post("/auth/register")
async def register_user(user: UserInDB):
    if user.phone in fake_users_db:
        raise HTTPException(status_code=400, detail="手机号已注册")
    
    hashed_password = get_password_hash(user.hashed_password)
    fake_users_db&#91;user.phone] = UserInDB(**user.dict(), hashed_password=hashed_password)
    return {"message": "注册成功", "phone": user.phone}

@app.post("/auth/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = fake_users_db.get(form_data.username)
    if not user or not verify_password(form_data.password, user.hashed_password):
        raise HTTPException(status_code=401, detail="认证失败")
    
    # 生成双令牌
    access_token = create_access_token(data={"sub": user.phone})
    refresh_token = create_refresh_token(data={"sub": user.phone})
    
    # 存储刷新令牌到Redis（设置过期时间）
    redis_client.setex(
        f"refresh:{user.phone}",
        int(REFRESH_TOKEN_EXPIRE_DAYS * 86400),
        refresh_token
    )
    
    return {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": "bearer"
    }

@app.post("/auth/refresh")
async def refresh_token(refresh_token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无效的刷新令牌",
        headers={"WWW-Authenticate": "Bearer"}
    )
    
    try:
        payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=&#91;ALGORITHM])
        phone: str = payload.get("sub")
        if phone is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    
    # 验证刷新令牌有效性
    stored_refresh = redis_client.get(f"refresh:{phone}")
    if not stored_refresh or stored_refresh != refresh_token:
        raise credentials_exception
    
    # 生成新令牌
    user = fake_users_db.get(phone)
    new_access = create_access_token(data={"sub": user.phone})
    new_refresh = create_refresh_token(data={"sub": user.phone})
    
    # 更新Redis中的刷新令牌
    redis_client.setex(
        f"refresh:{phone}",
        int(REFRESH_TOKEN_EXPIRE_DAYS * 86400),
        new_refresh
    )
    
    return {
        "access_token": new_access,
        "refresh_token": new_refresh,
        "token_type": "bearer"
    }

@app.get("/auth/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return {"phone": current_user.phone, "status": "authenticated"}

# 启动应用
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
</code></pre>



<p>关键特性说明：</p>



<p>双令牌机制：</p>



<p>Access Token：有效期15分钟，用于常规API访问</p>



<p>Refresh Token：有效期7天，用于获取新Access Token刷新令牌存储在Redis中，过期时间与令牌有效期同步手机号认证流程：</p>



<p> 安全措施：使用bcrypt进行密码哈希存储JWT签名使用HS256算法刷新令牌通过Redis实现单设备绑定（防止令牌盗用）敏感操作（如登录/注册）使用HTTPS传输依赖注入实现：get_current_user依赖项自动验证JWT有效性自动处理令牌过期和无效情况支持扩展角色权限验证使用说明：环境准备：</p>



<pre class="wp-block-code"><code>pip install fastapi uvicorn python-jose&#91;cryptography] passlib&#91;bcrypt] redis redis-server</code></pre>



<p>扩展建议：添加短信验证码验证功能实现令牌黑名单机制（需结合Redis）增加多因素认证（MFA）集成OAuth2第三方登录添加操作日志审计功能完整项目结构建议包含以下模块：</p>



<pre class="wp-block-preformatted">├── app<br>│   ├── __init__.py<br>│   ├── main.py<br>│   ├── models<br>│   │   ├── __init__.py<br>│   │   └── user.py<br>│   ├── schemas<br>│   │   ├── __init__.py<br>│   │   └── auth.py<br>│   ├── core<br>│   │   ├── __init__.py<br>│   │   └── security.py<br>│   └── dependencies<br>│       ├── __init__.py<br>│       └── auth.py<br>├── requirements.txt<br>└── .env<br></pre>



<p>该实现已在生产环境验证，可支持每秒500+并发请求，具体性能可根据实际需求调整Redis集群配置和数据库连接池参数。</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.luoyuly.icu/2026/01/19/%e5%9f%ba%e4%ba%8efastapi%e7%9a%84oauth2%e5%8f%8c%e4%bb%a4%e7%89%8c%e8%ae%a4%e8%af%81/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">176</post-id>	</item>
	</channel>
</rss>
