本教程面向首次把 Claude Code 引入真实代码仓库的零基础开发者。学完之后,你将能够:理解 Claude Code 的三层权限模型(模式 + 规则 + 钩子),读懂并写出 allow/deny/ask 规则,为你的第一个项目配置一份"只放行本地构建、禁止推送与外发网络"的安全围栏,并掌握在多档权限模式之间自如切换的方法。适用人群:刚完成安装、能跑通 claude 命令,但还没敢把它指向自己项目根目录的开发者。
npm install -g @anthropic-ai/claude-code 完成安装,Node.js ≥ 18claude 首次启动可走通任意一种登录方式)如果你还没装好,先回去完成安装与登录再回来——本教程不重复安装步骤,专注配置这一最容易踩坑、也最该在"第一个项目"阶段就做对的环节。
很多初学者一上来就问"我该把哪个开关关掉",结果越改越乱。先记住一张三层图:
这三层由 Claude Code 强制执行,不是由模型自觉。你在 CLAUDE.md 里写"请不要删文件"没有任何约束力,真正起作用的是 deny 规则和 PreToolUse 钩子。理解这一点,后面的配置才不会南辕北辙。
模式决定了"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)时停下来问你,除非你用规则预先放行。
切换模式不是靠在聊天里打字,而是用专门的控制:
Shift+Tab 循环 default → acceptEdits → plan,当前模式显示在状态栏。VS Code/Desktop 点输入框底部的模式指示器即可。claude --permission-mode plan。settings.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 只接受写在用户级配置里的默认值。
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,* 可在任意位置,单个 * 可跨多个参数。
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)",
"Bash(* --version)"
],
"deny": [
"Bash(git push *)"
]
}
}
最容易踩的两个坑:
Bash(ls *) 匹配 ls -la 但不匹配 lsof;而 Bash(ls*) 两者都匹配。对话框里选"Yes, don't ask again"生成的是带空格形式。末尾的 :*(如 Bash(ls:*))等价于带空格的尾通配,但只在结尾有效——写在中间(如 Bash(git:* push))会被当字面冒号。Bash(command:rm *) 是可绕过的(复合命令能逃逸),Claude Code 会忽略它并报警。正确写法是 Bash(rm *)。MCP 工具的匹配:deny/ask 可用 mcp__* 一键禁用所有 MCP 工具;allow 则必须在 mcp__<服务器名>__ 前缀之后才支持 glob,如 mcp__github__get_*。未锚定的 allow glob(如 *、mcp__*)会被跳过并警告。
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 逐条核对来源即可定位。
dontAsk 给"脚本化"项目上锁如果你的第一个项目其实是一个会被 CI 反复调用的脚本,你想要的不是"少打扰",而是"只准干我批准过的事"。这正是 dontAsk 的舞台:它自动拒绝一切未预先批准的工具。配合 claude -p "..." --permission-mode dontAsk,能保证无人值守时 Claude 绝不越界。
预期结果:在 dontAsk 下,任何不在 allow 列表里的工具调用都会被静默拒绝,任务会因"工具不可用"而中止或改道,但绝不会偷偷执行。
假设你的项目 ~/my-app,目标:允许 Claude 自由读代码、自由跑本地构建与测试、自由编辑工作区文件,但禁止 git push、禁止任何网络外发抓取、禁止删除仓库外的目录。
第 1 步,创建项目级配置:
mkdir -p ~/my-app/.claude
编辑 ~/my-app/.claude/settings.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 步,把个人偏好隔离开,避免污染团队配置:
// ~/my-app/.claude/settings.local.json (加入 .gitignore)
{
"permissions": {
"allow": ["Bash(pnpm *)"]
}
}
并把 settings.local.json 写进 .gitignore。
第 3 步,启动并验证:
cd ~/my-app
claude
进入会话后输入 /permissions,应看到三组规则分别标注来源 project 与 local。
第 4 步,跑第一个任务验证围栏是否就位:
> 帮我跑一遍测试,然后把当前改动提交成一个 fix: 开头的 commit
预期:npm test 与 git commit 因命中 allow 而免提示直接执行;若你随后追加一句"顺便推到远端",git push 会因命中 deny 而被拦截——围栏生效。
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 翻盘;模式只在"没有任何规则命中"时才决定是否需要问你。
code.claude.com/docs/en/permissions——规则语法的权威来源,含 Bash/Read/Edit/WebFetch/MCP 各工具的完整 specifier 列表。code.claude.com/docs/en/permission-modes——六档模式的取舍建议与各平台切换方式。code.claude.com/docs/en/settings——defaultMode、disableBypassPermissionsMode 等字段的完整定义。PreToolUse 钩子做脚本化审计与改写。Shift+Tab 循环),中间 45 分钟带学员动手写 allow/deny 并用 /permissions 核对,最后 30–45 分钟跑通实战案例、故意触发 deny 观察"被拦"现象。Bash(command:rm *)),要求其诊断为何不生效并改正——这比让 ta 默写语法更能检验理解深度。[!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 等场景全覆盖。
