桌面客户端登录流程技术设计文档
内容目录
一、概述
本技术设计文档详细描述了基于 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-dalek
或ring
:用于生成公私钥对 -
keyring
:用于安全存储用户凭证
-
三、登录流程设计
3.1 流程概览
- 用户点击客户端的“登录”按钮。
- 客户端使用
tiny-http
启动本地 HTTP 服务,监听127.0.0.1
上的随机端口。 - 客户端生成公钥(用于安全通信),并在浏览器中打开授权链接,引导用户完成授权。
- 用户在
yogu.pro
完成授权后,服务器通过回调将user_id
和access_token
返回到客户端的本地 HTTP 服务。 - 客户端接收回调,存储用户凭证,并跳转到成功页面。
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),确保不与常用端口冲突。
- 生成一对公私钥,用于后续可能的加密通信(公钥将发送至服务器)。
- 客户端调用 Tauri 后端接口,使用
-
技术实现:
- 使用
tiny-http
的Server::http
方法启动本地 HTTP 服务。 - 使用 Rust 的
rand
库生成随机端口。 - 使用
ed25519-dalek
或ring
算法生成公私钥对。
- 使用
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_id
和access_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_id
和access_token
参数。 - 验证参数完整性(确保
user_id
和access_token
不为空)。 - 将
user_id
和access_token
存储到本地安全存储中(如使用keyring
或加密后的本地文件)。 - 关闭本地 HTTP 服务,释放端口。
- 本地 HTTP 服务(
-
技术实现:
- 使用
tiny-http
的Request
对象解析查询参数。 - 使用 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 更新状态。
- 使用 Tauri 的
四、技术实现细节
4.1 本地 HTTP 服务
-
实现方式:
- 使用
tiny-http
库实现轻量级 HTTP 服务。 - 服务仅监听
127.0.0.1
,防止外部访问。
- 使用
-
端口选择:
- 动态选择可用端口,避免冲突。
- 使用
std::net::TcpListener
检查端口可用性,若端口被占用则重试。
-
请求处理:
- 监听
GET /
路径,解析查询参数user_id
和access_token
。 - 返回 HTTP 200 响应,表示接收成功。
- 监听
-
依赖:
-
在
Cargo.toml
中添加依赖:[dependencies] tiny-http = "0.12"
-
4.2 公私钥生成
-
算法:推荐使用
ed25519
(更高效)或RSA-2048
。 -
用途:
- 公钥用于标识客户端,发送至
yogu.pro
。 - 私钥保留在客户端,用于后续可能的签名验证。
- 公钥用于标识客户端,发送至
-
实现:
- 使用
ed25519-dalek
或ring
库生成密钥对。 - 公钥以 Base64 编码格式嵌入到授权 URL 中。
- 使用
-
依赖:
-
在
Cargo.toml
中添加依赖:[dependencies] ed25519-dalek = "2"
-
4.3 数据存储
-
存储方式:
- 使用
keyring
库将user_id
和access_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 分钟)并提示用户重试。
-
存储失败:
- 若凭证存储失败,记录日志并提示用户。
五、安全考虑
-
通信安全:
- 使用 HTTPS 协议与
yogu.pro
通信。 - 本地 HTTP 服务仅监听
127.0.0.1
,防止外部访问。
- 使用 HTTPS 协议与
-
凭证安全:
-
access_token
加密存储,避免明文泄露。 - 使用系统密钥管理器(如
keyring
)存储敏感数据。
-
-
参数验证:
- 验证回调 URL 的参数格式和完整性。
- 确保公钥和端口号正确编码,避免注入攻击。
-
超时处理:
- 本地 HTTP 服务设置超时(通过
tiny-http
的配置),防止长时间占用资源。
- 本地 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_id
和access_token
正确提取。
7.2 集成测试
- 模拟
yogu.pro
的授权流程,验证客户端能否正确接收回调。 - 测试凭证存储功能,确保数据安全保存。
- 测试错误场景(如端口占用、授权失败)。
7.3 端到端测试
- 启动客户端,完成完整登录流程,验证 UI 更新和成功页面跳转。
- 测试超时场景,确保客户端正确处理。
八、未来扩展
-
支持刷新令牌:若
yogu.pro
返回refresh_token
,客户端可实现自动刷新access_token
。 - 多用户支持:支持多个用户凭证存储,允许切换用户。
- 日志记录:增加详细的日志记录,便于调试和错误追踪。
- 跨平台兼容性:确保流程在 Windows、macOS 和 Linux 上稳定运行。
浏览 2015 次 · 下载PDF
禁止转载