项目简介
内容管理系统(CMS)是很多企业官网、知识库、博客平台和内部运营后台的基础能力。本项目以"文章发布与栏目管理"为核心,完整实现一个前后端分离的 Python 全栈项目:用户可以注册、登录、创建栏目、撰写文章、发布文章、搜索文章,并在仪表盘中查看自己的内容统计。
项目不是单纯讲概念,而是给出可运行源码:后端使用 FastAPI 提供 REST API,SQLite 作为轻量数据库,前端使用 Vue 3 + Vite 构建单页应用。系统包含密码哈希、Token 登录态、接口鉴权、CORS、数据模型、API 封装和基础 UI。
技术栈
- 后端:Python 3、FastAPI、Pydantic、SQLite、Uvicorn
- 前端:Vue 3、Vite、单文件组件、Fetch API
- 数据存储:SQLite,本地自动创建
cms.db - 认证方案:PBKDF2 密码哈希 + 随机 Bearer Token
- 项目形态:前后端分离,全栈项目源码位于
project/目录
系统架构
text
浏览器 / Vue 3 页面
│
│ HTTP JSON + Bearer Token
▼
FastAPI REST API
│
├── auth:注册、登录、当前用户、退出
├── categories:栏目管理
├── articles:文章管理、搜索、发布
└── dashboard:内容统计
│
▼
SQLite 数据库
前端负责页面交互和登录态保存,后端负责业务规则、认证鉴权、数据持久化。用户登录后,前端在请求头中携带 token;后端通过 tokens 表校验 token 是否存在和是否过期。
功能模块
-
用户模块
- 注册用户
- 登录并返回访问 token
- 获取当前用户信息
- 退出登录并删除 token
-
栏目模块
- 查看所有栏目
- 登录后创建栏目
- 栏目可被文章引用
-
文章模块
- 查看文章列表
- 根据关键字和状态筛选
- 登录后创建文章
- 作者可编辑、删除自己的文章
- 支持草稿、发布、归档三种状态
-
仪表盘模块
- 统计当前用户文章数
- 统计已发布文章、草稿数
- 统计阅读量
数据库/数据模型设计
本项目使用 SQLite,后端启动时自动执行建表语句。核心表如下。
users 用户表
| 字段 | 类型 | 说明 |
|---|---|---|
| id | INTEGER | 主键 |
| username | TEXT | 用户名,唯一 |
| TEXT | 邮箱,唯一 | |
| password_hash | TEXT | 加盐哈希后的密码 |
| role | TEXT | 用户角色 |
| created_at | TEXT | 创建时间 |
tokens 登录令牌表
| 字段 | 类型 | 说明 |
|---|---|---|
| token | TEXT | token 主键 |
| user_id | INTEGER | 所属用户 |
| expires_at | TEXT | 过期时间 |
| created_at | TEXT | 创建时间 |
categories 栏目表
| 字段 | 类型 | 说明 |
|---|---|---|
| id | INTEGER | 主键 |
| name | TEXT | 栏目名称 |
| description | TEXT | 栏目描述 |
| owner_id | INTEGER | 创建者 |
articles 文章表
| 字段 | 类型 | 说明 |
|---|---|---|
| id | INTEGER | 主键 |
| title | TEXT | 标题 |
| slug | TEXT | URL 别名,唯一 |
| summary | TEXT | 摘要 |
| content | TEXT | 正文 |
| status | TEXT | draft/published/archived |
| category_id | INTEGER | 栏目 ID |
| author_id | INTEGER | 作者 ID |
| view_count | INTEGER | 阅读量 |
| published_at | TEXT | 发布时间 |
后端接口设计
| 方法 | URL | 功能 | 鉴权 |
|---|---|---|---|
| POST | /api/auth/register |
注册账号并登录 | 否 |
| POST | /api/auth/login |
用户登录 | 否 |
| GET | /api/auth/me |
当前用户信息 | 是 |
| POST | /api/auth/logout |
退出登录 | 是 |
| GET | /api/dashboard |
获取统计数据 | 是 |
| GET | /api/categories |
栏目列表 | 否 |
| POST | /api/categories |
创建栏目 | 是 |
| GET | /api/articles |
文章列表/搜索 | 否 |
| GET | /api/articles/{id} |
文章详情 | 否 |
| POST | /api/articles |
创建文章 | 是 |
| PUT | /api/articles/{id} |
更新文章 | 是 |
| DELETE | /api/articles/{id} |
删除文章 | 是 |
接口返回 JSON,前端统一通过 frontend/src/api.js 封装请求。受保护接口会读取 Authorization 请求头并验证 token。
前端页面设计
前端使用 Vue 3 + Vite,核心页面集中在 App.vue 中,适合作为教学项目阅读和改造。
- 登录/注册页:未登录时显示,注册成功后自动进入系统。
- 左侧导航:数据概览、写文章、内容列表、栏目管理。
- 数据概览:展示文章总数、已发布、草稿和阅读量。
- 栏目管理:新增栏目并展示栏目列表。
- 文章编辑器:填写标题、slug、摘要、正文、栏目和状态。
- 内容列表:展示文章卡片,支持编辑和删除。
核心代码讲解
1. 数据库初始化
backend/app/database.py 使用 sqlite3 创建连接,并在启动时初始化数据表:
python
def init_db() -> None:
conn = get_connection()
try:
conn.executescript("""
CREATE TABLE IF NOT EXISTS users (...);
CREATE TABLE IF NOT EXISTS categories (...);
CREATE TABLE IF NOT EXISTS articles (...);
CREATE TABLE IF NOT EXISTS tokens (...);
""")
conn.commit()
finally:
conn.close()
这样项目首次运行时不需要手动建库,适合本地学习和演示。
2. 密码哈希与登录 token
backend/app/crud.py 中使用 PBKDF2 对密码进行加盐哈希:
python
def hash_password(password: str) -> str:
salt = secrets.token_hex(16)
digest = hashlib.pbkdf2_hmac(
"sha256", password.encode("utf-8"), salt.encode("utf-8"), 120_000
)
return f"pbkdf2_sha256${salt}${digest.hex()}"
登录成功后生成随机 token,并写入 tokens 表。后续请求只要携带 Authorization 请求头即可访问受保护接口。
3. FastAPI 鉴权依赖
backend/app/main.py 中的 current_user 是所有受保护接口复用的鉴权依赖:
python
def current_user(authorization: Optional[str] = Header(default=None), db=Depends(get_db)):
if not authorization or not authorization.lower().startswith("bearer "):
raise HTTPException(status_code=401, detail="请先登录")
token = authorization.split(" ", 1)[1].strip()
user = crud.get_user_by_token(db, token)
if not user:
raise HTTPException(status_code=401, detail="登录已过期")
return user
在创建文章、创建栏目、删除文章等接口上加入 Depends(current_user),即可实现接口级保护。
4. 文章创建接口
python
@app.post("/api/articles", response_model=schemas.ArticleOut)
def create_article(payload: schemas.ArticleCreate, user=Depends(current_user), db=Depends(get_db)):
return crud.create_article(db, payload.model_dump(), user["id"])
接口从 token 中识别作者,不允许前端直接提交作者 ID,避免伪造作者身份。
5. Vue API 封装
frontend/src/api.js 统一处理后端地址、JSON 请求和 token 注入:
javascript
export async function request(path, options = {}) {
const headers = { 'Content-Type': 'application/json', ...(options.headers || {}) }
const token = getToken()
if (token) headers.Authorization = `Bearer ${token}`
const response = await fetch(`${API_BASE}${path}`, { ...options, headers })
const data = await response.json().catch(() => ({}))
if (!response.ok) throw new Error(data.detail || '请求失败')
return data
}
前端组件只需要调用 api.createArticle()、api.login() 等方法,不需要重复写请求细节。
部署与运行步骤
源码在 project/ 目录中。
运行后端
bash
cd project/backend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
后端默认地址:http://127.0.0.1:8000。
运行前端
bash
cd project/frontend
npm install
npm run dev
前端默认访问 Vite 输出的地址,通常是 http://127.0.0.1:5173。如果后端地址变化,请修改 frontend/src/api.js 中的 API_BASE。
初始化/使用示例
- 进入前端页面,注册账号。
- 创建栏目,例如"公告""教程""产品动态"。
- 新建文章,状态选择"发布"。
- 回到内容列表,搜索关键字并编辑文章。
- 退出登录后,再尝试创建文章会被后端拒绝。
项目总结
这个内容管理系统覆盖了全栈项目的常见核心点:认证、鉴权、数据建模、REST API、前端状态管理、接口封装和本地部署。虽然它是一个轻量级示例,但结构清晰,后续可以继续扩展富文本编辑器、图片上传、评论系统、角色权限、操作日志、文章版本管理、静态站点生成等能力。
对于学习 Python 全栈开发的同学,这个项目的重点不在"功能堆砌",而在于理解前后端如何围绕同一套业务模型协作:后端保证数据安全和业务规则,前端负责用户体验和状态流转。掌握这套思路后,就可以把 CMS 改造成企业官网后台、知识库平台、博客系统或内部运营平台。