Tauri 项目实践:客户端与 Web 端的授权登录实现方案
在跨平台应用开发中(如基于 Tauri 构建的 Mind Elixir 客户端),如何让应用从 Web 端顺畅地获取授权并完成登录往往是一个常见且重要的需求。本文将总结我们在这个 Tauri 项目中探索的两种登录实现方法,并分享一个在 macOS 上开发时遇到的非常经典的坑点。
在跨平台应用开发中(如基于 Tauri 构建的 Mind Elixir 客户端),如何让应用从 Web 端顺畅地获取授权并完成登录往往是一个常见且重要的需求。本文将总结我们在这个 Tauri 项目中探索的两种登录实现方法,并分享一个在 macOS 上开发时遇到的非常经典的坑点。
旧的登录方式:本地 HTTP Server 通信(遗留方案)
在项目最初,为了解决 Web 端把 Token 传回桌面端的痛点,我们采取了在本地启动 HTTP 服务器进行跨应用通信的方法:
实现原理
通过 Tauri 结合 Rust 的 axum 框架,桌面程序会在后台启动一个微型的本地服务器,监听特定端口(如 127.0.0.1:6595)。当用户在浏览器(如 cloud.mind-elixir.com)中登录完毕后,Web 页面直接向这个本地接口发出带上登录参数的 POST 请求:
// axum_router.rs
async fn login_handler(
headers: HeaderMap,
Query(params): Query<Params>,
handle_clone: tauri::AppHandle,
) -> impl IntoResponse {
let token = params.token;
// 收到 HTTP 请求后,向 Tauri 的前端触发全局 login 事件
let _ = handle_clone.emit("login", Login { token: token });
// ...处理 CORS 返回
}
接下来,React 前端监听这个全局事件,获取 Token 存入本地存储后即可完成登录:
// App.tsx
const unlisten = listen<{ token: string }>('login', async (e) => {
localStorage.setItem('token', e.payload.token)
await fetchData()
toast.success('登录成功')
})
优缺点
- 优点:实现逻辑简单粗暴,并且非常方便在开发环境(
tauri dev)下随意调试,无需进行系统级的协议注册。 - 缺点:这是一个相对较重的方案;需要额外占用用户电脑端口,偶发情况还可能受制于严格的浏览器跨域(CORS)策略或端口被占用从而导致通信失败;另外,该方案在移动端无法唤起应用窗口。
新的登录方式:自定义 Scheme / Deep Link
由于本地服务器面临上述潜在风险,并且不符合系统深层集成的新趋势,我们后续改用了更加优雅和原生的方案——自定义 Scheme 登录(如唤起 mind-elixir://)。
配置与实现
-
引入插件和配置: 启用
@tauri-apps/plugin-deep-link插件,并在tauri.conf.json下注册我们的特定协议头部mind-elixir:"plugins": { "deep-link": { // 移动端(iOS/Android)配置 "mobile": [ { "scheme": ["mind-elixir"], "appLink": false } ], // 桌面端配置 "desktop": { "schemes": ["mind-elixir"] } } }注:移动端配置生效同时依赖同步写入 Android 的
AndroidManifest.xml(<data android:scheme="mind-elixir" />) 与 iOS 的Info.plist(CFBundleURLSchemes) 中。 -
桌面系统唤醒支持: 在
src-tauri/src/lib.rs的初始化钩子处,我们需要给 Windows 和 Linux 用户调用显式的注册 API。#[cfg(any(windows, target_os = "linux"))] { use tauri_plugin_deep_link::DeepLinkExt; app.deep_link().register_all()?; } -
前端接收请求: 在收到协议请求(即网页重定向到了形如
mind-elixir://login?token=xxxx的长链接)时,使用 Tauri 的 API 解析深层链接并完成授权。既要负责冷启动阶段获取(getCurrent()),也要监控运行时唤醒(onOpenUrl):import { onOpenUrl, getCurrent } from '@tauri-apps/plugin-deep-link' import { getCurrentWindow } from '@tauri-apps/api/window' import { isMobile } from './utils/platform' // 项目中自定义的环境判断工具 const handleDeepLinkUrls = async (urls: string[]) => { if (!urls || urls.length === 0) return // 【各端表现差异处理】 // 移动端(尤其是 iOS/Android)点击 Deep Link 浏览器会自动切换/唤醒对应的 App 到前台。 // 但在桌面端接收到 deep link 事件后,应用窗口可能依然保持在后台,因此我们需要通过 window API 手动将其调出并聚焦。 if (!isMobile()) { const win = getCurrentWindow() await win.show() await win.setFocus() } // 检查数组中以特定协议开头的链接 const loginUrl = urls.find((url) => url.startsWith('mind-elixir://login')) if (loginUrl) { const url = new URL(loginUrl) const token = url.searchParams.get('token') if (token) { localStorage.setItem('token', token) await fetchData() toast.success('登录成功') } } } // 处理冷启动 getCurrent().then((urls) => { if (urls) handleDeepLinkUrls(urls) }) // 处理运行时被协议唤醒 onOpenUrl(handleDeepLinkUrls)
利用这种方式,用户在使用浏览器验证登陆态后,浏览器能顺滑提示是否打开目标应用,体验极佳。
避坑指南:macOS/Linux 的自定义 Scheme 调试限制
根据 Tauri 官方文档说明,在 macOS 和 Linux 系统下,Deep Link(自定义 Scheme)在开发模式(tauri dev)下是无法正常工作的。
如果你修改了基于 Scheme 登录的代码,请务必将其打包后(tauri build)运行该程序来进行测试。这同时也体现了第一种 HTTP 通信方案的一个优势——它可以在不用频繁打包的开发阶段充当最佳的调试通道。