<!-- title: 给首个项目装权限围栏 -->

## 概述

本教程面向**首次把 Claude Code 引入真实代码仓库**的零基础开发者。学完之后，你将能够：理解 Claude Code 的三层权限模型（模式 + 规则 + 钩子），读懂并写出 `allow`/`deny`/`ask` 规则，为你的第一个项目配置一份"只放行本地构建、禁止推送与外发网络"的安全围栏，并掌握在多档权限模式之间自如切换的方法。适用人群：刚完成安装、能跑通 `claude` 命令，但还没敢把它指向自己项目根目录的开发者。

## 前置条件

- 已通过 `npm install -g @anthropic-ai/claude-code` 完成安装，Node.js ≥ 18
- 已完成登录认证（`claude` 首次启动可走通任意一种登录方式）
- 手头有一个可以"被折腾"的本地项目目录（建议是 git 仓库，方便回滚）
- 一个文本编辑器，能编辑 JSON 文件

如果你还没装好，先回去完成安装与登录再回来——本教程不重复安装步骤，专注**配置**这一最容易踩坑、也最该在"第一个项目"阶段就做对的环节。

## 正文

### 步骤 1：先建立心智模型——三层权限体系

很多初学者一上来就问"我该把哪个开关关掉"，结果越改越乱。先记住一张三层图：

1. **权限模式（mode）**：决定"基线"。当前模式下哪些动作默认免提示。
2. **权限规则（rules）**：在基线之上叠加"白名单/黑名单/必问清单"。
3. **钩子（hooks）**：在工具调用前后注入脚本，做最细粒度的拦截与审计。

这三层**由 Claude Code 强制执行，不是由模型自觉**。你在 `CLAUDE.md` 里写"请不要删文件"没有任何约束力，真正起作用的是 `deny` 规则和 `PreToolUse` 钩子。理解这一点，后面的配置才不会南辕北辙。

### 步骤 2：认识六种权限模式，知道每档"放行"到哪

模式决定了"Claude 不问你就能做的事"边界。这是你给项目选基线的第一道选择题：

| 模式 | 免提示可做的事 | 适用场景 |
|---|---|---|
| `default` | 仅只读操作（读文件、grep） | 入门、敏感操作 |
| `acceptEdits` | 只读 + 文件编辑 + 常见文件系统命令（`mkdir`/`touch`/`mv`/`cp`） | 你正盯着看的代码迭代 |
| `plan` | 仅只读（只读探查，不改源码） | 动手前先摸清仓库 |
| `auto` | 几乎全部，带后台安全校验（研究预览） | 长任务、减少审批疲劳 |
| `dontAsk` | 仅预先批准过的工具 | 锁定的 CI 与脚本 |
| `bypassPermissions` | 全部 | **仅限隔离容器/虚拟机** |

一条贯穿所有模式的安全底线：**除了 `bypassPermissions`，对"受保护路径"（`.git`、`.claude`、`.config/git`、`.vscode`、`.idea`、`.husky` 等）的写入永远不会被自动批准**。这条底线是为了防止 Claude 误改自己的配置或仓库状态。而 `deny` 规则和显式 `ask` 规则在**任何模式**（包括 `bypassPermissions`）下都生效——这就是你"围栏"的真正钉子。

**常见问题**：新手容易把 `acceptEdits` 当成"全自动"。注意它**仍然会在执行 shell 命令（如 `npm run build`）时停下来问你**，除非你用规则预先放行。

### 步骤 3：在会话中切换模式，并把默认值持久化

切换模式**不是靠在聊天里打字**，而是用专门的控制：

- **会话中**：在 CLI 里按 `Shift+Tab` 循环 `default → acceptEdits → plan`，当前模式显示在状态栏。VS Code/Desktop 点输入框底部的模式指示器即可。
- **启动时**：用标志位指定，例如 `claude --permission-mode plan`。
- **持久默认**：在 `settings.json` 写入：

```json
{
  "permissions": {
    "defaultMode": "acceptEdits"
  }
}
```

有三档**默认不在循环里**：`auto`（需账号满足条件，首次会弹 opt-in）、`bypassPermissions`（需 `--permission-mode bypassPermissions` 或 `--dangerously-skip-permissions` 启动）、`dontAsk`（永不进循环，只能用 `--permission-mode dontAsk` 启动）。

**预期结果**：按 `Shift+Tab`，状态栏文字应从 `Ask before edits`（default）变成 `Edit automatically`（acceptEdits）再变成 `Plan mode`（plan）。

**常见问题**：`defaultMode: "auto"` 写在**项目/本地**配置里会被忽略——`auto` 只接受写在**用户级**配置里的默认值。

### 步骤 4：动手写规则——`Tool(specifier)` 语法详解

规则统一遵循 `Tool` 或 `Tool(specifier)` 两种形式。求值顺序固定为 **`deny` → `ask` → `allow`，首个匹配决定结果，规则的"具体程度"不会改变顺序**。这意味着一条宽泛的 `deny`（如 `Bash(aws *)`）会压倒更窄的 `allow`（如 `Bash(aws s3 ls)`）——deny 不能被 allow 开口子。

**匹配整类工具**：直接写工具名，不带括号。`Bash` 匹配所有 shell 命令；作为 `deny` 规则时，它会把该工具从 Claude 的上下文里**整个移除**（Claude 根本看不到它能跑 bash）。

**精细匹配**：括号内写 specifier。示例：

```
Bash(npm run build)       # 精确匹配该命令
Read(./.env)              # 匹配读取当前目录的 .env
WebFetch(domain:example.com)  # 匹配对该域名的抓取
```

**通配符 `*`**：Bash 规则支持 glob，`*` 可在任意位置，单个 `*` 可跨多个参数。

```json
{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git commit *)",
      "Bash(* --version)"
    ],
    "deny": [
      "Bash(git push *)"
    ]
  }
}
```

**最容易踩的两个坑**：

1. **空格敏感**：`Bash(ls *)` 匹配 `ls -la` 但**不匹配** `lsof`；而 `Bash(ls*)` 两者都匹配。对话框里选"Yes, don't ask again"生成的是带空格形式。末尾的 `:*`（如 `Bash(ls:*)`）等价于带空格的尾通配，但**只**在结尾有效——写在中间（如 `Bash(git:* push)`）会被当字面冒号。
2. **不能用参数语法堵 bash**：`Bash(command:rm *)` 是**可绕过**的（复合命令能逃逸），Claude Code 会忽略它并报警。正确写法是 `Bash(rm *)`。

**MCP 工具的匹配**：deny/ask 可用 `mcp__*` 一键禁用所有 MCP 工具；allow 则必须在 `mcp__<服务器名>__` 前缀之后才支持 glob，如 `mcp__github__get_*`。未锚定的 allow glob（如 `*`、`mcp__*`）会被跳过并警告。

### 步骤 5：摸清 `settings.json` 的三级位置与优先级

权限规则可以分散在多个配置文件里，它们会**合并**生效。从最高到最低：

| 级别 | 路径 | 特点 |
|---|---|---|
| 托管/企业（managed） | 系统级，由 IT 下发 | **最高，不可被覆盖**，可锁定模式 |
| 用户（user） | `~/.claude/settings.json` | 你的全局默认 |
| 项目（project） | `<项目>/.claude/settings.json` | 随仓库走，可提交版本控制，团队共享 |
| 本地（local） | `<项目>/.claude/settings.local.json` | 个人覆盖，建议加入 `.gitignore` |

**求值与合并**：各级的 `allow`/`ask`/`deny` 会合并；同一调用按 `deny→ask→allow` 顺序首次匹配定胜负。托管层有两个"杀手锏"开关：`permissions.disableBypassPermissionsMode: "disable"` 禁用越权模式，`permissions.disableAutoMode: "disable"` 禁用 auto 模式——这两个最适合放在托管层，因为**那里才无法被下属层级覆盖**。

**预期结果**：运行 `claude` 后输入 `/permissions`，会列出所有生效规则并标注它来自哪个 settings 文件。这是排查"为什么这条规则没生效"的第一手段。

**常见问题**："我在项目里写了 allow，为什么不生效？" 多半是用户级或托管级有一条更宽的 `deny` 命中了同一调用。用 `/permissions` 逐条核对来源即可定位。

### 步骤 6：用 `dontAsk` 给"脚本化"项目上锁

如果你的第一个项目其实是**一个会被 CI 反复调用的脚本**，你想要的不是"少打扰"，而是"只准干我批准过的事"。这正是 `dontAsk` 的舞台：它**自动拒绝**一切未预先批准的工具。配合 `claude -p "..." --permission-mode dontAsk`，能保证无人值守时 Claude 绝不越界。

**预期结果**：在 dontAsk 下，任何不在 allow 列表里的工具调用都会被静默拒绝，任务会因"工具不可用"而中止或改道，但**绝不会**偷偷执行。

## 实战案例：给一个 Node 项目配"本地构建、禁止外发"的围栏

假设你的项目 `~/my-app`，目标：允许 Claude 自由读代码、自由跑本地构建与测试、自由编辑工作区文件，但**禁止 `git push`、禁止任何网络外发抓取、禁止删除仓库外的目录**。

**第 1 步**，创建项目级配置：

```bash
mkdir -p ~/my-app/.claude
```

编辑 `~/my-app/.claude/settings.json`：

```json
{
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": [
      "Bash(npm run *)",
      "Bash(npm test *)",
      "Bash(npm install)",
      "Bash(node *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(git add *)",
      "Bash(git commit *)"
    ],
    "deny": [
      "Bash(git push *)",
      "WebFetch",
      "Bash(rm -rf /*)",
      "Bash(curl *)",
      "Read(./.env)",
      "Read(./.env.*)"
    ]
  }
}
```

逐条解读：`defaultMode` 设为 `acceptEdits` 让文件编辑免提示，但 shell 命令仍需批准——`allow` 列表把构建/测试/git 读取放行；`deny` 里 `Bash(git push *)` 拦住误推送，`WebFetch`（裸工具名）把网络抓取能力整体摘除，`Read(./.env)` 保护本地密钥，`rm -rf /*` 兜底防止误删。

**第 2 步**，把个人偏好隔离开，避免污染团队配置：

```json
// ~/my-app/.claude/settings.local.json （加入 .gitignore）
{
  "permissions": {
    "allow": ["Bash(pnpm *)"]
  }
}
```

并把 `settings.local.json` 写进 `.gitignore`。

**第 3 步**，启动并验证：

```bash
cd ~/my-app
claude
```

进入会话后输入 `/permissions`，应看到三组规则分别标注来源 `project` 与 `local`。

**第 4 步**，跑第一个任务验证围栏是否就位：

```
> 帮我跑一遍测试，然后把当前改动提交成一个 fix: 开头的 commit
```

预期：`npm test` 与 `git commit` 因命中 allow 而**免提示**直接执行；若你随后追加一句"顺便推到远端"，`git push` 会因命中 deny 而**被拦截**——围栏生效。

## 工作流程图

```mermaid
flowchart TD
    A[Claude 发起一次工具调用] --> B{命中 deny 规则?}
    B -- 是 --> X[❌ 拒绝<br/>任何模式都拦]
    B -- 否 --> C{命中 ask 规则?}
    C -- 是 --> Y[⏸ 弹窗确认]
    C -- 否 --> D{命中 allow 规则?}
    D -- 是 --> Z[✅ 免提示执行]
    D -- 否 --> E{当前模式放行?}
    E -- default/plan --> W[⏸ 弹窗确认]
    E -- acceptEdits & 文件操作 --> Z
    E -- bypassPermissions --> Z
    E -- dontAsk --> X
    Y -- 用户批准 --> Z
    Y -- 用户拒绝 --> X
    W -- 用户批准 --> Z
    W -- 用户拒绝 --> X
```

核心规律：**deny 永远最先判，且无法被 allow 翻盘**；模式只在"没有任何规则命中"时才决定是否需要问你。

## 延伸阅读

- **官方 · Configure permissions**：`code.claude.com/docs/en/permissions`——规则语法的权威来源，含 Bash/Read/Edit/WebFetch/MCP 各工具的完整 specifier 列表。
- **官方 · Choose a permission mode**：`code.claude.com/docs/en/permission-modes`——六档模式的取舍建议与各平台切换方式。
- **官方 · Claude Code settings**：`code.claude.com/docs/en/settings`——`defaultMode`、`disableBypassPermissionsMode` 等字段的完整定义。
- **进阶 · Hooks 拦截**：把"装围栏"升级为"装闸机"，用 `PreToolUse` 钩子做脚本化审计与改写。

## 训练营适用性评估

- **适合类型**：面向"工程化使用 Claude Code"的企业内训、开发者入门营、DevSecOps 安全主题营。不适合纯 API 调用营（那是 SDK 议题）。
- **教学时长**：建议 1.5–2 小时。前 30 分钟讲三层模型与六档模式（演示 `Shift+Tab` 循环），中间 45 分钟带学员动手写 `allow`/`deny` 并用 `/permissions` 核对，最后 30–45 分钟跑通实战案例、故意触发 deny 观察"被拦"现象。
- **难度门槛**：低。只要会改 JSON 即可，但讲师需重点演示"空格敏感""deny 压倒 allow"这两个高频踩坑点。
- **考核建议**：给学员一个"故意写错"的 settings.json（例如 `Bash(command:rm *)`），要求其诊断为何不生效并改正——这比让 ta 默写语法更能检验理解深度。

## 参考来源

1. [Configure permissions — Claude Code Docs](https://code.claude.com/docs/en/permissions) — 官方权威：规则语法、求值顺序（deny→ask→allow）、各工具 specifier 与通配符行为。
2. [Choose a permission mode — Claude Code Docs](https://code.claude.com/docs/en/permission-modes) — 官方权威：六档模式对照表、各平台切换方式、受保护路径与 dontAsk/bypass 说明。
3. [Claude Code Permissions: Allow Lists, Deny Rules, and Sandboxing (2026)](https://www.claudedirectory.org/blog/claude-code-permissions-guide) — 实践向博客：allow/deny 规则与沙箱结合的落地指南。
4. [Claude Code Permission Modes: Default, acceptEdits, Bypass — explainx](https://www.explainx.ai/blog/claude-code-permission-modes-explained-2026) — 模式取舍的场景化解读，补充官方表格之外的选择建议。
5. [Permission Rule Syntax — codingnomads](https://codingnomads.com/claude-permission-rule-syntax) — 细粒度权限规则的语法拆解，含大量可复制示例。

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
