已部署到线上(frontend-only)。本提交把工作区里已上线但未提交的实现固化为分支基线。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
76 lines
3.8 KiB
Markdown
76 lines
3.8 KiB
Markdown
# 登录 + 开发者入口门(MySQL 后端)
|
||
|
||
两道关卡,都由后端 Rust(`Server/src/auth.rs`)实现,**默认实时查 MySQL**:
|
||
|
||
1. **入口门(藏门)**:起始页没有任何登录 UI 入口(设置齿轮已移除)。只有访问
|
||
`http://<host>/#<hash>` 且 `<hash>` 命中后端 `dev_gate` 表,前端才放行到登录页。
|
||
2. **登录(身份)**:登录页提交用户名 + 密码,后端按 `ccuser` 表里的 SHA256 校验。
|
||
|
||
> 入口门只是「把门藏起来」的共享口令,**不是身份凭证**;真正鉴权是第 2 步的登录。
|
||
|
||
> 例外入口:地址栏 `#photo` 是写死的公开入口,**不走入口门**直接显示登录页(去掉顶部
|
||
> 负载栏),登录成功进照片墙。详见 [photo-wall.md](photo-wall.md)。第 2 步登录仍是同一套。
|
||
|
||
## 请求流
|
||
|
||
```
|
||
浏览器 #<hash>
|
||
└─ POST /api/web/gate { hash } → DevGate.allows() 查 dev_gate 表 → 命中才显示登录页
|
||
登录页提交
|
||
└─ POST /api/web/login { username, pwd } → UserStore.verify() 查 ccuser 表 → 200 + token / 401
|
||
```
|
||
|
||
- 前端:`Client/src/api/gate.ts`(`checkGate`)、`Client/src/App.tsx`(监听 `hashchange`、防竞态、
|
||
回起始页时抹掉地址栏 hash)、登录 `Client/src/api/auth.ts`。
|
||
- 后端:`Server/src/auth.rs`、路由 `Server/src/routes/web.rs` 的 `/api/web/gate`、`/api/web/login`。
|
||
|
||
## 存储:MySQL(不缓存,按需查库)
|
||
|
||
机器内存紧张,所以**不把整表读进内存**:`UserStore` 与 `DevGate` 都是
|
||
`Db(pool) | Memory(兜底)` 两态,DB 模式下每次校验跑一条索引查询。
|
||
二者**共用同一个 sqlx 懒连接池**(`auth::db_pool_from_env()`,max 5 连接),避免开两份连接。
|
||
|
||
- 连接串来自环境变量 `DATABASE_URL`(线上写在 `/opt/internet-project/weather.env`):
|
||
`mysql://KarCharon:<password>@127.0.0.1:3306/InternetProject`
|
||
- **未设 `DATABASE_URL`** → 各自回退:用户用内置写死表(仅 `cc`)、入口门读 `GATE_FILE`
|
||
(默认 `dev_gate.txt`)再退内置。方便本地无 DB 时开发。
|
||
- 依赖:`sqlx 0.8`,features `runtime-tokio, tls-rustls, mysql, macros`。
|
||
|
||
### 表结构(库 `InternetProject`,utf8mb4)
|
||
|
||
```sql
|
||
CREATE TABLE ccuser (
|
||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||
username VARCHAR(64) NOT NULL UNIQUE,
|
||
password_sha256 CHAR(64) NOT NULL, -- SHA256(明文) 的小写 hex
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE TABLE dev_gate (
|
||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||
gate_hash VARCHAR(128) NOT NULL UNIQUE, -- 与前端 URL `#` 后那串原样匹配
|
||
label VARCHAR(64) DEFAULT NULL, -- 备注(开发者名)
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
```
|
||
|
||
- 加用户:`INSERT INTO ccuser (username, password_sha256) VALUES ('名', SHA2('明文',256));`
|
||
- 加开发者入口:`INSERT INTO dev_gate (gate_hash, label) VALUES ('<hash>', '名');`
|
||
(DB 模式即时生效,无需重启。)
|
||
- 当前 `cc`:登录密码 `192118Lht`;入口 hash `0086e83c9b10`(= 该密码 SHA256 的前 12 位)。
|
||
|
||
## 一次性搭库(服务器本机,MySQL 仅监听 127.0.0.1)
|
||
|
||
```sql
|
||
CREATE DATABASE InternetProject CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||
CREATE USER 'KarCharon'@'localhost' IDENTIFIED WITH mysql_native_password BY '<password>';
|
||
GRANT ALL PRIVILEGES ON InternetProject.* TO 'KarCharon'@'localhost';
|
||
-- 然后建上面两张表、插入初始数据。
|
||
```
|
||
|
||
## 安全注记(待办)
|
||
|
||
- 密码是**裸 SHA256(无盐)**,仅保证「不落明文」,不抗暴力破解。对外网开放前应换
|
||
Argon2/bcrypt 这类慢哈希。
|
||
- 登录返回的 `token` 目前**没有任何接口校验**,受保护路由实际尚未真正鉴权(见
|
||
`issue_token` 注释)。加鉴权中间件时再赋予其真实含义。
|