首页 › tutorials › 权限配置入门:让Claude Code安全跑项目

Claude Code 权限系统入门:从第一次弹窗到 settings.json 完全配置

概述

本教程面向第一次启动 Claude Code、却被"是否允许执行此命令"弹窗反复打断的零基础用户。学完之后,你将理解 Claude Code 的三层权限模型(只读 / 命令 / 改文件)、掌握 allow / ask / deny 规则的写法与判定顺序、能区分六种权限模式,并能为一个真实项目写出一份"既能放手干活、又不会误删生产环境"的 settings.json 配置。适用人群:刚装好 Claude Code、准备跑第一个项目的开发者,以及需要在团队内统一权限策略的技术负责人。

前置条件


第一步:理解"三层工具,三种审批"的底层模型

很多人第一次用 Claude Code,会被弹窗搞得心烦,根本原因是没搞清楚它把工具分成了三类,每类的审批策略完全不同。Claude Code 官方文档把它定义为分级权限系统(tiered permission system):

工具类型 举例 是否需要审批 点击"Yes, don't ask again"后的效果
只读工具 文件读取(Read)、Grep、Glob 否 永不弹窗
Bash 命令 Shell 执行(npm test、git commit) 是 按"项目目录 + 命令前缀"永久记住
文件修改 Edit / Write 是 仅当前会话有效,退出即失效

这里有两个关键认知,直接决定你后续配置策略:

  1. Bash 命令的记忆是"按项目"的、且持久化。 当你在 ~/my-app 里对 npm run test 点了"以后别再问",这条规则会永久写进 ~/.claude.json(按目录分组),下次再进这个项目就不会问。但换到 ~/other-project,它又会重新问。
  2. 文件修改的"放行"只在会话内有效。 这是有意为之的安全设计——脚本可以跑很多次,但"让你改我的源码"这件事每次新会话都要重新确认,防止上下文跑偏后 Claude 在错误的项目里乱改。

理解这一点后,你的配置思路就清晰了:对高频、低风险的命令(测试、构建、提交)做 allow 永久放行;对文件修改用"会话放行"或 acceptEdits 模式;对高危操作(git push、删库、写 .env)用 deny 硬性封死。

第二步:用 /permissions 命令看清现有规则

不用手动翻配置文件,Claude Code 内置了一个交互式管理界面。在会话中输入:

/permissions

你会看到一个列表,列出当前生效的所有权限规则,并且标注每条规则来自哪个 settings.json 文件。这一点非常重要——当规则互相冲突时,你能立刻定位是哪一层加的规则在起作用。

列表里你会看到三类规则:

判定顺序是固定的,且不受规则"精确度"影响,顺序为:

deny  →  ask  →  allow

第一条命中的规则决定结果。这意味着:一条宽泛的 deny 规则 Bash(aws *) 会拦下所有匹配调用,哪怕你同时写了一条更窄的 allow 规则 Bash(aws s3 ls)——deny 规则无法带"白名单例外"。同理,ask 优先于 allow:只要有一条 ask 匹配,即使存在更精确的 allow,照样弹窗。

还有一个容易踩的细节:deny 规则的行为取决于它是否带了限定符。

第一条(裸名)适用于"我完全不希望 Claude 碰某类工具";第二条(带限定符)适用于"我希望 Claude 用 Bash,但别碰某几个危险命令"。

另一个必须牢记的认知:权限规则是由 Claude Code(外壳程序)强制执行的,不是由模型执行的。你在 CLAUDE.md 或提示词里写"不要执行 git push",只是影响 Claude 想做什么,并不能阻止它调用;真正能阻止的只有 permissions 规则、权限模式或 PreToolUse hook。

第三步:选择合适的权限模式(defaultMode)

权限模式是"全局基调开关",决定 Claude Code 默认怎么对待每一次工具调用。在 settings.json 里用 defaultMode 字段设置。2026 版的官方文档列出了六种模式:

模式 行为 适用场景
default 标准行为,每个工具首次使用都询问 初次上手、谨慎操作
acceptEdits 自动接受文件编辑,以及 mkdir / touch / mv / cp 等常见文件系统命令(限工作目录内) 大规模重构、写新功能
plan 计划模式:只读探索 + 只读 shell,不改任何源码 接手陌生代码库、做调研
auto 自动批准,但有后台安全检查校验动作是否符合你的请求(研究预览功能) 信任度较高的自动化任务
dontAsk 除 allow 规则预先批准的工具外,其余一律自动拒绝 严格受控、只允许白名单操作
bypassPermissions 跳过几乎所有权限提示 仅限容器/VM 等隔离环境

其中 bypassPermissions 是最危险的模式,新手务必警惕。它会跳过包括对 .git、.config/git、.claude、.vscode、.idea、.husky、.cargo、.devcontainer、.yarn、.mvn 这些目录写入的提示。官方特意保留了两道"断路器":显式的 ask 规则仍然会弹窗,且 rm -rf /、rm -rf ~ 这种针对根目录或家目录的删除仍然会强制提示——这是为了防止模型错误造成不可逆破坏。只在没有真实数据的沙箱里使用它。

如果你在团队里管安全,可以用两个开关把危险模式彻底关掉(在任意 settings 文件里都有效,放在 managed settings 里最稳):

{
  "permissions": {
    "disableBypassPermissionsMode": "disable",
    "disableAutoMode": "disable"
  }
}

新手的推荐起步配置:先用 default 摸清项目,确认哪些命令高频后,切到 acceptEdits 让写代码流畅起来;永远不要在生产机器或含密钥的目录里用 bypassPermissions。

第四步:手写第一条规则——掌握 Bash 通配符语法

规则统一格式是 Tool 或 Tool(specifier)。下面用一个真实场景练手:你有一个 Node.js 项目,希望 Claude 能自由地跑测试、构建、提交代码,但绝对不能 git push、不能 rm 删文件。

编辑 .claude/settings.json(项目级共享配置,可提交到版本库):

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git commit *)",
      "Bash(git status)",
      "Bash(git diff *)"
    ],
    "deny": [
      "Bash(git push *)",
      "Bash(rm *)",
      "Bash(rm -rf *)"
    ]
  }
}

预期效果:Claude 跑 npm run test、npm run build 不再弹窗;执行 git commit -m "..." 直接放行;一旦它想 git push origin main 或 rm dist/,会直接被拦下,连弹窗都不弹(因为 deny 优先)。

写 Bash 规则时,通配符 * 前面有没有空格,含义完全不同,这是新手最容易写错的地方:

另外,:* 后缀是尾部通配符的等价写法,Bash(ls:*) 等同于 Bash(ls *)。但只有写在末尾的 :* 才被识别——Bash(git:* push) 里的冒号会被当成普通字符,匹配不到任何 git 命令。

通配符可以出现在任意位置:开头 Bash(* --version)、中间 Bash(git * main)、结尾 Bash(npm run test *)。一个 * 能跨多个参数(包含空格),所以 Bash(git *) 能匹配 git log --oneline --all。

第五步:读懂"复合命令"与"进程包装"两个隐藏陷阱

很多人写了 Bash(safe-cmd *) 以为万事大吉,结果 Claude 跑 safe-cmd && rm -rf / 还是出事了——因为它没理解 Claude Code 对复合命令的处理。

Claude Code 认识 shell 操作符,会按 &&、||、;、|、|&、&、换行符把复合命令拆成多个子命令,每条子命令都必须独立命中一条 allow 规则,整个命令才会被放行。所以 Bash(safe-cmd *) 不能授权 safe-cmd && other-cmd。

当你对一个复合命令点"Yes, don't ask again"时,Claude Code 会为每个需要审批的子命令分别保存一条规则(一条复合命令最多存 5 条),而不是存整串字符串。比如批准 git status && npm test,实际存的是 npm test 的规则——以后无论前面跟什么,只要含 npm test 就认。

第二个陷阱是进程包装器(process wrapper)。匹配 Bash 规则前,Claude Code 会剥离一层固定的包装器,所以 Bash(npm test *) 也能匹配 timeout 30 npm test。被剥离的包装器有:timeout、time、nice、nohup、stdbuf,以及不带任何 flag 的 xargs(带 flag 的 xargs -n1 不被剥离)。

但开发环境运行器不被剥离:direnv exec、devbox run、mise exec、npx、docker exec。因为这些工具会把参数当成命令执行,Bash(devbox run *) 实际上能匹配 devbox run rm -rf . 这种危险调用。正确写法是把运行器和内层命令一起写死:Bash(devbox run npm test),需要几个内层命令就写几条。

还有一类 exec 包装器永远会弹窗,无法用前缀规则放行:watch、setsid、ionice、flock,以及带 -exec 或 -delete 的 find。要放行它们,只能写精确匹配整条命令的规则。

好消息是,Claude Code 内置了一批只读命令,任何模式下都不弹窗:ls、cat、echo、pwd、head、tail、grep、find、wc、which、diff、stat、du、cd,以及 git 的只读形式。这个清单不可配置;如果你想对其中某条强制弹窗,得显式加一条 ask 或 deny。

第六步:保护敏感文件——Read / Edit 的 gitignore 式语法

让 Claude 自由改源码很爽,但你绝不想让它读你的 .env 或私钥。Read 和 Edit 规则采用类似 gitignore 的路径语法,有四种锚点:

写法 含义 举例
//path 文件系统根的绝对路径 Read(//Users/alice/secrets/**)
~/path 家目录相对路径 Read(~/Documents/*.pdf)
/path 项目根相对路径(不是系统绝对路径!) Edit(/src/**/*.ts)
path 或 ./path 当前目录相对路径 Read(*.env)

这里最大的反直觉点:/Users/alice/file 不是系统绝对路径,它是相对于项目根的路径!要写系统绝对路径,必须用 //Users/alice/file(双斜杠)。

一个常见的安全配置——封死所有 .env 和密钥文件:

{
  "permissions": {
    "deny": [
      "Read(**/.env)",
      "Read(**/.env.*)",
      "Read(**/*.pem)",
      "Read(**/id_rsa)",
      "Read(~/.ssh/**)"
    ]
  }
}

注意:裸文件名遵循 gitignore 语义,在任意深度都会匹配,所以 Read(.env) 和 Read(**/.env) 等价。* 匹配单个路径段,** 跨目录匹配。

还有一个进阶但关键的安全点——符号链接(symlink)。当 Claude 访问一个 symlink 时,规则会同时检查"链接本身"和"它指向的真实文件"两条路径:

这是为了防止"在允许目录里放一个软链,悄悄绕过去读到敏感文件"。配合下面的 .env 禁读规则,安全性才完整。

第七步:控制联网——WebFetch 与 MCP 规则

默认情况下 Claude 通过 WebFetch 工具联网。如果你想限制它只能访问自家 API 域名:

{
  "permissions": {
    "allow": ["WebFetch(domain:api.mycompany.com)"],
    "deny": ["WebFetch"]
  }
}

WebFetch 的 domain: 前缀匹配主机名,大小写不敏感,且会剥掉末尾的点号。通配符规则要注意:WebFetch(domain:*.example.com) 匹配任意深度的子域名(api.example.com、a.b.example.com),但不匹配 example.com 本身;而 WebFetch(domain:example.*) 里的 * 只能在两点之间匹配,所以匹配 example.org 但不匹配 example.evil.com——这是为了防止攻击者注册相似域名。

重要提醒:光限制 WebFetch 并不能阻止联网。只要 Bash 被允许,Claude 照样能用 curl、wget 访问任意 URL。想真正封死网络,要同时 deny 掉 Bash(curl *)、Bash(wget *),或干脆开启沙箱的网络限制。

MCP 工具的规则用服务器名做前缀:

{
  "permissions": {
    "allow": ["mcp__github__get_*"],
    "deny": ["mcp__puppeteer"]
  }
}

mcp__github__get_* 放行 github 服务器的所有 get_ 开头工具;mcp__puppeteer(裸服务器名)则把 puppeteer 服务器的全部工具整体禁用。

第八步:理解 settings.json 的四级优先级

权限规则遵循统一的 settings 优先级,从高到低:

1. Managed settings(托管策略,不可被任何层级覆盖)
2. 命令行参数(临时会话覆盖)
3. 本地项目设置 .claude/settings.local.json
4. 共享项目设置 .claude/settings.json
5. 用户设置 ~/.claude/settings.json

最核心的原则:只要任意一层 deny 了某个工具,其它任何层级都无法 allow 它。举例:

这条规则对所有层级成立,连命令行 --allowedTools 也救不回一条 managed deny。

这就引出一个团队配置的最佳实践:

文件 提交到 Git? 放什么
.claude/settings.json ✅ 提交 团队统一的 allow/deny 策略、共享的权限模式
.claude/settings.local.json ❌ 加进 .gitignore 个人临时放行的命令、本机路径
~/.claude/settings.json — 跨项目的个人偏好(如全局禁 rm)

把需要保密的、或仅本机有效的规则放进 settings.local.json,团队仓库里只留公共策略,是干净又安全的分工。

第九步:用参数匹配给高级工具加门禁

2026 版新增了一个强大能力——按工具的输入参数匹配(Tool(param:value)),适用于 deny 和 ask 规则。常见用法:

{
  "permissions": {
    "ask": ["Agent(model:opus)"],
    "deny": ["Bash(run_in_background:true)"]
  }
}

注意限制:参数名必须是工具输入的直接字段(不能匹配嵌套对象);每条规则只能写一个参数(要同时限 model 和 isolation 得写两条);省略的字段永远不匹配,所以 Agent(model:*) 匹配不到"没写 model"的调用。


实战案例:为一个 Node.js 项目配出"安全又能干"的权限

下面是一份可直接落地的完整配置,适用于一个需要写功能、跑测试、提交代码,但绝不能碰生产、不能泄密的 Node.js 项目。

.claude/settings.json(提交到团队仓库):

{
  "defaultMode": "acceptEdits",
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(npm test *)",
      "Bash(npx prettier *)",
      "Bash(npx eslint *)",
      "Bash(git commit *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(git add:*)",
      "WebFetch(domain:npmjs.com)"
    ],
    "ask": [
      "Bash(git push *)",
      "Bash(npm publish *)"
    ],
    "deny": [
      "Bash(rm *)",
      "Bash(rm -rf *)",
      "Bash(git push * main)",
      "Bash(git push * production)",
      "Bash(curl *)",
      "Bash(wget *)",
      "Read(**/.env)",
      "Read(**/.env.*)",
      "Read(**/*.pem)",
      "Read(**/id_rsa)",
      "Read(~/.ssh/**)",
      "WebFetch"
    ]
  }
}

.claude/settings.local.json(加进 .gitignore,仅本机):

{
  "permissions": {
    "allow": [
      "Bash(docker compose *)",
      "Bash(docker logs *)"
    ]
  }
}

配置解读:

  1. defaultMode: acceptEdits 让改代码流畅,但 deny 仍硬拦危险操作。
  2. 测试/构建/lint/提交全部放行,干活不卡顿。
  3. git push 和 npm publish 用 ask——每次确认,防止误推误发。
  4. 直接 rm、向 main/production 推送、curl/wget、读密钥、WebFetch 全部 deny 硬封。
  5. 注意 git push * main 这条 deny 会先于上面的 Bash(git push *) ask 命中——主干分支直接拦死,其余分支走确认,符合直觉的"主干严、分支宽"。

验证方法:进会话后跑 /permissions,确认所有规则按预期列出、来源文件标注正确;然后让 Claude 尝试 npm test(应静默通过)、git push origin main(应被拒)、读 .env(应被拒),逐项核对。

整体工作流程

flowchart TD
    Start["Claude 发起工具调用"] --> Deny{"deny 规则匹配?"}
    Deny -- "是" --> Block["❌ 直接阻止<br/>连弹窗都没有"]
    Deny -- "否" --> Ask{"ask 规则匹配?"}
    Ask -- "是" --> Prompt["⚠️ 弹窗确认"]
    Ask -- "否" --> Allow{"allow 规则匹配?"}
    Allow -- "是" --> Run["✅ 静默放行"]
    Allow -- "否" --> Mode{"查权限模式 defaultMode"}
    Mode -- "bypassPermissions" --> Run
    Mode -- "acceptEdits<br/>且为文件操作" --> Run
    Mode -- "default/plan" --> Prompt
    Mode -- "dontAsk" --> Block

    subgraph settings["规则来源优先级 5→1"]
        S5["5 ~/.claude 用户"]
        S4["4 .claude/settings.json 项目共享"]
        S3["3 .claude/settings.local.json 本地"]
        S2["2 命令行参数"]
        S1["1 Managed 托管策略"]
        S5 --> S4 --> S3 --> S2 --> S1
    end
    settings -. "任一层 deny 即全局 deny<br/>deny 在 ask/allow 之前评估" .-> Deny

常见问题与解决方案

Q1:我点了"Yes, don't ask again",但下次又问了,为什么? A:Bash 的记忆是按项目目录持久化的,文件修改的放行只在当前会话有效。换目录或新开会话,文件修改必然重问——这是设计,不是 bug。

Q2:我明明写了 Bash(git push *) 的 allow,为什么还弹窗? A:检查是否存在更宽的 ask 规则或更高层级的 deny。ask 优先于 allow,且任意层级的 deny 都能压住低层级的 allow。用 /permissions 查冲突来源。

Q3:Bash(curl http://github.com/ *) 这种 URL 限定规则安全吗? A:不安全。选项前置、协议切换(http↔https)、短链重定向、变量、多余空格都能绕过它。可靠做法是 deny 掉 curl/wget,改用 WebFetch(domain:github.com) + 一个 PreToolUse hook 校验 URL。

Q4:怎么让 Claude 在一个只读容器里完全放飞? A:用 defaultMode: "bypassPermissions"。但记住断路器仍在:显式 ask 规则、rm -rf /、rm -rf ~ 依然会拦。生产机或含密钥的目录绝不用它。

Q5:Read(/Users/me/file) 为什么没拦住对系统文件的访问? A:单斜杠 / 是项目根相对路径,不是系统绝对路径。要匹配系统绝对路径,写双斜杠 //Users/me/file。

Q6:加了 --add-dir 或 /add-dir 一个目录,它的 .claude/ 配置会自动生效吗? A:不会全部生效。--add-dir / /add-dir 授予的是文件访问权,不是完整配置根。只有 skills、subagents、部分 settings 键会从该目录加载;hooks、CLAUDE.md 默认不加载(CLAUDE.md 需设 CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1)。而 permissions.additionalDirectories 里的目录则只给文件访问、不加载任何配置。

进阶:当规则不够用时,上 Hook

权限规则是"声明式"的,表达不了复杂逻辑(比如"仅在工作日允许部署")。这时用 PreToolUse hook——它在权限提示之前运行一段你的脚本,脚本能 deny、强制 ask 或放行。

但有一条铁律:hook 的决定无法绕过权限规则。即便 hook 返回了 allow,一条匹配的 deny 规则照样拦下;反之,一个 exit code 2 的 hook 会先于 allow 规则执行,从而阻止一条本会被 allow 的调用。所以"allow 全部 Bash + hook 拦少数危险命令"是常见的精细控制范式。

延伸阅读

训练营适用性评估

参考来源

  1. Configure permissions — Claude Code Docs — 官方权限系统权威文档,本文规则语法、判定顺序、模式与优先级的原始来源
  2. Claude Code Permissions: A Practical settings.json Guide — Developers Digest — allow/deny/ask 规则与多层配置的实战图解
  3. Lock Down Claude Code With 5 Permission Patterns — dev.to — 五种封禁模式的工程化对比
  4. Claude Code settings.json, hooks, and permissions: a practical guide — claudefolio — 配置层级、规则语法与"何时该写 hook 而非规则"的取舍
  5. Claude Code settings.json Permissions: allow, ask, and deny Defaults — 层级化配置文件与默认值的补充参考

[!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学通信知识星球海报-扫码加入