首页 › tutorials › 用权限模式安全跑通首个项目

概述

本教程面向零基础开发者,用一条主线把"安装 → 理解权限 → 配置 settings.json → 跑通第一个真实项目"串成闭环。核心是一套被大多数入门教程忽略的安全护栏——权限模式(permission mode)。读完你会:装好 Claude Code 并验证;看懂 default / acceptEdits / plan / bypassPermissions 四种模式的差异;在用户级和项目级写出 allow / deny 规则;最后让 Claude 亲手为你造一个可运行的 Markdown 笔记统计 CLI,全程不被危险命令吓到,也不被频繁确认拖慢节奏。

前置条件

不需要你懂 Git、不需要你懂框架,会敲命令、会读报错即可。


正文

第一步:安装与验证

Claude Code 是一个 npm 全局包,一行命令装完。

npm install -g @anthropic-ai/claude-code

操作说明:-g 表示全局安装,装完后 claude 命令在任何目录都能用。

预期结果:终端会滚动输出下载与解包日志,最后没有红色 error 即成功。验证安装:

claude --version

正常会返回一个形如 2.x.x 的版本号(截至 2026 年中,主版本已到 2.x,例如 2.1.181 之后 /config 支持了 key=value 形式)。如果返回 command not found,说明 npm 的全局 bin 目录没在 PATH 里。

常见问题与解决:

  1. EACCES: permission denied:npm 全局目录无写权限。不要用 sudo 绕过(会把整个全局目录变成 root 所有,后患无穷)。正确做法是把 npm 全局前缀改到用户目录: bash mkdir ~/.npm-global npm config set prefix '~/.npm-global' # 然后把 ~/.npm-global/bin 加入 PATH
  2. claude --version 报 command not found:Windows 检查 %APPDATA%\npm 是否在 PATH;macOS/Linux 用 npm config get prefix 拿到全局 bin 路径,加进 ~/.zshrc 或 ~/.bashrc 的 export PATH=。
  3. 公司网络装不上:换淘宝镜像 npm config set registry https://registry.npmmirror.com,装完再换回官方源,避免后续依赖滞后。

第二步:登录与初始化项目目录

安装只是把程序放进系统,第一次跑还要让它"认识你"。

mkdir -p ~/cc-first && cd ~/cc-first
claude

操作说明:claude 不带任何参数,会进入交互式会话(REPL)。首次启动会引导你完成认证。

预期结果: - 如果用订阅:终端打印一个登录 URL,浏览器打开后授权,回到终端显示已登录、当前账号、可用额度层级。 - 如果用 API Key:会提示你粘贴 Key,或让你提前设环境变量: bash export ANTHROPIC_API_KEY="sk-ant-xxxxx" (把这行写进 ~/.zshrc / ~/.bashrc 持久化,新开终端不用重设。)

登录成功后,底部输入框亮起,光标闪烁——你已经能和它对话了。

常见问题与解决:

  1. 国内访问登录 URL 超时:Claude Code 的认证与推理都走 Anthropic 域名,需要稳定的网络出口。配好代理后,设置环境变量让 CLI 走代理: bash export HTTPS_PROXY=http://127.0.0.1:7890
  2. 不知道当前账号和额度:在会话里输入 /status,会显示登录身份、套餐层级、剩余额度。新手一定先 /status 一次,免得跑批任务时额度见底。
  3. 想换账号:/logout 退出,再次 claude 重新走认证。

第三步:看懂权限模式——本教程的核心

很多人装完就开问,结果每改一个文件、每跑一条命令都被打断确认,体验很糟;或者图省事一键放行所有操作,又不小心让模型 rm -rf 了关键目录。权限模式就是这中间的调节阀。

Claude Code 在编辑文件、执行 shell 命令、发起网络请求前,会"暂停并询问"。询问的频率由当前模式决定。CLI 里按 Shift+Tab 在三种交互模式间循环:default → acceptEdits → plan,当前模式显示在输入框附近。

四种模式对照:

模式 行为 适合场景
default 每个敏感操作(写文件、跑命令、联网)都暂停问一次 陌生项目、首次审查、敏感生产环境
acceptEdits 文件编辑自动批准,但跑命令、联网仍要问 已信任的代码改动,想少打断又怕乱跑命令
plan 只规划不执行——只读分析、给方案,不动文件不跑命令 需求不清时先让它出方案,避免误改
bypassPermissions 全部自动批准,不再问 沙盒、一次性任务、CI(慎用)

关键认知:bypassPermissions 不在 Shift+Tab 循环里,因为它风险高,要你显式指定,例如:

claude --permission-mode bypassPermissions

新手强烈建议全程留在 default,等熟悉了再视任务切换。

操作说明:按一次 Shift+Tab,观察输入框上方提示从 default 变成 acceptEdits;再按一次变 plan;第三次回到 default。

预期结果:模式字样实时更新,光标回到输入框。你可以立刻问一句"当前处于什么权限模式?"它会如实回答,验证你对提示的理解。

常见问题与解决:

  1. 忘了当前模式:看输入框上方文字;或直接问它。plan 模式下它若说"我只规划不执行",就是对的。
  2. acceptEdits 还是频繁弹窗:因为 acceptEdits 只自动批准"文件编辑",跑 npm test 这类命令依旧问。这是设计如此,不是 bug。要连命令也自动批,得靠下一步的 allow 规则。
  3. CI 里默认是哪种:非交互环境默认不弹窗,未在 allow 列表的命令会按规则处理或失败。CI 通常配 bypassPermissions + 锁死的沙盒镜像。

第四步:用 settings.json 写权限规则

模式是"总闸",规则是"细闸"。你可以在不切换模式的前提下,把特定命令永久加入白名单,省掉重复确认。配置入口是 /config 命令——交互式选项卡,能看状态、改选项。从 v2.1.181 起,还能一行改单值,例如:

/config verbose=true

但更可控的是直接编辑配置文件。Claude Code 的配置按作用域分层,后者覆盖前者:

flowchart LR
    A["企业/托管策略<br/>managed settings"] --> B["用户级<br/>~/.claude/settings.json"]
    B --> C["项目级<br/>.claude/settings.json<br/>随仓库提交共享"]
    C --> D["本地级<br/>.claude/settings.local.json<br/>个人覆盖,不入库"]
    D --> E["生效配置<br/>优先级最高"]
    style A fill:#fde2e2
    style E fill:#d6e8ff

权限规则用 allow / deny / ask 三个键,规则语法是 工具(参数模式):

{
  "permissions": {
    "allow": [
      "Bash(npm test)",
      "Bash(npm run lint)",
      "Read(./**)",
      "Edit(./src/**)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(curl:*)",
      "Read(./.env)",
      "Edit(./.env)"
    ],
    "ask": [
      "Bash(git push:*)"
    ]
  }
}

语义要点: - allow:命中即自动放行,不再问。 - deny:命中即拒绝,连问都不问。deny 永远优先于 allow,是最后一道安全锁。 - 未被任何规则命中的操作,按当前模式处理(default 会问)。 - Bash(npm test) 精确匹配命令;Bash(npm:*) 匹配 npm 开头的一切;Bash(rm -rf:*) 匹配 rm -rf 加任意参数。

操作说明:在项目根目录手动建 .claude/settings.local.json(或让 Claude 帮你建——但它建文件也算操作,default 模式会问你一次)。

预期结果:保存后回到会话,让它跑 npm test,如果命中 allow,会直接执行不再弹窗;让它读 .env,会被拒。

常见问题与解决:

  1. 改了 settings.json 没生效:部分选项需重启会话。最稳的办法是退出当前 REPL(Ctrl+C 两次或 /exit),重新 claude。
  2. 规则不匹配:规则按完整命令字符串前缀匹配。你写 Bash(git push),但模型执行的是 git push origin main,可能不完全匹配——用通配 Bash(git push:*) 更稳妥。: 是工具与参数的分隔符。
  3. 团队误提交了本地配置:项目级文件该入库共享,但本地级绝不能入库。务必确认 .claude/settings.local.json 在 .gitignore 里。
  4. 想全局禁掉某个危险命令:放用户级 ~/.claude/settings.json 的 deny,所有项目生效。

第五步:发起第一个真实任务

配置就绪,现在让它干活。在 default 模式下,把需求交给它:

在当前目录用 Node.js 造一个 Markdown 笔记统计 CLI,叫 mdcount。
功能:扫描指定目录下所有 .md 文件,统计总文件数、总行数、总字数、各级标题数量,
最后按字数降序输出 Top 5 文件。支持 --dir 参数指定目录,默认当前目录。

它会先规划(你可以在这一步切到 plan 模式只看方案),确认后开始建文件。default 模式下每个写文件、跑命令的步骤都会弹窗,你逐个批准即可;嫌烦就把对应的 Bash/Edit 加进 allow,体验立刻顺滑。


实战案例:mdcount 笔记统计器

下面是 Claude 会为你生成的完整可运行项目。即使你不用 Claude,把这几个文件手动建好,node index.js --dir . 也能直接跑。

项目结构:

cc-first/
├── index.js          # 入口与参数解析
├── counter.js        # 统计核心逻辑
├── formatter.js      # 结果格式化输出
└── package.json      # 声明 bin,支持全局调用

counter.js —— 统计核心:

const fs = require('fs');
const path = require('path');

// 递归收集目录下所有 .md 文件
function collectMarkdown(dir) {
  let results = [];
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, entry.name);
    // 跳过 node_modules 等噪声目录
    if (entry.isDirectory() && entry.name === 'node_modules') continue;
    if (entry.isDirectory()) {
      results = results.concat(collectMarkdown(full));
    } else if (entry.name.endsWith('.md')) {
      results.push(full);
    }
  }
  return results;
}

// 统计单个文件
function statFile(file) {
  const text = fs.readFileSync(file, 'utf8');
  const lines = text.split('\n');
  const words = text.split(/\s+/).filter(Boolean).length;
  // 匹配 Markdown 标题 # ~ ######
  const headings = {};
  for (const line of lines) {
    const m = line.match(/^(#{1,6})\s/);
    if (m) headings[m[1].length] = (headings[m[1].length] || 0) + 1;
  }
  return { file, lines: lines.length, words, headings };
}

module.exports = { collectMarkdown, statFile };

index.js —— 入口:

#!/usr/bin/env node
const { collectMarkdown, statFile } = require('./counter');
const { formatReport } = require('./formatter');

// 简单解析 --dir 参数,默认当前目录
const args = process.argv.slice(2);
let dir = '.';
for (let i = 0; i < args.length; i++) {
  if (args[i] === '--dir' && args[i + 1]) dir = args[i + 1];
}

const files = collectMarkdown(dir);
if (files.length === 0) {
  console.log(`目录 ${dir} 下没有 .md 文件`);
  process.exit(0);
}

const stats = files.map(statFile);
console.log(formatReport(stats));

formatter.js —— 输出:

function formatReport(stats) {
  const totalLines = stats.reduce((s, f) => s + f.lines, 0);
  const totalWords = stats.reduce((s, f) => s + f.words, 0);
  const headingTotals = {};
  stats.forEach(f => {
    Object.entries(f.headings).forEach(([level, n]) => {
      headingTotals[level] = (headingTotals[level] || 0) + n;
    });
  });

  const top5 = [...stats].sort((a, b) => b.words - a.words).slice(0, 5);

  let out = `📁 共扫描 ${stats.length} 个 Markdown 文件\n`;
  out += `📝 总行数:${totalLines} 总字数:${totalWords}\n`;
  out += `🏷  标题:` +
    Object.keys(headingTotals).sort()
      .map(l => `H${l}=${headingTotals[l]}`).join(' ') + '\n';
  out += `\n🏆 字数 Top 5:\n`;
  top5.forEach((f, i) => {
    out += `  ${i + 1}. ${f.file} — ${f.words} 字 / ${f.lines} 行\n`;
  });
  return out;
}

module.exports = { formatReport };

运行方式:

node index.js --dir .

预期输出(在你的笔记目录里跑):

📁 共扫描 42 个 Markdown 文件
📝 总行数:3180 总字数:21456
🏷  标题:H1=38 H2=156 H3=203

🏆 字数 Top 5:
  1. ./notes/architecture.md — 1842 字 / 312 行
  2. ./notes/roadmap.md — 1205 字 / 198 行
  ...

让它更有趣的延伸指令(直接在 Claude 会话里接着提): - "把 mdcount 包成可执行命令,加到 package.json 的 bin,支持 npx mdcount。" - "加 --watch 参数,文件变化时自动重算。" - "把 Top 5 结果输出成 Markdown 表格写进 REPORT.md。"

每一条都顺带验证你的权限规则——加 bin 要改 package.json(Edit)、要 npm link 测试(Bash)、要写 REPORT.md(Write)。default 模式会逐个确认,确认烦了就把规则写进 settings.local.json。


整体工作流程图

下面这张图把"从一条需求到安全落地"的全过程,以及权限系统在每个节点的介入点画在一起:

flowchart TD
    Start(["用户提出需求"]) --> Mode{"当前权限模式?"}
    Mode -->|default| AskEach["每个敏感操作<br/>都暂停询问"]
    Mode -->|acceptEdits| AutoEdit["文件编辑自动批<br/>命令/联网仍问"]
    Mode -->|plan| ReadOnly["只读分析<br/>不动文件不跑命令"]
    Mode -->|bypass| AutoAll["全部自动批准<br/>仅限沙盒"]

    AskEach --> Rule{"命中规则?"}
    AutoEdit --> Rule
    AutoAll --> Exec

    Rule -->|在 deny| Block["❌ 直接拒绝<br/>deny 优先级最高"]
    Rule -->|在 allow| Exec["✅ 自动放行"]
    Rule -->|未命中| Prompt["弹窗让用户决定"]
    Prompt -->|批准| Exec
    Prompt -->|拒绝| Block

    Exec --> Result(["产生改动/输出"])
    Block --> End(["任务终止或换方案"])
    ReadOnly --> Plan(["输出方案<br/>用户确认后再切 default 执行"])

    style Block fill:#fde2e2
    style Exec fill:#d6eedd
    style Plan fill:#fff4cc

读懂这张图,你就理解了 Claude Code 的安全模型:模式管总闸,规则管细闸,deny 是终审。


延伸阅读


训练营适用性评估

参考来源

  1. Claude Code 官方快速开始(中文) — 安装、订阅前置条件与首跑流程的权威基线
  2. Choose a permission mode — Claude Code Docs — default / acceptEdits / plan / bypassPermissions 四种模式的官方语义,含 Shift+Tab 循环说明
  3. Claude Code 设置官方文档(中文) — /config 命令、配置作用域(用户/项目/本地)与环境变量配置
  4. 2026 年 Claude Code 完全使用教程 — SimilarLabs — 从安装配置到核心命令、工作流的中文长文教程
  5. Claude Code 2026 完整使用指南 — Solosoft — 重点覆盖四大权限模式与企业级工作流,与本文权限主线互补

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