远程访问认证
CountBot 支持通过非本地 IP 远程访问 Web UI,同时通过强制认证系统保证安全性。
::: warning 桌面预编译版限制
桌面预编译版(如 .exe 安装包)不支持通过环境变量修改监听地址,无法开启远程访问模式。
如需远程访问,请使用源码版本。 :::
概述
CountBot 默认通过 127.0.0.1:8000 本地访问,无需登录认证。当通过非本地 IP 访问时,系统自动启用认证。
核心安全特性
- 首次设置管理员账号只能在服务所在机器本地完成,或通过
/setup/<随机码>远程初始化 - 密码要求:至少 8 位,包含大写字母、小写字母和数字
- 登录态通过 Cookie
CountBot_token维持 - 本地访问 (
127.0.0.1) 不受影响 - 渠道消息(微信/飞书/钉钉等)不受影响
本地 vs 远程检测
本地请求判定
以下来源视为本地请求,无需登录:
127.0.0.1localhost::1(IPv6)0.0.0.0
远程请求判定
除本地请求外,所有其他来源均视为远程请求,需要登录认证。
注意:通过反向代理访问时,代理会转发
X-Forwarded-For头,系统据此识别客户端真实 IP,不会视为本地请求。
首次设置
首次设置页面
首次访问时,系统自动跳转到 /first-setup 页面,要求设置:
- 管理员账号:用户名,建议使用邮箱地址
- 管理员密码:至少 8 位,包含大写字母、小写字母和数字
- 确认密码:与管理员密码一致
设置流程
用户访问 /first-setup
↓
验证密码规则(长度 ≥ 8,包含大写、小写、数字)
↓
保存凭据到数据库
↓
设置登录 Cookie
↓
跳转到 / 页面
安全机制
- 密码在数据库中加密存储(bcrypt)
- 首次设置完成后,设置页面不再显示
- 管理员凭据用于后续远程访问登录
远程初始化
场景
当 CountBot 部署在远程服务器,无法直接本地访问时,可通过 /setup/<随机码> 完成首次设置。
初始化流程
python start_app.py
↓
生成 8 位随机字母码
↓
打印启动信息(包含 setup URL)
↓
等待 15 秒
↓
打开浏览器(本地)
打印内容示例:
远程首次初始化入口:将此路径拼接到上方 Network 地址后访问
(有效期 30 分钟,初始化成功后立即失效)
→ /setup/akLRzvlW
安全机制
- 一次性验证:首次使用后立即删除随机码
- 随机性:8 位字母组合,足够抵抗普通扫描
- 超时保护:默认 30 分钟,超时后自动失效
- 本地优先:本地访问不受限制
示例
# 启动 CountBot(开启远程访问)
COUNTBOT_HOST=0.0.0.0 python start_app.py
# 输出包含 setup URL,拼接 Network 地址访问
# 例如:http://192.168.1.100:8000/setup/akLRzvlW
# 在浏览器中打开该 URL
# 设置管理员账号和密码
# 设置完成后使用 /login 页面登录
登录系统
登录页面
远程访问时,系统自动跳转到 /login 页面。
登录流程
用户访问远程页面
↓
检测需要登录
↓
重定向到 /login?redirect=<原URL>
↓
用户输入凭据
↓
验证成功:设置 Cookie 并跳转到原页面
验证失败:显示错误信息
Cookie 机制
- Cookie 名称:
CountBot_token - 有效期:365 天
- 安全属性:
Secure=False,HttpOnly=True,SameSite=Lax - 路径:
/
登出
点击页面右上角的 "Logout" 按钮即可登出。登出后 Cookie 被清除,下次访问远程页面时需要重新登录。
认证中间件
实现
认证通过 FastAPI 中间件实现,对所有非本地请求进行检查。
绕过页面
以下页面无需登录:
/login- 登录页面/first-setup- 首次设置页面/setup/*- 远程初始化页面/api/login- 登录 API/api/first-setup- 首次设置 API/api/setup/init- 远程初始化 API/api/setup/verify- 远程初始化验证 API/health- 健康检查/api/health- 健康检查 API/ws- WebSocket 连接(由独立逻辑处理)
WebSocket 认证
WebSocket 连接通过 token 查询参数传递认证令牌:
ws://example.com/ws?token=<CountBot_token>
前端在连接时自动附加 Cookie 中的令牌。
远程初始化安全
随机码生成
使用 secrets.token_hex(32) 生成 64 位随机码,密码学安全。
数据库存储
随机码加密存储在 settings 表中:
| key | value | encrypted |
|---|---|---|
setup_init_code_hash | bcrypt.hash(随机码) | True |
验证流程
GET /setup/<随机码>
↓
从数据库读取 setup_init_code_hash
↓
bcrypt.checkpw(随机码, hash)
↓
验证成功:渲染设置页面,存储 setup_code_verified=true
验证失败:返回 404
一次性使用
验证成功后随机码从数据库删除:
del settings["setup_init_code_hash"]
del settings["setup_init_code_verified"]
密码安全
密码规则
- 最小长度:8 位
- 必须包含:大写字母、小写字母、数字
- 正则:
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$
密码存储
密码使用 bcrypt 哈希:
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
hashed_password = pwd_context.hash(password)
密码验证
pwd_context.verify(password, hashed_password)
环境变量
认证系统不使用专用环境变量。本地/远程检测基于请求来源 IP。
如需修改服务监听地址和端口:
| 变量 | 默认值 | 说明 |
|---|---|---|
COUNTBOT_HOST | 127.0.0.1 | 监听地址 |
COUNTBOT_PORT | 8000 | 监听端口 |
详见部署与运维。
渠道访问
渠道消息通过独立 API 路由处理,不受远程访问认证影响:
/api/telegram/webhook/api/feishu/webhook/api/dingtalk/webhook/api/wecom/webhook/api/weibo/webhook/api/xiaozhi/webhook/api/qq/webhook/api/wechat/webhook
这些路由由 ChannelManager 注册,在认证中间件检查前直接返回。
故障排查
首次设置页面不显示
- 确保数据库为空
- 检查数据库文件
data/countbot.db
登录失败
- 确认管理员账号和密码正确
- 检查浏览器 Cookie 是否被阻止
远程初始化 URL 未显示
- 检查启动日志
- 确保数据库为空且未设置管理员
安全最佳实践
- 完成远程初始化后立即删除启动日志中的 setup URL
- 不要在公开渠道分享 setup URL 或管理员凭据
- 使用反向代理时配置 HTTPS
- 定期更新 CountBot 到最新版本
- 设置强密码并妥善保管
相关文件
| 文件 | 说明 |
|---|---|
backend/middleware/auth.py | 认证中间件 |
backend/api/auth.py | 认证 API |
backend/database.py | 数据库配置 |
backend/app.py | 应用初始化 |
start_app.py | 启动脚本 |