赞
踩
编写博客系统的目的是为了更加深入的了解项目相关各种命令及程序流程,使自己熟练的掌握一些基础知识
开发使用了前后端分离的技术,使用docker部署
server : 使用python语言,fastapi库
client :nodejs的前端开发环境
vite + ts :前端构建工具
vue3 + vue-router + navie-ui : js库,框架
openapi-generate-typescript :js库,用于自动生成axios请求代码
markdown解析和高亮使用highlight.js,编辑器v-md-editor
feature
系统主要完成以下几方面的功能:
用户的相关信息如下:用户ID、用户名、用户密码、用户头像、用户组、状态、注册时间、更新时间。
用户注册时需提供用户名、用户密码。
用户登录时可以采用用户名,密码进行登录。
用户可以发布文章、发表评论、回复。审核、删除自己文章的评论,
文章的相关信息如下:文章ID、发布日期、发表用户ID、文章标题、文章内容、状态。
文章可以被普通用户发布、修改、删除和评论,但修改和删除仅限于自己发表的。
文章发布时需要设置标签。
评论的相关信息如下:评论ID、评论日期、发表用户ID、评论文章ID、评论内容、父评论ID、状态。
评论可以被用户发表和删除以及被其他用户回复。
标签的相关信息如下:标签ID、标签名称
用户发表文章时可以设置标签
个人资料管理
修改头像,修改密码
文章管理
发表文章,删除文章
评论管理
发表评论,回复评论,删除自己发表的评论,审核/删除自己文章下的评论
标签管理
创建标签
在个人后台和普通用户一致,并且可以访问管理员后台
用户管理
审核/封禁用户,修改是否可以注册,查看api接口统计
用户表(tb_user)
字段名 | 数据类型 | 说明 |
---|---|---|
id | int | 用户ID,自增主键 |
username | varchar(255) | 用户名,不可重复 |
password | varchar(255) | 用户密码,加密存储 |
avatar | varchar(255) | 用户头像 |
group_id | int | 用户组ID,用于权限管理 |
state | int | 用户状态ID,0表示未审核,1表示已审核 |
created_at | datetime | 注册时间 |
updated_at | datetime | 最近更新时间 |
文章表(tb_post)
字段名 | 数据类型 | 说明 |
---|---|---|
id | int | 文章ID,自增主键 |
user_id | int | 发表用户ID |
title | varchar(50) | 文章标题 |
content | text | 文章内容 |
state | int | 文章状态ID,0表示未审核,1表示已审核 |
created_at | datetime | 发表时间 |
updated_at | datetime | 最近更新时间 |
文章标签关联表(tb_post_tag)
字段名 | 数据类型 | 说明 |
---|---|---|
post_id | int | 文章ID,外键关联tb_post表 |
tag_id | int | 标签ID,外键关联tb_tag表 |
评论表(tb_comments)
字段名 | 数据类型 | 说明 |
---|---|---|
id | int | 评论ID,自增主键 |
post_id | int | 评论文章ID,外键关联tb_post表 |
parent_id | int | 父评论ID,用于表示评论的层级关系 |
uid | int | 评论用户ID,外键关联tb_user表 |
content | text | 评论内容 |
state | int | 评论状态ID,0表示未审核,1表示已审核 |
created_at | datetime | 发表时间 |
标签表(tb_tag)
字段名 | 数据类型 | 说明 |
---|---|---|
id | int | 标签ID,自增主键 |
name | varchar(50) | 标签名称 |
reference_count | int | 标签被引用次数,用于热门标签的排序 |
以上是blog数据库的表结构设计,其中表之间通过外键关联实现数据的关联和查询。
使用orm 模型生成,sql文件是运行后导出的
模型类,每一个类对应一个数据表
from sqlalchemy import Column, String, Integer, DateTime, func, ForeignKey, Text from sqlalchemy.orm import declarative_base, relationship Base = declarative_base() class User(Base): __tablename__ = 'tb_user' id = Column(Integer, primary_key=True, index=True, autoincrement=True) username = Column(String(255), unique=True, nullable=False, comment='用户名') password = Column(String(255), unique=False, nullable=False, comment='密码') avatar = Column(String(255), unique=False, nullable=True, default='', comment='头像') group_id = Column(Integer, unique=False, nullable=False, comment='用户组id', default=0) state = Column(Integer, unique=False, nullable=False, comment='状态id', default=0) created_at = Column(DateTime, default=func.now(), server_default=func.now(), nullable=False, comment='创建时间') updated_at = Column(DateTime, default=func.now(), server_default=func.now(), nullable=False, onupdate=func.now(), comment='更新时间') under_posts = relationship('Post', back_populates='own_user', cascade="all, delete") # 用户拥有的文章 def __repr__(self): return f'<User>id={self.id},username={self.username},group_id={self.group_id} ...' class Tag(Base): __tablename__ = 'tb_tag' id = Column(Integer, primary_key=True, index=True, autoincrement=True) name = Column(String(50), nullable=False) reference_count = Column(Integer) under_posts = relationship('Post', secondary='tb_post_tag', passive_deletes=True) # 多对多, tag被多个post拥有 def __repr__(self): return f'<tag> id={self.id},name={self.name}' class Post(Base): __tablename__ = 'tb_post' id = Column(Integer, primary_key=True, index=True, autoincrement=True) user_id = Column(Integer, ForeignKey("tb_user.id"), nullable=False) title = Column(String(50), nullable=False) content = Column(Text, nullable=False) state = Column(Integer, unique=False, nullable=False, comment='状态id', default=0, server_default='0') created_at = Column(DateTime, default=func.now(), server_default=func.now(), nullable=False, comment='创建时间') updated_at = Column(DateTime, default=func.now(), server_default=func.now(), nullable=False, onupdate=func.now(), comment='更新时间') own_user = relationship('User', back_populates='under_posts', passive_deletes=True) # 文章作者 own_tags = relationship('Tag', secondary='tb_post_tag', overlaps='under_posts') # 拥有的tag own_comments = relationship("Comment", back_populates="under_post", cascade="all, delete") # 拥有的评论 def __repr__(self): return f'<post> id={self.id},title={self.title} ...' class PostTag(Base): # 中间表 __tablename__ = 'tb_post_tag' post_id = Column(Integer, ForeignKey("tb_post.id"), primary_key=True) tag_id = Column(Integer, ForeignKey("tb_tag.id"), primary_key=True) def __repr__(self): return f'<post_tage> post_id={self.post_id},tag_id={self.tag_id} ...' class Comment(Base): __tablename__ = 'tb_comments' id = Column(Integer, primary_key=True) post_id = Column(Integer, ForeignKey('tb_post.id'), nullable=False) parent_id = Column(Integer, nullable=False) uid = Column(Integer, ForeignKey('tb_user.id'), nullable=False) content = Column(Text, nullable=False) state = Column(Integer, nullable=False, comment='状态id', default=0) created_at = Column(DateTime, default=func.now(), server_default=func.now(), nullable=False, comment='创建时间') under_post = relationship("Post", back_populates="own_comments", passive_deletes=True) # 所属文章 def __repr__(self): return f'<Comment> id={self.id},content={self.content} ...'
建表语句
-- -------------------------------------------------------- -- 主机: 127.0.0.1 -- 服务器版本: 8.0.29 - MySQL Community Server - GPL -- 服务器操作系统: Win64 -- HeidiSQL 版本: 12.3.0.6589 -- -------------------------------------------------------- CREATE DATABASE IF NOT EXISTS `blog`; USE `blog`; -- 导出 表 blog.tb_comments 结构 CREATE TABLE IF NOT EXISTS `tb_comments` ( `id` int NOT NULL AUTO_INCREMENT, `post_id` int NOT NULL, `parent_id` int NOT NULL, `uid` int NOT NULL, `content` text NOT NULL, `state` int NOT NULL COMMENT '状态id', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `post_id` (`post_id`), KEY `uid` (`uid`), CONSTRAINT `tb_comments_ibfk_1` FOREIGN KEY (`post_id`) REFERENCES `tb_post` (`id`), CONSTRAINT `tb_comments_ibfk_2` FOREIGN KEY (`uid`) REFERENCES `tb_user` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -- 导出 表 blog.tb_post 结构 CREATE TABLE IF NOT EXISTS `tb_post` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NOT NULL, `title` varchar(50) NOT NULL, `content` text NOT NULL, `state` int NOT NULL DEFAULT '0' COMMENT '状态id', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `ix_tb_post_id` (`id`), CONSTRAINT `tb_post_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `tb_user` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -- 导出 表 blog.tb_post_tag 结构 CREATE TABLE IF NOT EXISTS `tb_post_tag` ( `post_id` int NOT NULL, `tag_id` int NOT NULL, PRIMARY KEY (`post_id`,`tag_id`), KEY `tag_id` (`tag_id`), CONSTRAINT `tb_post_tag_ibfk_1` FOREIGN KEY (`post_id`) REFERENCES `tb_post` (`id`), CONSTRAINT `tb_post_tag_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tb_tag` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -- 导出 表 blog.tb_tag 结构 CREATE TABLE IF NOT EXISTS `tb_tag` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `reference_count` int DEFAULT NULL, PRIMARY KEY (`id`), KEY `ix_tb_tag_id` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -- 导出 表 blog.tb_user 结构 CREATE TABLE IF NOT EXISTS `tb_user` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL COMMENT '用户名', `password` varchar(255) NOT NULL COMMENT '密码', `avatar` varchar(255) DEFAULT NULL COMMENT '头像', `group_id` int NOT NULL COMMENT '用户组id', `state` int NOT NULL COMMENT '状态id', `created_at` datetime NOT NULL COMMENT '创建时间', `updated_at` datetime NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`), KEY `ix_tb_user_id` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
使用python库 bandit
Bandit 是一款旨在查找 Python 代码中常见安全问题的工具。为此,Bandit 处理每个文件,从中构建 AST
(fastapipg-3.11) 24123@lolik D:\24123\code\python\fastapipg git:use-pdm ~5 > bandit -r app.py sql api config [main] INFO profile include tests: None [main] INFO profile exclude tests: None [main] INFO cli include tests: None [main] INFO cli exclude tests: None [main] INFO running on Python 3.11.1 [node_visitor] WARNING Unable to find qualified name for module: app.py Run started:2023-06-17 08:30:52.391282 Test results: >> Issue: [B324:hashlib] Use of weak MD5 hash for security. Consider usedforsecurity=False Severity: High Confidence: High CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html) More Info: https://bandit.readthedocs.io/en/1.7.5/plugins/b324_hashlib.html Location: api\userapi.py:144:10 143 144 md5 = hashlib.md5(data).hexdigest() 145 filename = f"{md5}{ext}" -------------------------------------------------- >> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction. Severity: Medium Confidence: Low CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html) More Info: https://bandit.readthedocs.io/en/1.7.5/plugins/b608_hardcoded_sql_expressions.html Location: config\options.py:162:46 161 sheet = workbook.add_sheet(f'{table_name}', cell_overwrite_ok=True) 162 result = session.execute(text(f"SELECT * FROM {table_name}")) 163 columns = (i[0] for i in result.cursor.description) -------------------------------------------------- >> Issue: [B605:start_process_with_a_shell] Starting a process with a shell, possible injection detected, security issue. Severity: High Confidence: High CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) More Info: https://bandit.readthedocs.io/en/1.7.5/plugins/b605_start_process_with_a_shell.html Location: config\options.py:203:4 202 if not args.vite and args.open: 203 os.system(f'start chrome http://127.0.0.1:{args.port}') 204 -------------------------------------------------- >> Issue: [B605:start_process_with_a_shell] Starting a process with a shell, possible injection detected, security issue. Severity: High Confidence: High CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html) More Info: https://bandit.readthedocs.io/en/1.7.5/plugins/b605_start_process_with_a_shell.html Location: config\options.py:208:4 207 f.write(f"export const host:string = 'http://127.0.0.1:{args.port}'") 208 os.system(f'start cmd /k "cd {ppath}static && npm run dev"') 209 -------------------------------------------------- >> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Severity: Low Confidence: High CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html) More Info: https://bandit.readthedocs.io/en/1.7.5/plugins/b101_assert_used.html Location: sql\crud.py:108:12 107 u = (await session.execute(select(User).where(User.username == username_new))).scalar_one_or_none() 108 assert u is not None 109 return UpdateSuccess.from_User(u, "更新成功") -------------------------------------------------- >> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Severity: Low Confidence: High CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html) More Info: https://bandit.readthedocs.io/en/1.7.5/plugins/b101_assert_used.html Location: sql\crud.py:134:12 133 u = (await session.execute(select(User).where(User.username == user_old.username))).scalar_one_or_none() 134 assert u is not None 135 return UpdateSuccess.from_User(u, "更新成功") -------------------------------------------------- Code scanned: Total lines of code: 1604 Total lines skipped (#nosec): 0 Run metrics: Total issues (by severity): Undefined: 0 Low: 2 Medium: 1 High: 3 Total issues (by confidence): Undefined: 0 Low: 1 Medium: 0 High: 5 Files skipped (0): (fastapipg-3.11) 24123@lolik D:\24123\code\python\fastapipg git:use-pdm ~5 >
共有5个问题
Issue0
是说Use of weak MD5 hash for security
这里把文件md5作为文件名并没有什么问题,还能避免文件重复
Issue1
Possible SQL injection vector through string-based query construction
存在sql注入,这里的sql语句是写死的字符串,且只会在初始化时运行,用来判断数据库是否存在,若不存在则自动创建
Issue2,3
Starting a process with a shell, possible injection detected
在开发时用到,生产环境可以直接删除,用来在服务器运行同时打开浏览器,编译前端代码
Issue4,5
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
断言应该删除,debug时遗留的问题
代码仓库:https://github.com/2412322029/fastapi_test#readme
/docs 目录为对应api文档
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。