# Go Blog 一个简洁、快速的博客引擎,使用 Go + Gin + Tailwind CSS 构建。支持 SQLite 和 MySQL,首次运行自动完成配置。 ## 功能特性 - **文章管理** — 文章的创建、编辑、删除,支持标签分类、自定义发布时间和更新时间 - **Markdown 渲染** — 文章与评论支持 GFM Markdown(表格、任务列表、删除线),代码块语法高亮 + 一键复制,标题锚点链接,图片懒加载与灯箱预览 - **评论系统** — 文章评论功能,后台可审核(通过/拒绝/删除),支持评论设置 - **用户系统** — 用户注册、登录,角色分为管理员(admin)和作者(author) - **角色权限** — 管理员可访问后台管理面板;作者可管理自己的文章 - **个人中心** — 上传头像(支持裁剪)、编辑个人信息、修改密码 - **站点设置** — 自定义站点标题、副标题、favicon 图标 - **导航管理** — 自定义首页导航链接,支持图标 - **瀑布流布局** — 首页文章以瀑布流展示,支持无限滚动加载 - **阅读统计** — 文章阅读量统计,带机器人流量检测 - **附件上传** — 文章支持上传附件,基于内容寻址自动去重 - **RSS 订阅** — 自动生成 RSS Feed(`/rss`、`/feed`),链接使用站点设置的规范地址 - **搜索功能** — 文章全文搜索 - **多语言** — 支持中文 / English,自动检测浏览器语言或手动切换 - **自适应界面** — Tailwind CSS,桌面端和移动端均可正常使用 - **安全加固** — 会话密钥加密随机、Cookie HttpOnly/SameSite=Lax/HTTPS Secure、全站 CSRF、SQL 注入防护(参数化+路由 ID 数值化)、`/uploads` 白名单挂载(SQLite 数据库不可下载)、登录限速(IP+用户名,5 次失败锁 15 分钟)、附件越权校验、上传安全(危险扩展黑名单+magic-bytes 检测+头像 JPEG 重编码)、Gravatar 默认关闭、禁用用户会话实时失效,完整清单见 [SECURITY_TODO.md](./SECURITY_TODO.md) - **开箱即用** — 首次运行自动创建配置文件、数据库;管理员账号密码为随机生成并仅一次性打印(不再使用 admin/admin) ## 技术栈 | 层级 | 库/工具 | |---|---| | 路由 | [gin](https://github.com/gin-gonic/gin) | | ORM | [gorm](https://gorm.io) | | SQLite | [glebarez/sqlite](https://github.com/glebarez/sqlite)(纯 Go,无需 CGO) | | Session | [gin-contrib/sessions](https://github.com/gin-contrib/sessions) | | 配置 | [gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) | | 密码 | [golang.org/x/crypto](https://pkg.go.dev/golang.org/x/crypto/bcrypt) | | 文件校验 | [gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype) | | CSS | [Tailwind CSS](https://tailwindcss.com)(构建期静态生成,见 `scripts/build_tailwind.sh`) | ## 快速开始 ```bash go run . ``` 打开 登录。首次运行时会自动创建管理员账号 `admin`,初始密码为**随机生成**并仅一次性打印在日志中——请立即记录并登录后修改(SECURITY_TODO #12,不再使用默认 admin/admin)。 > 前端资源全部本地化(`static/css/app.css` 为 Tailwind 静态构建产物,已提交)。修改 HTML 模板/Go 代码中的 Tailwind 类后,运行 `./scripts/build_tailwind.sh`(需 Node ≥ 18,npx 可用)重新生成并提交新产物;vendor 库更新同理重新下载到 `static/vendor/` 并提交。 > 安全状态一览见下方 [安全加固](#安全加固) 章节,完整修复清单与验证记录见 [SECURITY_TODO.md](./SECURITY_TODO.md)。 ## 配置 首次运行时,配置文件会自动生成: | 平台 | 配置文件路径 | 数据存储路径 | |---|---|---| | Linux | `/etc/blog_go/config.yaml` | `/srv/blog_go/` | | Windows | `./win/etc/blog_go/config.yaml` | `./win/srv/blog_go/` | ```yaml # config.yaml database: type: sqlite # sqlite(默认)或 mysql dsn: "" # MySQL 连接串,sqlite 模式下忽略 web: port: "8080" # Web 服务端口,"" 或 "0" 可只启用 socket socket: "" # unix socket 路径(Linux 部署推荐,见 install_linux.sh) trusted_proxies: # 可信反向代理 IP/CIDR;直接影响 X-Forwarded-For - 127.0.0.1 # 仅列表内的代理可设置客户端 IP(防 XFF 伪造) - ::1 path: ./win/srv/blog_go # 数据存储路径(数据库、上传文件) secret: <自动生成> # Session 加密密钥;缺失时拒绝启动 ``` ### 使用 MySQL 编辑 `config.yaml`: ```yaml database: type: mysql dsn: user:password@tcp(127.0.0.1:3306)/blog_go?charset=utf8mb4&parseTime=True&loc=Local web: port: "8080" ``` 先创建数据库: ```sql CREATE DATABASE blog_go CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` ## 项目结构 | 目录 | 作用 | |---|---| | `config/` | 配置加载与自动创建(区分 Linux/Windows 路径) | | `models/` | 数据模型:用户、文章、标签、评论、附件、阅读统计、站点设置等 + GORM 初始化 | | `handlers/` | HTTP 请求处理:首页、文章、登录/注册、评论、后台管理、个人中心、RSS、上传校验 | | `middleware/` | 中间件:登录/角色鉴权、CSRF、HTTPS 检测、安全响应头(CSP/HSTS 等) | | `i18n/` | 中英文翻译与浏览器语言检测 | | `templates/` | HTML 模板:layouts 公共布局、pages 前台、admin 后台、user 用户文章 | | `static/` | 静态资源:Tailwind 产物、前端 Markdown 渲染器、vendor 本地化第三方库(marked、highlight.js 等) | | `scripts/` | 构建辅助脚本(Tailwind 重建、数据库迁移 SQL) | 根目录文件:`main.go`(入口 + 路由注册)、`main_test.go` 与 `handlers/_test.go`(测试)、`fresh.conf`(fresh 热重载配置)、`install_linux.sh`(Linux 部署脚本)。逐文件说明: ``` go_blog/ ├── main.go # 入口,路由注册,中间件装配 ├── config/ │ └── config.go # 配置加载 / 自动创建(区分操作系统) ├── models/ │ ├── user.go # 用户模型 + bcrypt │ ├── article.go # 文章模型 │ ├── article_tag.go # 文章-标签关联 │ ├── article_view.go # 文章阅读统计 │ ├── attachment.go # 附件模型 │ ├── bot_detector.go # 机器人流量检测 │ ├── comment.go # 评论模型 │ ├── comment_config.go # 评论配置 │ ├── config_cache.go # 平台配置缓存 │ ├── db.go # GORM 初始化、自动迁移 │ ├── nav_link.go # 导航链接模型 │ ├── seed.go # 初始数据填充 │ ├── site_setting.go # 站点设置模型 │ ├── tag.go # 标签模型 │ └── upload_config.go # 上传配置 ├── middleware/ │ ├── auth.go # 登录验证、角色鉴权、语言检测 │ ├── csrf.go # CSRF 同步器令牌防护 │ ├── https.go # 请求是否 HTTPS 检测(cookie Secure) │ └── security_headers.go # 安全响应头(CSP、HSTS、nosniff 等) ├── handlers/ │ ├── helpers.go # 公共工具函数 │ ├── home.go # 首页 │ ├── article.go # 文章详情、文章列表 API │ ├── auth.go # 登录 │ ├── login_ratelimit.go # 登录限速(IP+用户名,5 次失败锁定 15 分钟) │ ├── admin.go # 管理后台首页 │ ├── admin_comment.go # 评论管理 │ ├── admin_user.go # 用户管理 │ ├── admin_analytics.go # 阅读统计 │ ├── attachment.go # 附件上传/删除 │ ├── comment.go # 评论提交 │ ├── my_articles.go # 用户文章管理 │ ├── profile.go # 个人中心 │ ├── rss.go # RSS Feed │ ├── settings.go # 站点/导航/上传/评论设置 │ └── upload_validator.go # 上传文件校验(扩展名白名单 + magic-bytes) ├── i18n/ │ └── i18n.go # 中英文翻译映射 + Accept-Language 检测 ├── static/ │ ├── css/app.css # Tailwind 静态构建产物(go:embed 编入二进制) │ ├── css/input.css # Tailwind 构建输入(@tailwind 指令) │ ├── css/markdown.css # Markdown 排版样式(go:embed 编入二进制) │ ├── js/markdown.js # 前端 Markdown 渲染器(BlogMD) │ └── vendor/ # 本地化的第三方前端库(marked/DOMPurify/highlight.js/cropperjs/easymde) ├── scripts/ │ └── build_tailwind.sh # 重新生成 Tailwind CSS(需 Node,见下) ├── templates/ │ ├── layouts/base.html # 公共布局(导航栏 + 头像下拉菜单 + 页脚) │ ├── pages/ │ │ ├── home.html # 首页(瀑布流 + 无限滚动) │ │ ├── article.html # 文章详情页 │ │ ├── login.html # 登录页 │ │ ├── register.html # 注册页 │ │ ├── profile.html # 个人中心 │ │ ├── search.html # 搜索页 │ │ └── 404.html # 404 页面 │ ├── admin/ │ │ ├── dashboard.html # 管理后台首页 │ │ ├── article_list.html # 文章列表 │ │ ├── article_create.html# 文章编辑 │ │ ├── comment_list.html # 评论审核 │ │ ├── user_list.html # 用户列表 │ │ ├── user_form.html # 用户表单 │ │ ├── analytics_views.html # 阅读统计 │ │ ├── settings_site.html # 站点设置 │ │ ├── settings_navlinks.html # 导航链接设置 │ │ ├── settings_upload.html # 上传设置 │ │ ├── settings_download.html # 下载设置 │ │ └── settings_comment.html # 评论设置 │ └── user/ │ ├── my_articles.html # 我的文章列表 │ └── my_article_form.html # 我的文章编辑 └── win/ # 运行时数据目录(Windows,自动创建) ├── etc/blog_go/ │ └── config.yaml └── srv/blog_go/ ├── blog.db ├── attachments/ ├── avatars/ └── logos/ ``` ## 安全加固 基于 2026-08-19 安全审计与网络上线验证(共 25 项发现,P0–P3 均已修复,详见 [SECURITY_TODO.md](./SECURITY_TODO.md)): - **认证与会话** - 会话密钥由 `crypto/rand` 生成 32 字节随机数;配置文件缺失 secret 时拒绝启动(不再静默回退) - Cookie 加固:`HttpOnly` + `SameSite=Lax`,HTTPS 下自动加 `Secure` - 全站 CSRF 防护(同步器令牌,30+ 表单与 AJAX 全覆盖);登录/注册成功强制会话轮换(防会话固定) - 登录限速:按 IP+用户名 5 次失败锁定 15 分钟;用户不存在时也执行 bcrypt 比较,抹平计时侧信道 - 禁用/锁定/软删用户的会话实时失效;管理员口令 bcrypt cost 12 - **数据与注入** - GORM 全参数化查询;管理路由的 `:id` 先解析为数值再入查询(防字符串条件注入) - `/uploads` 仅白名单挂载子目录,SQLite 数据库文件不可从公网下载;目录列表与路径穿越一律 404 - **上传安全** - 扩展名白名单 + 危险扩展名黑名单(.html/.svg/.js 等,防同源 Active Content) - 内容 magic-bytes 与声明类型一致性校验(gabriel-vasile/mimetype) - 头像强制解码→256px 缩放→JPEG 重编码后落盘,原始字节一律不落地 - **输出与传输** - CSP(`default-src 'self'`,第三方前端资源已本地化)、`X-Frame-Options: DENY`、`X-Content-Type-Options: nosniff`、`Referrer-Policy`、`Permissions-Policy`;HTTPS 下发 HSTS - 客户端 IP 解析仅信任 `web.trusted_proxies` 名单内代理(防 X-Forwarded-For 伪造);RSS 链接使用站点设置的规范地址(防 Host 头污染) 安全回归用例 50+(`handlers/*_test.go`、`main_test.go`、`middleware/*_test.go`),`go test -race ./...` 全绿。 ## 路由 ### 公开路由 | 方法 | 路径 | 说明 | |---|---|---| | GET | `/` | 首页 | | GET | `/search` | 搜索页 | | GET | `/article/:slug` | 文章详情 | | GET | `/rss`、`/feed` | RSS 订阅 | | GET | `/login` | 登录页 | | GET | `/register` | 注册页 | | GET | `/uploads/*` | 静态文件(头像、附件等) | ### 公开 JSON API(`/api` 前缀) | 方法 | 路径 | 说明 | |---|---|---| | GET | `/api/articles` | 文章列表 API(无限滚动) | | POST | `/api/auth/login` | 登录(限流锁定 429) | | POST | `/api/auth/register` | 注册(用户名冲突 409) | | POST | `/api/auth/logout` | 退出登录 | | POST | `/api/article/:slug/comments` | 发表评论(校验错误 400) | ### 管理后台(需管理员权限) | 方法 | 路径 | 说明 | |---|---|---| | GET | `/admin` | 管理后台首页 | | GET | `/admin/articles` | 文章列表 | | GET | `/admin/articles/new` | 新建文章表单 | | GET | `/admin/articles/:id/edit` | 编辑文章表单 | | GET | `/admin/comments` | 评论管理 | | GET | `/admin/users` | 用户列表 | | GET | `/admin/users/new` | 新建用户表单 | | GET | `/admin/users/:id/edit` | 编辑用户表单 | | GET | `/admin/analytics/views` | 阅读统计 | | GET | `/admin/settings/site`、`/navlinks`、`/upload`、`/download`、`/comments` | 各设置页 | 管理 JSON API(仅管理员,均带 JSON 错误码 + HTTP 语义状态码): | 方法 | 路径 | 说明 | |---|---|---| | POST | `/api/admin/articles` | 创建文章 | | PUT/DELETE | `/api/admin/articles/:id` | 更新/删除文章 | | POST | `/api/admin/articles/attachments` | 上传附件(multipart) | | DELETE | `/api/admin/articles/attachments/:id` | 删除附件 | | GET | `/api/admin/articles/:id/attachments` | 附件列表 | | POST | `/api/admin/comments/:id/approve`、`/:id/reject`、`/:id/delete` | 评论审核操作 | | POST/PUT/DELETE | `/api/admin/users`、`/:id` | 用户增改删(自禁/最后管理员均 403) | | POST | `/api/admin/settings/site` | 站点设置(文本/URL/清除) | | POST | `/api/admin/settings/site/favicon`、`/site/logo` | favicon/logo 上传(multipart) | | POST | `/api/admin/settings/navlinks`、`/upload`、`/download`、`/comments` | 各设置保存(`action` 分发) | ### 个人中心(需登录) | 方法 | 路径 | 说明 | |---|---|---| | GET | `/profile` | 编辑个人信息页 | | POST | `/api/profile` | 保存资料(JSON,密码改密校验 400) | | POST | `/api/profile/avatar` | 上传头像(multipart、JPEG 重编码) | | GET | `/my/articles` | 我的文章列表 | | GET | `/my/articles/new`、`/:id/edit` | 我的文章表单页 | | POST/PUT/DELETE | `/api/my/articles`、`/:id` | 文章增改删(跨作者 404/403) | | POST | `/api/my/articles/attachments` | 上传附件(multipart) | | DELETE | `/api/my/articles/attachments/:id` | 删除附件 | | GET | `/api/my/articles/:id/attachments` | 附件列表 | ### API 约定 - 成功:`{"ok": true, "redirect": "...", "data": {...}}`(`redirect` 为原 302 目标,前端 fetch 后跳转) - 失败:`{"ok": false, "code": "", "error": "<按请求语言翻译的文案>"}`;状态码:400 校验失败 / 401 未登录 / 403 无权限 / 404 不存在 / 409 冲突 / 429 限流锁定 / 500 服务错误 - 除文件上传(multipart)外请求体为 `application/json`;CSRF 通过 `X-CSRF-Token` 请求头传递 ## 用户角色 | 角色 | 个人中心 | 我的文章 | 管理后台 | | --- | --- | --- | --- | | `admin` | ✓ | ✓ | ✓ | | `author` | ✓ | ✓ | ✗ | ## 许可证 MIT