桌面客户端登录流程技术设计文档

创建:xiaozi · 最后修改:xiaozi 2025-06-01 18:13 ·

一、概述

本技术设计文档详细描述了基于 Tauri 和 tiny-http 库的 Yogu 客户端登录流程实现方案,涵盖了客户端与 Yogu 服务器之间的交互逻辑、授权流程以及数据存储机制。目标是提供一个安全、高效的用户登录体验,确保用户能够在 yogu.pro 平台完成授权后,客户端能够正确处理回调并完成登录。

二、系统架构

2.1 系统组件

  • 客户端:基于 Tauri 开发的桌面应用程序,负责启动本地 HTTP 服务(使用 tiny-http)、处理用户授权回调并存储用户凭证。
  • Yogu 服务器:运行于 yogu.pro 的后端服务,负责用户身份验证和授权。
  • 浏览器:用户通过浏览器访问 yogu.pro 完成授权操作。

2.2 技术栈

  • 前端:Tauri(基于 Rust 和 WebView),HTML/JavaScript
  • 后端:Tauri 提供的 Rust 后端,使用 tiny-http 库运行本地 HTTP 服务
  • 通信协议:HTTP/HTTPS
  • 数据存储:本地文件系统或安全存储(如 keyring
  • 依赖库
    • tiny-http:用于实现轻量级本地 HTTP 服务
    • rand:用于生成随机端口
    • ed25519-dalekring:用于生成公私钥对
    • keyring:用于安全存储用户凭证

三、登录流程设计

3.1 流程概览

  1. 用户点击客户端的“登录”按钮。
  2. 客户端使用 tiny-http 启动本地 HTTP 服务,监听 127.0.0.1 上的随机端口。
  3. 客户端生成公钥(用于安全通信),并在浏览器中打开授权链接,引导用户完成授权。
  4. 用户在 yogu.pro 完成授权后,服务器通过回调将 user_idaccess_token 返回到客户端的本地 HTTP 服务。
  5. 客户端接收回调,存储用户凭证,并跳转到成功页面。

3.2 流程时序图

以下是登录流程的 Mermaid 时序图,描述了各组件之间的交互:

---
config:
  look: handDrawn
  theme: redux-color
---
sequenceDiagram
    participant 用户
    participant 客户端
    participant 本地HTTP服务
    participant 浏览器
    participant Yogu服务器

    用户->>客户端: 点击“登录”按钮
    客户端->>本地HTTP服务: 启动服务(tiny-http),监听127.0.0.1:{随机端口}
    客户端->>客户端: 生成公私钥对
    客户端->>浏览器: 打开授权URL: https://yogu.pro/auth/login/native_app?port={port}&public_key={public_key}
    浏览器->>Yogu服务器: 访问授权页面
    Yogu服务器->>用户: 显示登录页面
    用户->>Yogu服务器: 输入凭证并授权
    Yogu服务器->>浏览器: 重定向到 http://127.0.0.1:{port}/?user_id=xxx&access_token=xxx
    浏览器->>本地HTTP服务: 发送回调请求
    本地HTTP服务->>客户端: 传递 user_id 和 access_token
    客户端->>客户端: 存储凭证到本地
    客户端->>本地HTTP服务: 关闭服务
    客户端->>浏览器: 打开成功页面: https://yogu.pro/auth/login/native_app_succeeded
    浏览器->>Yogu服务器: 访问成功页面
    Yogu服务器->>浏览器: 返回成功页面
    客户端->>用户: 更新UI,显示登录成功

3.3 详细步骤

3.3.1 点击“登录”按钮

  • 触发条件:用户在客户端 UI 中点击“登录”按钮。
  • 操作
    • 客户端调用 Tauri 后端接口,使用 tiny-http 启动本地 HTTP 服务。
    • 选择一个随机端口(范围:1024-65535),确保不与常用端口冲突。
    • 生成一对公私钥,用于后续可能的加密通信(公钥将发送至服务器)。
  • 技术实现
    • 使用 tiny-httpServer::http 方法启动本地 HTTP 服务。
    • 使用 Rust 的 rand 库生成随机端口。
    • 使用 ed25519-dalekring 算法生成公私钥对。

3.3.2 打开授权链接

  • 操作
    • 客户端构造授权 URL,格式为:

      https://yogu.pro/auth/login/native_app?native_app_port={port}&native_app_public_key={public_key}
      
      • {port}:本地 HTTP 服务监听的端口号。
      • {public_key}:客户端生成的公钥(Base64 编码)。
    • 使用 Tauri 的 tauri::api::shell::open 方法在默认浏览器中打开该 URL。

  • 安全考虑
    • 确保 public_key 已正确编码,避免 URL 注入风险。
    • 使用 HTTPS 协议与 yogu.pro 通信,确保数据传输安全。

3.3.3 用户授权

  • 操作

    • 用户在浏览器中访问 yogu.pro 的授权页面,输入凭证(如用户名/密码)或通过第三方登录(如 OAuth)。

    • yogu.pro 验证用户身份,生成 user_idaccess_token

    • 服务器通过 HTTP 重定向将以下参数返回到客户端的本地 HTTP 服务:

      http://127.0.0.1:{port}/?user_id={user_id}&access_token={access_token}
      
  • 技术细节

    • yogu.pro 需支持标准的 OAuth 2.0 授权码流程。
    • 重定向 URL 使用客户端提供的 native_app_port 动态构造。

3.3.4 客户端处理回调

  • 操作
    • 本地 HTTP 服务(tiny-http)监听到 GET 请求,解析 URL 中的 user_idaccess_token 参数。
    • 验证参数完整性(确保 user_idaccess_token 不为空)。
    • user_idaccess_token 存储到本地安全存储中(如使用 keyring 或加密后的本地文件)。
    • 关闭本地 HTTP 服务,释放端口。
  • 技术实现
    • 使用 tiny-httpRequest 对象解析查询参数。
    • 使用 Rust 的 serde 库进行参数序列化/反序列化。
    • 使用 keyring 库或加密文件存储用户凭证。
  • 安全考虑
    • 验证回调请求的来源,确保来自 127.0.0.1
    • access_token 进行加密存储,防止泄露。

3.3.5 跳转到成功页面

  • 操作
    • 客户端在浏览器中打开成功页面:

      https://yogu.pro/auth/login/native_app_succeeded
      
    • 客户端更新 UI,显示登录成功状态。

  • 技术实现
    • 使用 Tauri 的 tauri::api::shell::open 打开成功页面。
    • 通过 Tauri 的前端事件机制通知 UI 更新状态。

四、技术实现细节

4.1 本地 HTTP 服务

  • 实现方式
    • 使用 tiny-http 库实现轻量级 HTTP 服务。
    • 服务仅监听 127.0.0.1,防止外部访问。
  • 端口选择
    • 动态选择可用端口,避免冲突。
    • 使用 std::net::TcpListener 检查端口可用性,若端口被占用则重试。
  • 请求处理
    • 监听 GET / 路径,解析查询参数 user_idaccess_token
    • 返回 HTTP 200 响应,表示接收成功。
  • 依赖
    • Cargo.toml 中添加依赖:

      [dependencies]
      tiny-http = "0.12"
      

4.2 公私钥生成

  • 算法:推荐使用 ed25519(更高效)或 RSA-2048
  • 用途
    • 公钥用于标识客户端,发送至 yogu.pro
    • 私钥保留在客户端,用于后续可能的签名验证。
  • 实现
    • 使用 ed25519-dalekring 库生成密钥对。
    • 公钥以 Base64 编码格式嵌入到授权 URL 中。
  • 依赖
    • Cargo.toml 中添加依赖:

      [dependencies]
      ed25519-dalek = "2"
      

4.3 数据存储

  • 存储方式
    • 使用 keyring 库将 user_idaccess_token 存储到系统密钥管理器。
    • 备选方案:将凭证加密后存储到本地文件(使用 AES-256 加密)。
  • 存储内容
    • user_id:用户唯一标识符(字符串)。
    • access_token:授权令牌(字符串)。
    • 可选:存储 refresh_token(若 yogu.pro 支持)。
  • 依赖
    • Cargo.toml 中添加依赖:

      [dependencies]
      keyring = "2"
      

4.4 错误处理

  • 端口占用
    • 若随机端口被占用,尝试下一个可用端口。
    • 最大重试次数:5 次。
  • 授权失败
    • yogu.pro 返回错误,客户端捕获错误并显示提示。
    • 使用 Tauri 的前端事件机制通知 UI 显示错误信息。
  • 回调失败
    • 若本地 HTTP 服务未收到回调,设置超时(默认 5 分钟)并提示用户重试。
  • 存储失败
    • 若凭证存储失败,记录日志并提示用户。

五、安全考虑

  1. 通信安全
    • 使用 HTTPS 协议与 yogu.pro 通信。
    • 本地 HTTP 服务仅监听 127.0.0.1,防止外部访问。
  2. 凭证安全
    • access_token 加密存储,避免明文泄露。
    • 使用系统密钥管理器(如 keyring)存储敏感数据。
  3. 参数验证
    • 验证回调 URL 的参数格式和完整性。
    • 确保公钥和端口号正确编码,避免注入攻击。
  4. 超时处理
    • 本地 HTTP 服务设置超时(通过 tiny-http 的配置),防止长时间占用资源。

六、伪代码示例

6.1 启动本地 HTTP 服务

use tiny_http::{Server, Response};
use rand::Rng;
use std::net::TcpListener;

fn start_local_server() -> Result<(u16, Server), Box<dyn std::error::Error>> {
    let mut port = rand::thread_rng().gen_range(1024..65535);
    let mut server = None;

    for _ in 0..5 {
        if TcpListener::bind(format!("127.0.0.1:{}", port)).is_ok() {
            server = Some(Server::http(format!("127.0.0.1:{}", port))?);
            break;
        }
        port = rand::thread_rng().gen_range(1024..65535);
    }

    server.ok_or("Failed to bind port".into()).map(|s| (port, s))
}

6.2 构造授权 URL 并打开

use tauri::api::shell;

fn open_auth_url(port: u16, public_key: String) {
    let url = format!(
        "https://yogu.pro/auth/login/native_app?native_app_port={}&native_app_public_key={}",
        port, public_key
    );
    shell::open(&url).expect("Failed to open browser");
}

6.3 处理回调

use tiny_http::{Request, Response};
use url::Url;
use std::io::Cursor;

fn handle_callback(mut request: Request) -> Response<Cursor<Vec<u8>>> {
    let url = Url::parse(&format!("http://127.0.0.1{}", request.url())).expect("Invalid URL");
    let query: HashMap<_, _> = url.query_pairs().collect();

    let user_id = query.get("user_id").expect("Missing user_id").to_string();
    let access_token = query.get("access_token").expect("Missing access_token").to_string();

    // 存储凭证
    store_credentials(&user_id, &access_token).expect("Failed to store credentials");

    // 返回成功响应
    Response::from_string("Callback received").with_status_code(200)
}

6.4 主流程(监听请求)

use tiny_http::Server;
use std::io::Cursor;

fn run_server(server: Server) {
    for request in server.incoming_requests() {
        if request.url().starts_with("/") {
            let response = handle_callback(request);
            request.respond(response).expect("Failed to respond");
            break; // 处理一次请求后退出
        }
    }
}

6.5 跳转到成功页面

fn redirect_to_success() {
    shell::open("https://yogu.pro/auth/login/native_app_succeeded")
        .expect("Failed to open success page");
}

6.6 完整流程

fn login_flow() -> Result<(), Box<dyn std::error::Error>> {
    // 启动本地 HTTP 服务
    let (port, server) = start_local_server()?;

    // 生成公私钥
    let (public_key, _private_key) = generate_key_pair();

    // 打开授权 URL
    open_auth_url(port, public_key);

    // 运行服务器,等待回调
    run_server(server);

    // 跳转到成功页面
    redirect_to_success();

    Ok(())
}

七、测试计划

7.1 单元测试

  • 测试端口选择逻辑,确保 tiny-http 能绑定到随机端口。
  • 测试公私钥生成,确保公钥正确编码。
  • 测试回调参数解析,确保 user_idaccess_token 正确提取。

7.2 集成测试

  • 模拟 yogu.pro 的授权流程,验证客户端能否正确接收回调。
  • 测试凭证存储功能,确保数据安全保存。
  • 测试错误场景(如端口占用、授权失败)。

7.3 端到端测试

  • 启动客户端,完成完整登录流程,验证 UI 更新和成功页面跳转。
  • 测试超时场景,确保客户端正确处理。

八、未来扩展

  1. 支持刷新令牌:若 yogu.pro 返回 refresh_token,客户端可实现自动刷新 access_token
  2. 多用户支持:支持多个用户凭证存储,允许切换用户。
  3. 日志记录:增加详细的日志记录,便于调试和错误追踪。
  4. 跨平台兼容性:确保流程在 Windows、macOS 和 Linux 上稳定运行。

浏览 2015 次 · 下载PDF

Home - Wiki
Copyright © 2011-2025 iteam. Current version is 2.144.1. UTC+08:00, 2025-07-18 13:36
浙ICP备14020137号-1 $Map of visitor$