很多人在GitHub上最烦的事,不是代码写不出来,而是明明输对了用户名和密码,git push就是报错。尤其是2021年8月13日之后,GitHub官方正式取消了密码认证,凡是走HTTPS协议操作仓库,一律要求使用Personal Access Token(个人访问令牌,下称PAT)。更闹心的是,现在随便打开一篇文章讲GitHub认证,动不动就甩给你一段"SSH key配置"的教程,搞得很多人以为发代码只能配密钥。实际上,PAT才是日常用起来最轻量、最不容易出问题的方式,前提是你把它的原理和操作流程搞清楚。
这篇文章我把整套流程掰碎了讲一遍:为什么GitHub要废弃密码、PAT到底是个什么东西、去哪里创建、创建的时候那些权限项怎么选、日常使用中会遇到哪些坑、怎么排查解决。适合刚接触GitHub的小白,也适合用了几年的老手——因为新版的细粒度Token(Fine-grained token)和经典Token(Classic token)两套体系并存,很多人根本不注意,结果配出来的Token一会儿灵一会儿不灵,其实多半是选错了类型或者权限范围没勾对。
1. 为什么GitHub突然要你用Token,而不是密码
1.1 密码认证为什么被废弃
先从背景聊起。早年间GitHub走HTTPS协议操作远程仓库,就是拿账号密码去登录,密码输对了就放行。这个模式一直在跑,直到2021年8月13日GitHub发了正式公告:对Git操作强制要求使用基于令牌的认证方式,账户密码彻底退出历史舞台。
官方给的理由非常实诚:如果你的账号开了两步验证(2FA),单纯靠密码去认证本来就不好使,因为Git命令行不会像浏览器那样弹出一个"输入动态验证码"的界面,你压根没法把第二层凭证传过去。与其硬做一套Git专用的2FA交互,不如直接用Token代替密码。Token本质上是一个随机生成的字符串,它替代密码来完成身份验证,同时可以附加权限范围、过期时间等策略。
这个思路其实很好理解,打个比方:密码是你家大门的钥匙,一旦泄露,别人拿着就能把你家里所有房间都打开;而PAT更像是酒店房卡,只能刷开你指定的楼层和房间,而且过了退房日期就失效,你随时可以去前台注销这张房卡。Token就是这张房卡。
1.2 HTTPS与SSH,到底选哪条路
现在GitHub主流的仓库认证方式就两条路:走HTTPS协议配Token,走SSH协议配密钥。很多人在这两者之间反复横跳,其实没必要纠结,两条路都能用,按场景选就行。
- HTTPS + Token:配置简单,不涉及生成密钥对,不需要把公钥上传到GitHub后台。日常电脑上只需要记一次用户名和Token,之后Git会自动记住。适合大多数个人开发者,尤其适合在公司电脑上临时拉仓库的场景。
- SSH + Key:需要先在本机生成一对密钥(公钥和私钥),再把公钥加到GitHub账户里。配置步骤多一点,但配置完成后非常稳定,不需要像Token一样担心过期时间。适合长期固定的一台开发机。
我并不觉得SSH比HTTPS"高级",实际工作中很多人被SSH密钥的权限和格式问题折磨过,反而HTTPS + Token用起来最不费脑子。这篇文章只讲PAT这一条线,如果你确实打算用SSH,换一篇教程慢慢研究也行。
2. 两种Personal Access Token,别傻傻分不清
2.1 Classic Token与Fine-grained Token的区别
点开GitHub的Settings -> Developer settings -> Personal access tokens,你会看到两个入口:Tokens (classic) 和 Fine-grained tokens。这是两套完全独立的Token生成体系,权限模型差异巨大,选错类型是新手最常见的坑。
先看Classic Token(经典Token)。它比较好理解,生成的时候通过勾选一堆scope(权限范围)来决定Token能干什么——比如repo这个scope代表仓库的全部读写权限,delete_repo代表删除仓库权限,workflow代表读写GitHub Actions工作流文件的权限。Classic Token是"一揽子授权",一旦勾选了repo,就意味着你有权访问名下所有仓库里跟代码相关的操作,哪怕你只想操作其中一个仓库,也得给Token这么大的权。
再看Fine-grained Token(细粒度Token)。这是GitHub后来推出的新版Token体系,权限模型精细得多:你可以指定这个Token只对某个仓库或者某几个仓库生效,再在仓库级别上逐项勾选权限,比如只读代码、读写issues、读写pull requests等。正因为它是按仓库、按权限逐项控制的,所以也叫"最小权限Token"。
官方其实已经将Fine-grained Token定义成"新"的推荐模式,未来Classic Token会逐步被弱化,GitHub官方文档里也建议新用户优先使用Fine-grained Token。但是Fine-grained Token有个现实问题:它配置起来相对繁琐,而且部分老工具或企业内部系统的兼容性还不算完美。我个人的建议是,自己个人开发机上用Classic Token图省事也行,但如果是在公司或者多人协作环境里,尽量用Fine-grained Token去做权限收敛。
2.2 Classic Token的权限项到底该怎么勾
如果你决定先试试Classic Token,有几个高频scope我把它们的含义整理成表格,照着勾就行:
| Scope | 名称含义 | 典型使用场景 |
|---|---|---|
| repo | 私有仓库的完整读写权限 | 拉取或推送私有仓库代码 |
| workflow | 更新GitHub Actions工作流文件 | 提交.github/workflows目录下的代码 |
| read:org | 读取组织信息 | 部分企业内部工具需要读取组织成员信息 |
| gist | 操作Gist代码片段 | 命令行工具需要创建或修改Gist |
| delete_repo | 删除仓库权限 | 一般不勾,风险较大 |
| admin:public_key | 管理公钥 | 用API操作部署密钥时才会用到 |
日常仅做代码拉取和推送的话,勾上repo和workflow两个基本够用。如果只是拉取公开仓库的代码,理论上连repo都不用勾,只勾public_repo就行——不过为了省心,我通常还是直接勾repo。
2.3 Fine-grained Token的配置思路
Fine-grained Token的创建界面是分成几个部分的:Token name(名称)、Expiration(过期时间)、Repository access(仓库访问范围)、Permissions(具体权限)。名称随便写,建议写成能提醒自己的名字,比如"laptop-2025-push",方便过期后一眼认出是哪个设备用的。
仓库访问范围有三个选项:
- Public Repositories only(仅公开仓库)
- All repositories(全部仓库)
- Only select repositories(指定仓库)
如果只是想操作自己的某个项目,就选"Only select repositories",把目标仓库选上。然后下边的Permissions列表里,你会看到几十个权限项,每一项都有"No access"和"Read-only"两种起步选项,部分敏感项还有"Read and write"。对于普通代码推送场景,只需要找到Repository permissions下面的Contents,把它设置成Read and write,这就够了。如果还需要处理GitHub Actions,则把Workflows也设成Read and write。至于Metadata,它默认是只读的,保持不动就行。
需要注意的是,Fine-grained Token生效之前需要等待GitHub后台同步权限,通常几秒到几十秒就能用,极少数情况下会延迟一两分钟,不要一看没生效就重新生成一遍。
3. 从创建到配置,一步步教你跑通
3.1 在网页端创建自己的第一个Token
实际动手创建一次,你会觉得比想象中简单。登录GitHub,点击右上角头像,进入Settings,在左侧菜单最下方找到Developer settings,展开之后能看到Personal access tokens,点击进入。
我以Classic Token为例,流程是:
- 点击Generate new token,在下拉里选择Generate new token (classic)。
- 如果你开启了2FA,GitHub会要求你重新输入一次密码或者验证码,这是安全校验,不用慌。
- 在Note输入框里写一个备注名,比如
home-desktop,目的是以后清理Token时知道它是谁。 - 在Expiration里选择有效期,有30天、60天、90天和自定义选项。强烈建议不要选"No expiration(永久)",因为Token一旦泄露且永不过期,等于把大门钥匙丢了。
- 在Select scopes区域勾选权限,普通推拉代码就勾
repo和workflow。 - 拉到页面底部,点击Generate token,GitHub会生成一串很长的字符串,这串字符串只在当前页面显示一次,关掉页面就再也看不到了,所以此时必须复制并保存到本地密码管理器或者临时文件里。
Token的格式通常是一串乱码开头加上下划线分隔的字符。复制的时候要注意不要把多余的空格带进去,很多认证失败就是复制时多了一个空格或者换行符导致的。
3.2 在命令行里把Token交给Git
创建好Token之后,接下来要让它能和Git协同工作。如果你第一次推送代码,Git会在命令行里弹出一个用户名和密码的输入提示,用户名填你的GitHub用户名,密码那一栏填的不是账户密码,而是你刚创建的Token——把它粘贴进去就能通过认证。
之后再推送就不会反复询问了,因为Git默认会调用系统的凭据管理器把用户名和Token存起来。Windows上通常用的是Windows Credential Manager,macOS上用的是钥匙串(Keychain),Linux上可能是libsecret。如果你的Git之前已经存过旧密码,推送时会直接用旧密码去认证,结果一直报403,解决办法是打开系统凭据管理器,删掉旧的GitHub凭据记录,再重新输一次新的Token。
3.3 把Token写进远程仓库地址
有一种情况我建议手动改远程地址:公司的测试机或者CI编译机上不方便弹交互式输入提示,这时你可以把Token直接拼在远程地址里。格式是这样的:
git remote set-url origin https://用户名:Token@github.com/用户名/仓库名.git举个实际例子,假设用户名是samchen,Token是ghp_xxxxxxxxxxxx,仓库是my-project,那么执行:
git remote set-url origin https://samchen:ghp_xxxxxxxxxxxx@github.com/samchen/my-project.git然后直接git push就能推上去。不过要明白一点,这种写法相当于把Token明文写进了配置里,如果这台机器上还有别人能看配置文件,风险很高。我个人只在临时环境或者一次性构建环境里这么用,平时个人电脑不会这么写,宁可让Git弹出输入框。
3.4 用环境变量保护Token
在自动化脚本里,不推荐把Token硬编码到脚本里。正确做法是用环境变量。在Linux或macOS上,可以在~/.bashrc或者~/.zshrc里追加一行:
export GITHUB_TOKEN="ghp_xxxxxxxxxxxx"Windows上可以通过"系统属性 -> 环境变量"来设置,或者在PowerShell里临时设置:
$env:GITHUB_TOKEN="ghp_xxxxxxxxxxxx"脚本里想用的时候就引用环境变量,比如用GitHub API时:
curl -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user这样一来,Token不会跟着代码仓库走,避免一个不小心把Token提交到公开仓库里的惨剧。
4. 实操演示:推一个新项目到GitHub
4.1 从零初始化并关联远程仓库
纸上谈兵没什么意思,我直接模拟一次完整的推新项目过程。假设本地有个myblog文件夹,里面已经有一堆文章和代码,现在想推到GitHub上一个新建的仓库里。
第一步,进入项目文件夹,初始化Git仓库:
cd myblog git init git add . git commit -m "first commit"第二步,在GitHub网页上创建一个空仓库,复制它的HTTPS地址,形如:
https://github.com/samchen/myblog.git第三步,把远程地址加上去:
git remote add origin https://github.com/samchen/myblog.git第四步,推代码。第一次推的时候Git会让你输用户名和Token,照之前的说法操作即可:
git branch -M main git push -u origin main如果是第一次在这个电脑上使用Git,可能还需要先设置你的用户名和邮箱,否则commit会报错Please tell me who you are。用这两行设置:
git config --global user.name "samchen" git config --global user.email "samchen@example.com"4.2 本地记住Token,避免反复输入
如果你发现每次推代码都要重新输入用户名和Token,多半是Git没开启凭据存储功能。Git默认有一套凭据机制,但不同系统上行为不太一样。执行这句可以开启长期存储:
git config --global credential.helper store这条配置会让Git把凭据明文存到~/.git-credentials文件里,好处是省心,坏处是明文。你如果觉得不放心,可以改用下面这条:
git config --global credential.helper managerWindows上Git for Windows自带的Git Credential Manager就是通过manager调用的,走的是系统安全凭据库,安全性更好。macOS上用osxkeychain,Linux上可以装libsecret或者用store按需选择。
我自己的习惯是:服务器或CI环境用store,个人开发机用manager,这样兼顾效率和安全性。
4.3 CI/CD里使用Token的正确姿势
很多人喜欢把Token直接写在流水线配置里,比如GitHub Actions的YAML文件里硬编码一个Token字符串,这是个大忌。YAML文件本身就在仓库里,一旦仓库代码对外可见(哪怕只是内部可见),Token就等于白送了。
正确做法是把Token存到GitHub仓库的Secrets里。路径是仓库的Settings -> Secrets and variables -> Actions,然后新建一个Repository secret,名字随意,比如REPO_TOKEN,值填你的Token。流水线里这样引用:
- name: Push to repo env: TOKEN: ${{ secrets.REPO_TOKEN }} run: | git remote set-url origin https://x-access-token:${TOKEN}@github.com/samchen/myblog.git git push origin mainx-access-token是GitHub Actions里一个约定俗成的占位用户名,目标仓库看到这个用户名就知道后面跟的是Token。这套用法是我在多个项目里实测没问题的,你可以直接抄。
5. 常见问题与排查技巧实录
5.1 403 Permission denied:Token权限不够
这是我最常被问到的问题,报错长这样:
remote: Permission to samchen/myblog.git denied to xxx. fatal: unable to access 'https://github.com/samchen/myblog.git/': The requested URL returned error: 403这个报错大概率就是Token权限没给够。检查思路有几条:
- 如果你用的是Classic Token,确认勾了
repo;如果你用的是Fine-grained Token,确认给目标仓库开了Contents的Read and write权限。 - 确认Token是刚创建且没过期。过期Token的表现通常不是403,而是401,但有些缓存环境下表现很怪。
- 确认你推的仓库地址和Token授权的仓库一致。Fine-grained Token如果只授权了A仓库,你拿它去推B仓库,GitHub会直接拒绝。
5.2 404 Not Found:GitHub故意隐身的坑
有一种情况非常迷惑:Token完全没配错,仓库地址也没写错,但推送时报404。这个其实是GitHub有意为之的安全策略——当认证身份对目标仓库没有访问权时,GitHub不会告诉你"没有权限",而是假装"仓库不存在",返回404。
所以遇到404报错时,不要急着怀疑仓库地址拼错了,先检查Token对目标仓库是不是真的拥有权限。要么用Fine-grained Token,确认在Repository access里选到了目标仓库;要么用Classic Token,确认勾上了repo。
5.3 401 Authentication failed:Token本身有问题
fatal: Authentication failed for 'https://github.com/.../'这种报错,基本指向Token本身无效。常见原因有:
- Token被复制漏了字符,或者复制时带上了空格。
- Token被手动撤销或过期了。
- 输入用户名时用了邮箱而不是GitHub用户名。用户名应该填GitHub登录名,不是注册邮箱。
- 系统凭据管理器里缓存了一个旧的Token,正在拿旧值去认证。
最后一种情况特别坑,因为Git不会每次都重新读你的输入,而是优先走凭据管理器。解决方法是去系统凭据管理器里搜github.com,删掉旧记录,再重新走一次认证流程。Windows上是控制面板 -> 凭据管理器 -> Windows凭据,找到git:https://github.com,删除。macOS上是钥匙串访问,搜github.com,删除相关条目。
5.4 排查Git配置与本地缓存
有些时候问题不在Token,而在本地的Git全局配置上。执行下面几条命令检查一下:
git config --global --list重点看credential.helper和url.开头的配置。如果你之前试过各种教程,可能会残留一些奇怪的配置。比如有人配过url.https://token@github.com/.insteadOf,这个全局替换规则会把所有GitHub请求都带上某个固定Token,一旦那个Token失效,全局所有仓库都会报错。
确认无误后,再清一次本地凭据缓存:
git credential reject然后输入protocol=https、host=github.com,最后按两次回车。这个操作会强制Git清除指定主机的缓存凭据。之后再执行git push,Git会重新弹出用户名和Token输入框。
5.5 常见的"Token已失效"误判现场
我见过有读者反馈:明明刚生成的Token,第一次用就提示过期。排查下来发现,他在Gist、CodeSpaces或者GitHub另外的产品页面里也生成过同名Token,误把新生成的Token当成旧Token复制出去了。多套Token并存时很容易手滑复制错。
我的建议是,每次生成新Token前,先到后台把不用的旧Token全部删掉,保持界面上只有一个活动的Token。这样既降低误复制概率,也方便管理。后台列表里每个Token都会显示创建时间和最后使用时间,看到最后使用时间永远是"No"的,基本可以断定这个Token早就废了。
5.6 用GitHub CLI顺手诊断认证状态
如果你装了GitHub CLI(gh),可以用它快速检查当前认证状态,这是排查认证问题的一把利器。执行:
gh auth status它会显示当前登录用户名、认证方式(Token还是SSH)以及Token的权限范围。如果显示未登录,可以用gh auth login重新走一遍流程,CLI会自动帮你生成Token并写入Git配置,免去手动填Token的麻烦。我平时给新员工配置开发环境,基本都让他们直接用gh auth login,简单粗暴不容易出错。
6. 高级话题:Token安全管理的几条铁律
6.1 别把Token提交进仓库
这听起来像废话,但GitHub的Secret Scanning工具几乎每天都在扫描到用户误提交的Token。哪怕你的仓库是私有的,也不建议把Token写进任何代码文件里,因为私有仓库同样存在被拖库或泄露的风险。一个简单的检查方法:推代码之前先跑一遍git diff,确认没有出现ghp_这种前缀的字符串。
6.2 定期轮换Token
有效期不是一个形式主义的东西。即使你没发现Token泄露,也建议每隔90天或者180天换一次。直接在后台删掉旧Token,生成新的,然后更新你本地的Git凭据缓存。养成这个习惯后,哪怕某次Token不小心中招,泄露影响的范围也会被限制在一个较短的时间窗口里。
6.3 最小权限原则
尽量不要图省事把Token权限拉到最大,比如Classic Token里把repo、admin全勾上。如果Token只需要拉代码,就只给Read-only权限;需要推送,再考虑开Read and write。Fine-grained Token在这里尤其好用,把"只能操作指定仓库"这个能力发挥出来,出问题的时候能有效缩小损失半径。
6.4 团队场景下的权限收敛
如果你所在的团队规模稍大,不建议成员各自用自己的账户生成Token去操作公有的部署仓库。正确的做法是在团队级别建立专用的部署账号,用部署账号去生成Token,并把它存到密钥管理系统里。这样即使有人离职,也不会带走能访问团队仓库的Token。GitHub也支持Deploy Keys,但这个更接近SSH认证的范畴,和本文的Token不太一样,需要的话单独去研究。
7. 后续还可以怎么扩展
这次把PAT从原理讲到实操,再讲到排错和管理,应该能覆盖绝大多数日常使用场景了。但我实际用下来,还有一些边界情况值得注意,比如配置了多个GitHub账号(工作账号和私人账号)时,全局凭据缓存很容易串账。这时候建议给不同的仓库分别设置独立的local级配置,而不是依赖全局的用户名和Token。
还有个方向是GitHub Actions里直接用自动化方式生成Token——支持通过OIDC(OpenID Connect)做短期Token,不用手动维护一个长期有效的密钥。这个玩法适合已经有一定CI/CD经验的人去摸索,配置稍微复杂,但安全收益非常明显。
我个人的体会是,GitHub的认证体系一直在向"更短时效、更细粒度、更自动化"的方向演进,PAT作为现阶段最主流的个人认证方式,早一点用熟它,后面不管接触CI/CD、自动化脚本还是各类开发者工具,都会省掉一堆不必要的麻烦。最后再提醒一句:新生成的Token一定先复制保存,关掉页面就真的找不回来了。