为什么需要用户设置和 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(智元界) 原创发布