为什么需要用户设置和 API Key 管理?
前两篇完成了对话界面,但用户还需要管理自己的账号信息和 API Key。这是任何 SaaS 产品的标配功能。
本篇回答三个问题:
1. 用户设置页面需要包含哪些功能?
2. API Key 怎么生成和管理才安全?
3. 用量统计怎么实现?
用户设置页面需要哪些功能?
基本信息编辑
用户登录后可以查看和编辑自己的昵称、头像、邮箱。
// frontend/src/pages/Settings.tsx
import { useState, useEffect } from "react";
import { useAuth } from "@/hooks/useAuth";
import api from "@/lib/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
export default function Settings() {
const { user, token } = useAuth();
const [nickname, setNickname] = useState(user?.nickname || "");
const [email, setEmail] = useState(user?.email || "");
const [saving, setSaving] = useState(false);
const [message, setMessage] = useState("");
const handleSave = async () => {
setSaving(true);
try {
await api.put("/auth/profile", { nickname, email });
setMessage("保存成功");
} catch (e: any) {
setMessage(e.response?.data?.detail || "保存失败");
}
setSaving(false);
setTimeout(() => setMessage(""), 3000);
};
return (
账号设置
基本信息
昵称
setNickname(e.target.value)} />
邮箱
setEmail(e.target.value)} />
{message && (
{message}
)}
{saving ? "保存中..." : "保存"}
);
}
修改密码
安全敏感操作需要独立的密码修改流程。
// 在 Settings.tsx 中增加密码修改卡片
修改密码
setOldPwd(e.target.value)} />
setNewPwd(e.target.value)} />
setConfirmPwd(e.target.value)} />
修改密码
后端需要验证旧密码正确性,再更新为新密码。
# backend/app/routers/auth.py(追加)
@router.put("/auth/password")
async def change_password(
body: ChangePasswordRequest,
user: User = Depends(require_auth),
db: AsyncSession = Depends(get_db),
):
"""修改密码。"""
if not verify_password(body.old_password, user.password_hash):
raise HTTPException(400, "当前密码错误")
if len(body.new_password) str:
"""生成 API Key:sk_前缀 + 32位随机字符串。"""
return f"sk_{secrets.token_hex(32)}"
def hash_api_key(key: str) -> str:
"""对 API Key 做单向哈希。"""
return hashlib.sha256(key.encode()).hexdigest()
def validate_api_key(key: str) -> str:
"""验证 API Key 格式。"""
if not key.startswith("sk_"):
raise ValueError("API Key 格式错误")
return hash_api_key(key)
API Key 管理页面
// frontend/src/pages/ApiKeys.tsx
import { useState, useEffect } from "react";
import api from "@/lib/api";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
interface ApiKey {
id: number;
name: string;
prefix: string;
last_used_at: string | null;
is_active: boolean;
created_at: string;
}
export default function ApiKeys() {
const [keys, setKeys] = useState([]);
const [name, setName] = useState("");
const [newKey, setNewKey] = useState("");
useEffect(() => { loadKeys(); }, []);
const loadKeys = async () => {
const { data } = await api.get("/api-keys");
setKeys(data);
};
const handleCreate = async () => {
const { data } = await api.post("/api-keys", { name });
setNewKey(data.key); // 只展示这一次
loadKeys();
setName("");
};
const handleDelete = async (id: number) => {
await api.delete(`/api-keys/${id}`);
loadKeys();
};
return (
API Key 管理
{/* 新建 Key */}
创建 API Key
setName(e.target.value)} />
创建
{/* 新 Key 提示(只出现一次) */}
{newKey && (
新 Key 已创建,请立即保存!
关闭后不会再显示完整 Key
)}
{/* Key 列表 */}
已有 Key
{keys.map((k) => (
{k.name}
{k.prefix}...
{k.last_used_at ? "最近使用" : "未使用"}
handleDelete(k.id)}>
删除
))}
);
}
API Key 认证中间件
# backend/app/services/api_key_auth.py
from app.services.api_key_service import hash_api_key
async def authenticate_by_api_key(request: Request):
"""API Key 认证中间件。"""
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer sk_"):
return None
key_hash = hash_api_key(auth_header[7:])
with models.get_db() as conn:
row = conn.execute(
"SELECT user_id FROM api_keys WHERE key_hash=? AND is_active=1",
(key_hash,)
).fetchone()
if row:
# 更新最后使用时间
conn.execute(
"UPDATE api_keys SET last_used_at=? WHERE key_hash=?",
(int(time.time()), key_hash)
)
conn.commit()
return row["user_id"]
return None
用量统计怎么实现?
统计什么
每个用户每天调用 API 的次数、消耗的 Token 数、提问数。
CREATE TABLE usage_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id),
date TEXT NOT NULL, -- 2026-06-18
api_calls INTEGER DEFAULT 0, -- API 调用次数
tokens_used INTEGER DEFAULT 0, -- 消耗 Token 数
queries INTEGER DEFAULT 0 -- 提问数
);
用量统计页面
// 在 Settings.tsx 中增加用量统计卡片
用量统计
{loading ? (
加载中...
) : (
{stats.api_calls}
API 调用
{stats.tokens_used}
Token 消耗
{stats.queries}
提问次数
)}
总结
功能和对应解决的问题:
| 功能 | 解决什么问题 |
|---|---|
| 修改昵称/邮箱 | 用户信息管理 |
| 修改密码 | 账号安全 |
| API Key 管理 | 第三方程序调用接口 |
| 用量统计 | 直观了解使用情况 |
下一篇将进入部署阶段——用 Docker 打包 KNow 并发布到服务器。
本文是 《AI 全栈开发实战——做一个真正的产品》 系列的第 9 篇。
本文由 Zyentor(智元界) 原创发布