本教程面向第一次启动 Claude Code、却被"是否允许执行此命令"弹窗反复打断的零基础用户。学完之后,你将理解 Claude Code 的三层权限模型(只读 / 命令 / 改文件)、掌握 allow / ask / deny 规则的写法与判定顺序、能区分六种权限模式,并能为一个真实项目写出一份"既能放手干活、又不会误删生产环境"的 settings.json 配置。适用人群:刚装好 Claude Code、准备跑第一个项目的开发者,以及需要在团队内统一权限策略的技术负责人。
npm i -g @anthropic-ai/claude-code 或官方原生安装器),能执行 claude 命令并完成登录cd、ls、文本编辑很多人第一次用 Claude Code,会被弹窗搞得心烦,根本原因是没搞清楚它把工具分成了三类,每类的审批策略完全不同。Claude Code 官方文档把它定义为分级权限系统(tiered permission system):
| 工具类型 | 举例 | 是否需要审批 | 点击"Yes, don't ask again"后的效果 |
|---|---|---|---|
| 只读工具 | 文件读取(Read)、Grep、Glob | 否 | 永不弹窗 |
| Bash 命令 | Shell 执行(npm test、git commit) |
是 | 按"项目目录 + 命令前缀"永久记住 |
| 文件修改 | Edit / Write | 是 | 仅当前会话有效,退出即失效 |
这里有两个关键认知,直接决定你后续配置策略:
~/my-app 里对 npm run test 点了"以后别再问",这条规则会永久写进 ~/.claude.json(按目录分组),下次再进这个项目就不会问。但换到 ~/other-project,它又会重新问。理解这一点后,你的配置思路就清晰了:对高频、低风险的命令(测试、构建、提交)做 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 规则的行为取决于它是否带了限定符。
Bash:把 Bash 工具从 Claude 的可用上下文里整体移除,Claude 根本看不到它,自然不会尝试调用。Bash(rm *):Bash 工具仍然可用,只在 Claude 真去执行 rm ... 时拦截。第一条(裸名)适用于"我完全不希望 Claude 碰某类工具";第二条(带限定符)适用于"我希望 Claude 用 Bash,但别碰某几个危险命令"。
另一个必须牢记的认知:权限规则是由 Claude Code(外壳程序)强制执行的,不是由模型执行的。你在 CLAUDE.md 或提示词里写"不要执行 git push",只是影响 Claude 想做什么,并不能阻止它调用;真正能阻止的只有 permissions 规则、权限模式或 PreToolUse hook。
权限模式是"全局基调开关",决定 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。
规则统一格式是 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 *)(星号前有空格):要求 ls 后必须跟一个空格或字符串结尾,匹配 ls -la,不匹配 lsof。Bash(ls*)(无空格):没有词边界约束,ls -la 和 lsof 都匹配。另外,:* 后缀是尾部通配符的等价写法,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。
让 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 禁读规则,安全性才完整。
默认情况下 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 优先级,从高到低:
1. Managed settings(托管策略,不可被任何层级覆盖)
2. 命令行参数(临时会话覆盖)
3. 本地项目设置 .claude/settings.local.json
4. 共享项目设置 .claude/settings.json
5. 用户设置 ~/.claude/settings.json
最核心的原则:只要任意一层 deny 了某个工具,其它任何层级都无法 allow 它。举例:
~/.claude/settings.json)里 allow 了 Bash(git push *),但项目设置里 deny 了——结果是被禁。这条规则对所有层级成立,连命令行 --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)"]
}
}
Agent(model:opus):Claude 调用子代理且请求 Opus 模型时弹窗(防止悄悄烧贵模型)Bash(run_in_background:true):禁止后台运行 Bash(防止起失控的长任务)注意限制:参数名必须是工具输入的直接字段(不能匹配嵌套对象);每条规则只能写一个参数(要同时限 model 和 isolation 得写两条);省略的字段永远不匹配,所以 Agent(model:*) 匹配不到"没写 model"的调用。
下面是一份可直接落地的完整配置,适用于一个需要写功能、跑测试、提交代码,但绝不能碰生产、不能泄密的 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 *)"
]
}
}
配置解读:
defaultMode: acceptEdits 让改代码流畅,但 deny 仍硬拦危险操作。git push 和 npm publish 用 ask——每次确认,防止误推误发。rm、向 main/production 推送、curl/wget、读密钥、WebFetch 全部 deny 硬封。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 里的目录则只给文件访问、不加载任何配置。
权限规则是"声明式"的,表达不了复杂逻辑(比如"仅在工作日允许部署")。这时用 PreToolUse hook——它在权限提示之前运行一段你的脚本,脚本能 deny、强制 ask 或放行。
但有一条铁律:hook 的决定无法绕过权限规则。即便 hook 返回了 allow,一条匹配的 deny 规则照样拦下;反之,一个 exit code 2 的 hook 会先于 allow 规则执行,从而阻止一条本会被 allow 的调用。所以"allow 全部 Bash + hook 拦少数危险命令"是常见的精细控制范式。
settings.json 的所有字段,见官方 Settings 页(权限设置表在文末)PreToolUse 实现声明式规则表达不了的运行时逻辑auto 模式分类器配置.claude/settings.json + .claude/settings.local.json,并通过 /permissions 验证通过。mcp__server 规则与托管 MCP 策略)。[!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 等场景全覆盖。
