首页 / Tauri 2 入门教程 / 权限与能力最佳实践

Tauri 2 入门教程

权限与能力最佳实践

本教程共 48 篇 · 第 41 篇 · 更新于 2026-08-09 · 约 10 分钟阅读

TauriTauri 2 入门教程权限capabilitiespermissionsscope最小授权安全

本节目标:读完你能写出规范的 capabilities 配置文件,理解 permission 的粒度层次,会用 scope 把权限锁到最小作用域,并掌握按需授权的实战原则。

前面几章我们一直在用各种插件,每次都要在 capabilities/default.json 里加权限。但一直没有系统讲过这套权限体系怎么设计、怎么配、怎么配才安全。这一章把 permissions、capabilities 和 scope 三者串起来讲清楚,给你一套可落地的最佳实践。

三个核心概念的关系

先理清三个容易混的概念:

  • Permission(权限):描述「能不能用某个命令」。比如 fs:allow-read-file 是一个权限,允许前端调用读文件命令。
  • Scope(作用域):描述「能用这个命令操作哪些资源」。比如有了 fs:allow-read-file 还不够,还得限定只能读 $APPDATA 目录,这个路径限制就是 scope。
  • Capability(能力):把 permission 和 scope 打包,绑定到具体的窗口上。一个 capability 说的是「main 窗口有这些权限、这些 scope」。

打个比方:permission 是门禁卡上的一个权限项(能进大门),scope 是这个权限项的限制(只能进 3 楼),capability 是整张门禁卡(张三的卡,有这些权限项和限制,适用于这栋楼)。

capabilities 文件结构

Capability 文件放在 src-tauri/capabilities/ 目录下,JSON 格式。每个文件描述一组权限绑定到哪些窗口:

// src-tauri/capabilities/default.json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "主窗口的权限配置",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "core:window:allow-set-title",
    "fs:default",
    {
      "identifier": "fs:allow-read-file",
      "allow": [{ "path": "$APPDATA/**" }]
    },
    {
      "identifier": "fs:allow-write-file",
      "allow": [{ "path": "$APPDATA/**" }]
    }
  ]
}

逐字段解释:

  • $schema:指向 Tauri 自动生成的 JSON Schema 文件,让 IDE 有自动补全。桌面应用用 desktop-schema.json,移动端用 mobile-schema.json
  • identifier:这个 capability 的唯一标识,随便取名,不重复就行。
  • windows:哪些窗口应用这套权限。["main"] 表示只给 label 为 main 的窗口。["*"] 表示所有窗口。
  • permissions:权限列表,可以是字符串(引用预定义权限)或对象(权限 + scope)。
Tip

src-tauri/capabilities/ 目录下的所有 .json 文件默认自动启用。如果你在 tauri.conf.jsonapp.security.capabilities 里显式指定了 capability 列表,则只有指定的生效。

permission 的粒度层次

Tauri 的权限不是非黑即白的「全开或全关」,而是有层次:

第一层:default 权限集

每个插件都有一个 default 权限集,包含一组「开箱即用」的基础权限。比如 fs:default 包含了所有读相关命令和 $APP 目录的 scope。这是最省事的用法,但安全性也最低——它开放了一整类操作。

"fs:default"

第二层:单命令权限

如果你只需要某个具体命令,不要用 default 全开,而是只授权那一个:

"fs:allow-write-text-file"

这比 fs:default 安全得多——前端只能写文件,不能读、不能删、不能监控。

第三层:权限 + scope

最精细的控制:只开一个命令,且限定操作作用域:

{
  "identifier": "fs:allow-write-text-file",
  "allow": [{ "path": "$HOME/test.txt" }]
}

这表示前端只能写 $HOME/test.txt 这一个文件,其他路径全拒绝。

Note

权限粒度从粗到细:plugin:defaultplugin:allow-<command>plugin:allow-<command> + scope。安全要求越高,粒度越细。实际开发中,用 default 配合 scope 是最常见的折中方案——既不用列一堆单命令权限,又把可操作作用域锁住了。

scope:路径和资源的精细控制

Scope 是 Tauri 2 权限体系里最有价值的特性之一。它让权限不再是「有/没有」的二元选择,而是「有,但只能碰这些」。

Scope 分 allowdeny 两个列表:

  • allow:允许访问的路径/资源
  • deny:明确禁止的路径/资源

deny 的优先级高于 allow。即使某个路径匹配了 allow,只要也进了 deny,运行时照样被挡。

{
  "identifier": "fs:allow-read-file",
  "allow": [{ "path": "$APPLOCALDATA/**" }],
  "deny": [{ "path": "$APPLOCALDATA/EBWebView/**" }]
}

这个配置表示:允许读 $APPLOCALDATA 下的所有文件,但排除 EBWebView 子目录——因为那里存着 WebView 的敏感数据。这种「允许大部分、排除小部分」的模式在实际中非常常用。

Scope 中的路径支持 glob 通配符:

  • *:匹配单层路径
  • **:递归匹配所有子目录
$APPDATA/*        → 只匹配 $APPDATA 下的直接子文件/子目录
$APPDATA/**       → 递归匹配 $APPDATA 下所有层级的文件和目录
$APPDATA/config/* → 匹配 $APPDATA/config/ 下的直接子文件

按窗口分权

不同窗口需要不同权限。比如主窗口需要文件读写,一个只读的预览窗口不需要写权限。你可以为不同窗口创建不同的 capability 文件:

// src-tauri/capabilities/main.json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    {
      "identifier": "fs:allow-write-file",
      "allow": [{ "path": "$APPDATA/**" }]
    }
  ]
}
// src-tauri/capabilities/preview.json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "preview-capability",
  "windows": ["preview"],
  "permissions": [
    "core:default",
    {
      "identifier": "fs:allow-read-file",
      "allow": [{ "path": "$APPDATA/**" }]
    }
  ]
}

主窗口能读写,预览窗口只能读。即使预览窗口被 XSS 攻击,攻击者也无法修改文件——因为它的权限集里根本没有写权限。

Warning

如果一个窗口同时被多个 capability 文件引用,它的有效权限是这些 capability 的并集,不是交集。比如窗口 main 出现在两个 capability 里,一个给了读权限一个给了写权限,那 main 窗口同时有读写权限。务必检查每个窗口最终合并后的权限集,避免意外叠加。

按平台分权

有些插件只在特定平台有意义。比如 nfc:allow-scan 在桌面平台没意义,global-shortcut 在移动端没意义。Capability 支持 platforms 字段做平台过滤:

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "desktop-capability",
  "windows": ["main"],
  "platforms": ["linux", "macOS", "windows"],
  "permissions": ["global-shortcut:allow-register"]
}
{
  "$schema": "../gen/schemas/mobile-schema.json",
  "identifier": "mobile-capability",
  "windows": ["main"],
  "platforms": ["iOS", "android"],
  "permissions": ["nfc:allow-scan", "biometric:allow-authenticate"]
}

这样桌面权限和移动权限互不干扰,也不会在错误平台上意外启用。

自定义权限集(Permission Set)

如果你发现自己反复写同一组权限,可以定义一个 permission set,把多个权限打包成一个新的标识符。在 src-tauri/permissions/ 目录下创建 TOML 文件:

# src-tauri/permissions/home-read-extends.toml
[[set]]
identifier = "allow-home-read-extended"
description = "允许在 $HOME 目录下非递归读取文件和创建目录"
permissions = [
    "fs:read-files",
    "fs:scope-home",
    "fs:allow-mkdir"
]

然后在 capability 里直接引用这个 set:

{
  "permissions": [
    "core:default",
    "allow-home-read-extended"
  ]
}

注意自定义权限集的 identifier 没有 fs: 前缀——因为它属于应用本身,不是插件的。

Note

权限文件(permissions)只能用 TOML 格式,capability 文件可以用 JSON 或 TOML。这是 Tauri 的硬性规定。

最小授权原则清单

把前面所有最佳实践浓缩成一份可执行的清单:

  1. 不用 * 通配权限:每个权限都要明确写出来,不要图省事用通配符放开所有命令。
  2. 优先用单命令权限:只用到一个命令就只开那一个,不要用 default 全开。
  3. 必须配 scope:授权文件操作类权限时,必须用 allow 限定可访问路径,不能让前端碰整个硬盘。
  4. 善用 deny:敏感子目录用 deny 显式排除,即使它被 allow 覆盖。
  5. 按窗口分权:不同窗口用不同 capability 文件,权限最小化。
  6. 检查并集:一个窗口出现在多个 capability 里时,合并后的权限可能比你预期的大,要逐一核查。
  7. 按平台过滤:桌面和移动端的权限用 platforms 分开,避免无意义的权限被启用。
  8. 定期审计:随着应用迭代,权限会只增不减。定期过一遍 capabilities 配置,删掉不再需要的权限。
Tip

Tauri 会自动生成 JSON Schema 文件(在 src-tauri/gen/schemas/ 目录下),其中包含所有可用的权限列表。在 capability 文件里设好 $schema 后,IDE 会自动补全可用的权限标识符,非常方便。不确定某个插件有哪些权限时,去看这个 schema 文件。

小结

Tauri 2 的权限体系由三层组成:permission(单条权限)、scope(作用域限制)、capability(权限与窗口的绑定)。写 capability 时,优先用单命令权限而非 default 全开,必须给文件操作类权限配 scope 限定路径,善用 deny 排除敏感子目录。多窗口应用要按窗口分权,不同平台用 platforms 过滤。反复使用的权限组合可以定义成 permission set 复用。核心原则只有一条:默认全拒绝,按需最小授权——每多开一个权限,都要问自己「这个权限真的需要吗?能不开就不开」。