3 编写插件权限
原文链接: https://tauri.app/learn/security/writing-plugin-permissions/
本练习的目标是更好地理解在编写自己的插件时如何创建插件权限。
完成后,你将有能力为自己的插件创建简单的权限。
你还会得到一个示例 Tauri 插件,其中的权限部分是自动生成的,部分是手工编写的。
1. 创建一个 Tauri 插件
在我们的示例中,我们将借助 Tauri 的 cli 来引导生成 Tauri 插件源代码结构。
请确保已安装所有前置条件,并通过运行 cargo tauri info 确认你的 Tauri CLI 版本正确。
输出应当显示 tauri-cli 版本为 2.x。
本分步说明中我们使用 pnpm,但你可以选择其它包管理器并在命令中相应替换。
安装好较新的版本后,你就可以使用 Tauri CLI 创建插件了。
1
2
3
4
5
6
| cd tauri-learning
cargo tauri plugin new test
cd tauri-plugin-test
pnpm install
pnpm build
cargo build
|
2. 创建一个新命令
为了演示一些实用而简单的东西,假设我们的命令把用户输入写入临时文件夹中的文件,同时给文件加上一些自定义头信息。
我们把命令命名为 write_custom_file,在 src/commands.rs 中实现它,并把它加入插件 builder 以暴露给前端。
Tauri 的核心工具会为这个命令自动生成 allow 和 deny 权限,因此我们不必操心这一点。
命令实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| use tauri::{AppHandle, command, Runtime, Manager};
use crate::models::*;
use crate::Result;
use crate::TestExt;
#[command]
pub(crate) async fn ping<R: Runtime>(
app: AppHandle<R>,
payload: PingRequest,
) -> Result<PingResponse> {
app.test1().ping(payload)
}
#[command]
pub(crate) async fn write_custom_file<R: Runtime>(
user_input: String,
app: AppHandle<R>,
) -> Result<String> {
std::fs::write(app.path().temp_dir().unwrap(), user_input)?;
Ok("success".to_string())
}
|
为你新增的命令自动生成内置权限:
1
| const COMMANDS: &[&str] = &["ping", "write_custom_file"];
|
这些内置权限会由 Tauri 构建系统自动生成,并出现在 permissions/autogenerated/commands 文件夹中。
默认会创建一个 enable-<command> 和一个 deny-<command> 权限。
3. 暴露新命令
上一步是编写实际的命令实现。
接下来我们要把它暴露给前端以便使用。
配置 Tauri builder 生成 invoke handler,把前端的 IPC 请求转给刚实现的命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("test")
.invoke_handler(tauri::generate_handler![
commands::ping,
commands::write_custom_file,
])
.setup(|app, api| {
#[cfg(mobile)]
let test = mobile::init(app, api)?;
#[cfg(desktop)]
let test = desktop::init(app, api)?;
app.manage(test);
// 托管 state,使命令可以访问它
app.manage(MyState::default());
Ok(())
})
.build()
}
|
在前端模块中暴露新命令。
这一步对示例应用成功导入前端模块是必需的。这只是为了方便,没有安全影响,因为命令处理器已经生成,命令也可以从前端手动调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
| import { invoke } from '@tauri-apps/api/core'
export async function ping(value: string): Promise<string | null> {
return await invoke<{value?: string}>('plugin:test|ping', {
payload: {
value,
},
}).then((r) => (r.value ? r.value : null));
}
export async function writeCustomFile(user_input: string): Promise<string> {
return await invoke('plugin:test|write_custom_file',{userInput: user_input});
}
|
提示
invoke 参数需要是 CamelCase。在这个示例中是 userInput 而不是 user_input。
确保你的包已构建:
pnpm build
4. 定义默认插件权限
由于我们的插件应当默认暴露 write_custom_file 命令,我们应当把它加入 default.toml 权限。
把这个加入我们的默认权限集,以允许我们刚暴露的新命令。
1
2
3
| [default]
description = "Default permissions for the plugin"
permissions = ["allow-ping", "allow-write-custom-file"]
|
5. 从示例应用调用测试命令
生成的插件目录结构包含一个 examples/tauri-app 文件夹,其中有一个可直接使用的 Tauri 应用来测试该插件。
由于我们新增了命令,需要稍微修改前端,改为调用我们的新命令。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| import Greet from './lib/Greet.svelte'
import { ping, writeCustomFile } from 'tauri-plugin-test-api'
let response = ''
function updateResponse(returnValue) {
response += `[${new Date().toLocaleTimeString()}]` + (typeof returnValue === 'string' ? returnValue : JSON.stringify(returnValue)) + '<br>'
}
function _ping() {
ping("Pong!").then(updateResponse).catch(updateResponse)
}
function _writeCustomFile() {
writeCustomFile("HELLO FROM TAURI PLUGIN").then(updateResponse).catch(updateResponse)
}
</script>
<main class="container">
<h1>Welcome to Tauri!</h1>
<div class="row">
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo vite" alt="Vite Logo" />
</a>
<a href="https://tauri.app" target="_blank">
<img src="/tauri.svg" class="logo tauri" alt="Tauri Logo" />
</a>
<a href="https://svelte.dev" target="_blank">
<img src="/svelte.svg" class="logo svelte" alt="Svelte Logo" />
</a>
</div>
<p>
Click on the Tauri, Vite, and Svelte logos to learn more.
</p>
<div class="row">
<Greet />
</div>
<div>
<button on:click="{_ping}">Ping</button>
<div>{@html response}</div>
</div>
<div>
<button on:click="{_writeCustomFile}">Write</button>
<div>{@html response}</div>
</div>
</main>
<style>
.logo.vite:hover {
filter: drop-shadow(0 0 2em #747bff);
}
.logo.svelte:hover {
filter: drop-shadow(0 0 2em #ff3e00);
}
</style>
|
运行它并点击 “Write” 按钮,你应该会看到:
success
并且你会在临时文件夹中找到一个 test.txt 文件,其中包含来自我们新实现的插件命令的消息。
🥳