昨天搭了个基本的博客API,但数据验证太简单了。今天加强一下验证规则,让系统更安全。
昨天的问题
Day1的验证确实太弱了:
- 密码只检查长度,"12345678"这种弱密码也能过
- 邮箱没限制域名,临时邮箱、垃圾邮箱都能注册
- 文章内容没过滤,啥都能发
今天分三步解决:密码强度 → 邮箱限制 → 内容过滤
第一步:密码强度验证
昨天只检查密码长度,"12345678"这种弱密码也能过。现在加上复杂度要求。
修改密码验证
在Day1代码基础上,只改schemas.py里的密码验证:
# 修改 schemas.py 中的 UserRegister 类
# 其他代码保持不变,只增加密码复杂度检查
from pydantic import BaseModel, Field, EmailStr, validator
import re
class UserRegister(BaseModel):
"""用户注册模型"""
username: str = Field(..., min_length=3, max_length=50, description="用户名")
email: EmailStr = Field(..., description="邮箱地址")
password: str = Field(..., min_length=8, description="密码")
# 新增:密码复杂度验证
@validator('password')
def validate_password_strength(cls, v):
"""验证密码强度"""
# 检查大写字母
if not re.search(r'[A-Z]', v):
raise ValueError('密码必须包含至少一个大写字母')
# 检查小写字母
if not re.search(r'[a-z]', v):
raise ValueError('密码必须包含至少一个小写字母')
# 检查数字
if not re.search(r'\d', v):
raise ValueError('密码必须包含至少一个数字')
# 检查特殊字符
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', v):
raise ValueError('密码必须包含至少一个特殊字符(!@#$%^&*等)')
# 检查连续字符(防止123456、abcdef这类密码)
if re.search(r'(.)\1{2,}', v): # 3个以上相同字符
raise ValueError('密码不能包含3个以上连续相同字符')
if re.search(r'(012|123|234|345|456|567|678|789)', v):
raise ValueError('密码不能包含连续数字')
if re.search(r'(abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz)', v.lower()):
raise ValueError('密码不能包含连续字母')
return v
测试密码验证
测试弱密码(应该失败):
curl -X POST "http://localhost:8000/users/register" \
-H "Content-Type: application/json" \
-d '{
"username": "洛克斯",
"email": "luokesi@example.com",
"password": "12345678"
}'
应该会看到详细的错误信息:

测试强密码(应该成功):
curl -X POST "http://localhost:8000/users/register" \
-H "Content-Type: application/json" \
-d '{
"username": "洛克斯",
"email": "luokesi@example.com",
"password": "MyPass136!"
}'

第二步:邮箱域名限制
有用户用临时邮箱注册,过期后找不回账号。还有垃圾邮箱增加运营成本。限制一下邮箱域名。
增加邮箱域名验证
在第一步基础上,继续修改schemas.py:
# 修改 schemas.py 中的 UserRegister 类
# 其他代码保持不变,只增加邮箱域名验证
from pydantic import BaseModel, Field, EmailStr, validator
import re
class UserRegister(BaseModel):
"""用户注册模型 - 负责格式验证"""
username: str = Field(..., min_length=3, max_length=50, description="用户名")
email: EmailStr = Field(..., description="邮箱地址")
password: str = Field(..., min_length=8, description="密码")
@validator('password')
def validate_password_strength(cls, v):
"""验证密码强度"""
# 检查大写字母
if not re.search(r'[A-Z]', v):
raise ValueError('密码必须包含至少一个大写字母')
# 检查小写字母
if not re.search(r'[a-z]', v):
raise ValueError('密码必须包含至少一个小写字母')
# 检查数字
if not re.search(r'\d', v):
raise ValueError('密码必须包含至少一个数字')
# 检查特殊字符
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', v):
raise ValueError('密码必须包含至少一个特殊字符(!@#$%^&*等)')
# 检查连续字符(防止123456、abcdef这类密码)
if re.search(r'(.)\1{2,}', v): # 3个以上相同字符
raise ValueError('密码不能包含3个以上连续相同字符')
if re.search(r'(012|123|234|345|456|567|678|789)', v):
raise ValueError('密码不能包含连续数字')
if re.search(r'(abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz)', v.lower()):
raise ValueError('密码不能包含连续字母')
return v
# 新增:邮箱域名验证
@validator('email')
def validate_email_domain(cls, v):
"""验证邮箱域名限制"""
domain = v.split('@')[1].lower()
allowed_domains = [
'gmail.com', 'qq.com', '163.com', '126.com',
'outlook.com', 'hotmail.com', 'sina.com'
]
if domain not in allowed_domains:
allowed_list = ', '.join(allowed_domains)
raise ValueError(f'不支持的邮箱域名,请使用以下邮箱: {allowed_list}')
return v
测试邮箱域名限制
测试不支持的邮箱域名(应该失败):
curl -X POST "http://localhost:8000/users/register" \
-H "Content-Type: application/json" \
-d '{
"username": "我是罗杰",
"email": "luojie@onepiece.com",
"password": "MyPass136!"
}'
应该看到域名限制的错误:

测试支持的邮箱域名(应该成功):
curl -X POST "http://localhost:8000/users/register" \
-H "Content-Type: application/json" \
-d '{
"username": "我是罗杰",
"email": "luojie@qq.com",
"password": "MyPass136!"
}'

第三步:内容安全过滤
用户发文章可能包含广告、垃圾信息。加个基础的内容检查。
增加内容安全验证
添加内容安全检查函数,在文章创建时使用:
# 继续修改 schemas.py
# 在文件开头增加内容安全检查函数
def validate_content_safety(content: str) -> str:
"""检查内容安全性"""
# 定义敏感词列表(实际项目中应该从数据库或配置文件读取)
banned_words = [
'黑胡子','白胡子','茶胡子'
]
content_lower = content.lower()
for banned_word in banned_words:
if banned_word in content_lower:
raise ValueError(f'内容中包含敏感词: {banned_word}')
return content
# 修改 PostCreate 模型,增加内容安全检查
class PostCreate(BaseModel):
"""文章创建模型"""
title: str = Field(..., min_length=5, max_length=200, description="文章标题")
content: str = Field(..., min_length=10, description="文章内容")
# 新增:文章标题安全检查
@validator('title')
def validate_title_safety(cls, v):
"""验证文章标题安全性"""
return validate_content_safety(v)
# 新增:文章内容安全检查
@validator('content')
def validate_content_safety_and_length(cls, v):
"""验证文章内容安全性和长度"""
# 检查内容安全
validate_content_safety(v)
# 检查内容长度上限
if len(v) > 10000:
raise ValueError('文章内容不能超过10000字符')
return v
测试内容过滤
注意:发布文章需要先登录
# 首先确保已登录(如果还没登录的话)
curl -X POST "http://localhost:8000/users/login" \
-H "Content-Type: application/json" \
-d '{
"account": "我是罗杰",
"password": "MyPass136!"
}'

然后测试包含敏感词的文章(应该失败):
curl -X POST "http://localhost:8000/posts" \
-H "Content-Type: application/json" \
-d '{
"title": "他们都是胡子",
"content": "白胡子强于黑胡子,黑胡子强于茶胡子!"
}'
现在应该会看到内容安全验证的错误信息

更新版本信息
完成验证功能后,更新main.py中的版本信息:
修改的地方
# 1. 更新应用标题和版本
app = FastAPI(
title="博客系统API v2.0", # 从 "博客系统API" 改为 "博客系统API v2.0"
description="7天FastAPI学习系列 - Day2数据验证增强版本", # 更新描述
version="2.0.0" # 从 "1.0.0" 改为 "2.0.0"
)
# 2. 更新根路由的版本信息
@app.get("/")
def root():
return {
"message": "欢迎使用博客系统API v2.0", # 更新消息
"version": "2.0.0", # 更新版本号
"docs": "/docs",
"features": ["用户管理", "文章管理", "数据验证增强"], # 添加新功能
"next_version": "Day3将添加数据库持久化" # 更新下一步
}
# 3. 更新健康检查接口
@app.get("/health")
def health_check():
return {
"status": "healthy",
"version": "2.0.0", # 添加版本信息
"users_count": len(users_db),
"posts_count": len(posts_db)
}
今天完成了什么
加强了数据验证:
- 密码强度:大小写字母 + 数字 + 特殊字符
- 邮箱限制:只允许主流邮箱域名
- 内容过滤:基础的敏感词检查
现在的系统安全性提升了不少,但还是用内存存储。明天换成真正的数据库。
2356

被折叠的 条评论
为什么被折叠?



