本教程面向刚装好 Claude Code、却每次都要点"Allow"点到怀疑人生的零基础开发者。你将学会用 三种权限模式 和 settings.json 的 allow/ask/deny 规则,把一个"问个不停的助手"调教成"边界清晰的受控沙箱"。学完后你能为任意项目写一份可入库、可复用的权限配置文件,让 Claude 在白名单内自由操作、在危险操作前停下来问你。无需编程经验,跟着复制粘贴即可完成首个受控项目。
claude --version 有输出即可;若没装,参考官方 npm install -g @anthropic-ai/claude-code)~/perm-demo)Claude Code 出厂默认是最谨慎模式:它每想改一个文件、每想跑一条命令,都会暂停并弹出确认框。这对第一次用的人很安全,但一旦你让它"重构这个模块""跑一下测试",确认框就会像弹幕一样刷屏。
更麻烦的是:这些放行决定只活在当前会话里。下次重启,又从零开始点。这就是社区俗称的 "approval fatigue"(放行疲劳)。
解决办法不是粗暴地打开"全部自动通过"(那就是危险的 bypassPermissions 模式),而是用配置文件把规则固化下来:哪些操作永远自动放行,哪些永远拒绝,哪些每次都要问。这就是本教程的核心。
Claude Code 把"该不该问"抽象成模式。在交互界面里按 Shift+Tab 会在三个模式间循环切换,当前模式显示在输入框上方。
| 模式 | 改文件 | 跑命令 | 典型场景 |
|---|---|---|---|
| default | 问 | 问 | 日常开发,最稳 |
| acceptEdits | 自动放行 | 仍问 | 改一批文件、跑少量命令时提速 |
| plan | 不改(只读) | 受限 | 先让它出方案、不动手 |
| bypassPermissions | 全自动 | 全自动 | 慎用,仅限一次性脚本/CI |
记忆口诀:default 最稳,acceptEdits 提速改文件,plan 只读不动手,bypass 是裸奔。
关键认知:模式是"会话级开关",settings.json 是"项目级规则"。两者叠加——规则定义白名单/黑名单,模式决定白名单之外的操作要不要问你。本教程重点是把"规则"写进文件。
Claude Code 的配置不是单一文件,而是三层叠加,优先级从高到低:
/etc/claude-code/managed-settings.json 等,普通用户基本用不到).claude/settings.local.json(不入库,个人偏好,如个人的 API Key).claude/settings.json(入库,团队共享的权限规则)~/.claude/settings.json(所有项目通用的默认值)合并规则:高优先级覆盖低优先级。deny 永远是硬上限——任何一层写了 deny,下层再 allow 也无效。这条规则是沙箱安全的基石:团队领导在共享层 deny 掉 rm -rf,团队成员的个人配置再怎么 allow 也绕不过去。
flowchart TD
U["~/.claude/settings.json<br/>用户全局默认"] --> M["合并"]
P[".claude/settings.json<br/>项目共享·入库"] --> M
L[".claude/settings.local.json<br/>项目本地·不入库"] --> M
C["命令行参数"] --> M
M --> F["最终生效配置"]
F --> R{"deny 硬上限<br/>任何层 deny 即拒绝"}
R -->|允许| GO["执行"]
R -->|拒绝| BLOCK["拦截"]
实践要点:权限规则放
.claude/settings.json(入库),密钥/个人偏好放.claude/settings.local.json(加进.gitignore)。
规则写在 permissions 对象里,三组数组:
allow:自动放行,不问ask:每次都问(即使模式是 acceptEdits)deny:永远拒绝每条规则是一个"工具说明符"字符串,格式是 工具名(参数模式)。常用工具:Bash、Read、Edit、Write、WebFetch。
flowchart LR
OP["Claude 要执行操作"] --> C1{"命中 deny?"}
C1 -->|是| BLK["❌ 拒绝"]
C1 -->|否| C2{"命中 allow?"}
C2 -->|是| OK1["✅ 自动放行"]
C2 -->|否| C3{"命中 ask?"}
C3 -->|是| ASK["❓ 弹窗询问"]
C3 -->|否| MODE{"看当前模式"}
MODE -->|default| ASK
MODE -->|acceptEdits<br/>且为编辑| OK2["✅ 自动放行"]
MODE -->|plan| BLK2["🚫 只读拦截"]
Bash(git status) # 精确匹配,只放行这一条命令
Bash(git diff:*) # 前缀匹配,git diff 后面跟任何参数都行
Bash(npm run test:*) # 放行 npm run test、npm run test:unit 等
Bash(ls:*) # 放行所有 ls 开头
冒号加星号 :* 表示"前缀匹配,后面随便接"。这是减少弹窗最有效的语法——把团队高频命令写成前缀规则,能干掉 80% 的确认框。
Read # 放行所有读取(最宽松,慎用)
Read(./src/**) # 只能读 src 目录
Edit(./src/**) # 只能改 src 目录
Edit(~/.zshrc) # 精确到某个文件
Write(//tmp/out.txt) # 绝对路径用双斜杠开头
把密钥、环境变量文件 deny 掉,避免 Claude 把 .env 内容读进上下文、甚至写进 commit:
Read(./.env)
Read(./.env.*)
Read(./**/*.key)
Read(./**/*.pem)
Read(./**/credentials*)
mkdir -p ~/perm-demo/.claude
cd ~/perm-demo
git init
echo "node_modules/" > .gitignore
echo ".claude/settings.local.json" >> .gitignore
预期结果:生成一个带 .claude 目录、已初始化 git 的空项目,本地配置被排除在版本控制外。
.claude/settings.json用编辑器创建 ~/perm-demo/.claude/settings.json,粘贴:
{
"model": "claude-sonnet-4-6",
"permissions": {
"defaultMode": "default",
"allow": [
"Read",
"Bash(git status)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(ls:*)",
"Bash(wc:*)",
"Bash(npm run test:*)",
"Bash(npm run build)"
],
"ask": [
"Bash(npm install:*)",
"Bash(git add:*)",
"Bash(git commit:*)"
],
"deny": [
"Bash(rm:*)",
"Bash(sudo:*)",
"Bash(git push:*)",
"Bash(curl:*)",
"Bash(wget:*)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/*.key)",
"Read(./**/*.pem)",
"Edit(~/.**)",
"Write(~/.**)"
]
}
}
字段逐行解读:
- model:固定用某个模型,避免每次手选
- defaultMode: default:会话默认从最稳模式起步
- allow:读文件全放行;git 查看类、统计类、构建测试类命令前缀放行——这些是"看"和"验",零风险
- ask:装依赖、改 git 状态这种"有副作用"的操作每次确认
- deny:删除、提权、推送、联网下载、读密钥、动家目录——红线
预期结果:保存后文件即是项目的权限宪法。
cd ~/perm-demo
claude
进入交互界面后输入:
/permissions
预期结果:弹出权限管理界面,能看到刚才写的 allow/ask/deny 三组规则,且来源标记为 .claude/settings.json。
在会话里依次让它做三件事,观察弹窗差异:
Read、ls、wc,全部自动放行,无弹窗ask,弹窗询问你同意与否deny,直接拒绝,连问都不问预期结果:三条命令对应三种行为,证明规则已接管。
按 Shift+Tab 切到 acceptEdits,让它"创建一个 utils.js 并写个加法函数"。这次改文件不再弹窗,但若它想跑 npm install,仍会问——因为 ask 规则优先于模式。
预期结果:体会"规则 + 模式"双层控制:规则管白名单/黑名单,模式管白名单之外那一档的松紧。
把上面的配置打包成一个只读分析沙箱——让 Claude 自由读代码、自由跑统计命令,但一行都改不了、连不上网、删不掉任何东西。这种配置特别适合"让 AI 帮我做代码审查/统计但绝不能动我的代码"的场景。
创建 ~/perm-demo/.claude/settings.json(覆盖步骤 2 的版本):
{
"model": "claude-sonnet-4-6",
"permissions": {
"defaultMode": "default",
"allow": [
"Read",
"Glob",
"Grep",
"Bash(git status)",
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(ls:*)",
"Bash(wc:*)",
"Bash(find:*)",
"Bash(grep:*)",
"Bash(rg:*)",
"Bash(cat:*)",
"Bash(echo:*)"
],
"deny": [
"Edit",
"Write",
"NotebookEdit",
"Bash(rm:*)",
"Bash(mv:*)",
"Bash(cp:*)",
"Bash(mkdir:*)",
"Bash(touch:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push:*)",
"Bash(curl:*)",
"Bash(wget:*)",
"Bash(npm:*)",
"WebFetch",
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/*.key)",
"Read(./**/*.pem)"
]
},
"additionalDirectories": [],
"env": {
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
}
}
设计思路:
- allow 里全是"只读"工具:Read/Glob/Grep + 只读 Bash 命令
- deny 里把所有"写"工具(Edit/Write/NotebookEdit)一刀切掉,再加文件系统修改命令、git 写操作、联网、包管理
- 即便你不小心切到 bypassPermissions,deny 仍然是硬上限——这是 deny 模式最强的价值:模式会忘,规则不会
实测任务:在 ~/perm-demo 里随便丢几个源码文件,然后对 Claude 说:
"统计这个项目每个文件的代码行数,找出最长的三个函数,输出一份 markdown 报告。"
预期结果:它能流畅读文件、跑 wc -l、跑 rg 搜函数,最终生成报告——全程零弹窗,且如果你后续追问"顺便把这些函数重构一下",它会因 deny(Edit) 而无法动手,只能口头给建议。沙箱成立。
flowchart TD
A["开始一个新项目"] --> B["cd 到项目根目录<br/>git init"]
B --> C["创建 .claude/settings.json"]
C --> D{"项目要不要入库共享?"}
D -->|团队共享| E["权限规则写 settings.json<br/>个人密钥写 settings.local.json"]
D -->|个人练手| F["都写 local 即可"]
E --> G["定义 allow: 高频只读命令"]
F --> G
G --> H["定义 ask: 有副作用但可控"]
H --> I["定义 deny: rm/sudo/密钥/家目录"]
I --> J["把 settings.local.json<br/>加进 .gitignore"]
J --> K["进会话<br/>/permissions 检查"]
K --> L{"日常用哪个模式?"}
L -->|谨慎改| M["default"]
L -->|批量改文件| N["acceptEdits<br/>Shift+Tab"]
L -->|先出方案| O["plan<br/>Shift+Tab 两次"]
M --> P["放心交给 Claude"]
N --> P
O --> P
Q1:规则写了但没生效?
检查三点:① JSON 语法是否正确(少逗号、多逗号都会让整个文件被忽略);② 文件路径是否在 .claude/settings.json,而非项目根目录;③ 是否被更高优先级层覆盖——在 /permissions 界面看每条规则的来源标注。
Q2:allow 和 ask 同时匹配一条命令,听谁的?
优先级是 deny > ask > allow。所以 Bash(git push:*) 同时出现在 allow 和 deny 里,结果是被拒。利用这点可以把"团队默认放行、但个人想拦"的操作放个人层的 deny。
Q3:Bash(npm run test) 和 Bash(npm run test:*) 有什么区别?
前者精确匹配,只有 npm run test 这一条放行,npm run test:unit 仍会问;后者前缀匹配,子命令全放行。做测试/构建类规则几乎都用 :*。
Q4:acceptEdits 模式下还是弹窗?
说明该操作命中了 ask,或不在 allow 里。模式只决定"白名单之外"的默认行为,ask 规则永远优先于模式。
Q5:bypassPermissions 到底能不能用?
能用,但仅建议在一次性、可丢弃、无敏感数据的环境(如临时容器、CI 沙箱)里用,且务必配 deny 兜底。本地主力开发环境永远不要长期挂 bypass,一次误删家目录就够你哭的。
Q6:怎么临时给一条命令放行,又不污染配置?
在弹窗时选 "Yes, and don't ask again for this command"——Claude 会把这条规则写进 .claude/settings.local.json,下次自动放行,且不污染团队共享配置。这是日常最常用的"渐进式建白名单"手法。
Q7:项目里已经有 .env,怎么保证不被读?
在 deny 加 Read(./.env) 和 Read(./.env.*)。注意路径相对于项目根;若密钥散落各处,用通配 Read(./**/*.key)、Read(./**/credentials*) 兜底。
deny 和团队标准放 .claude/settings.json 入库;个人模型偏好、临时放行放 .claude/settings.local.json。Bash:永远别写 "Bash" 不带参数——那等于把整个 shell 放行,比 bypass 还危险。.env、*.key、*.pem、credentials* 进 deny,且加进 .gitignore。rm:*、sudo:*、git push:*、git push --force:* 是四条"后悔药买不到"的红线。WebFetch、curl:*、wget:* 放 ask 或 deny,避免 Claude 把代码片段发到外网。.gitignore:.claude/settings.local.json 必须忽略,否则个人密钥会泄露。settings.md 说明每条规则的意图,方便新成员理解。code.claude.com/docs/en/permission-modes——Shift+Tab 循环与四种模式的权威说明code.claude.com/docs/en/settings——所有字段(model、env、additionalDirectories 等)的完整列表code.claude.com/docs/en/permissions——allow/ask/deny 与工具说明符的细节github.com/anthropics/claude-code/tree/main/examples/settings——可直接拷贝的样板code.claude.com/docs/en/env-vars——settings.json 里 env 字段可放哪些变量settings.json 用于 CI 的 -p 无头批处理,配合 bypass + deny 做"无人值守但安全"的管道managed-settings.json(企业策略层)做组织级红线settings.json,跑通"统计沙箱"案例且 rm -rf 被拒、.env 不可读,即视为通过[!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 等场景全覆盖。
