2026年GitHub密码推送认证失效全解析:PAT排查与SSH迁移指南

1. 2026年还有人在密码推送的坑里,这事本身就值得聊聊

先别笑,就在上个月我还帮两个同事处理过一模一样的报错。一个是刚入职半年的校招生,在本地仓库跑git push直接弹窗要求输入用户名密码,输完密码之后GitHub干脆利落地回了一句"Support for password authentication was removed"。另一个是干了五年的老后端,开启两步验证(2FA)之后,原本每天都在推的仓库突然开始报fatal: Authentication failed,他当场就懵了。

这个标题叫"2026仍高频",不是标题党。GitHub早在2021年8月就强制移除了账号密码的HTTPS推送认证,但Personal Access Token(后面统一叫PAT)作为替代品,到今天依然是开发者日常最容易踩坑的一块。原因很简单:PAT不是密码,但很多人的使用习惯还停留在密码时代。密码是永久有效的,PAT会过期;密码只有一种权限,PAT有几十种权限范围(scope)可以选;密码在GitHub后台就一个,PAT你可以一把一把地生成,也可能一把一把地忘掉。

这篇文章想做的事很朴素:把PAT从生成、配置、失效排查到替代方案彻底讲透。如果你曾经被git push的401折磨过,或者你刚开完2FA发现原来好使的推送突然全挂了,又或者你只是想在2026年把这套机制一次性搞明白、以后再也不用搜教程——那这篇文章就是给你写的。

2. PAT的工作原理:为什么它一到期,你的推送就哑火

很多人对PAT的理解就是"一串代替密码的字符串",这个理解不算错,但只停留在第一层。要真正搞定PAT相关的各种报错,你得把它当成一个有时效、有权限、有绑定关系的小型身份凭证来看。

2.1 一个token到底绑定了哪些东西

你在GitHub网页端生成PAT的时候,页面上会出现一堆选项。很多人不看直接一路Next,这就为后面的坑埋下了伏笔。实际上,一个PAT背后至少绑定了四层信息:

  • 绑定的账号:这个token属于哪个GitHub用户,推送时GitHub会校验这个归属关系。
  • 有效期(expiration):GitHub允许你设置30天、60天、90天,或者自定义天数,甚至选"永不过期"。到期的瞬间,token直接失效,任何请求都会返回401。
  • 权限范围(scopes):这是token的核心。比如reposcope才允许你读写普通仓库,workflowscope允许你推送GitHub Actions的配置文件,read:packages允许你拉取私有包。权限不够的时候,GitHub返回的报错可能是403而不是401,这是很多人排查时被绕晕的原因。
  • 组织授权(SSO):如果你的仓库属于一个启用了SAMLO单点登录的组织,你生成token之后还必须单独给这个组织授权,否则推送时一样报错。

所以当一个token失效,它可能是真的到期了,可能是权限被改了,可能是组织授权掉了,也可能是仓库本身转移了归属。只盯着"过期"两个字排查,容易走进死胡同。

2.2 为什么GitHub要搞"定期过期"这么麻烦的机制

用一个生活化的类比:你家的门锁密码如果几十年不换,一旦泄露,别人能一直自由进出;但如果每三个月换一次,就算泄露了,影响范围也是有限的。PAT就是这个逻辑,GitHub作为全球最大的代码托管平台,每年都有海量的自动化扫描工具在GitHub上爬token,很多人的token因为被扫到而泄露,GitHub甚至会主动发邮件通知你"我们发现你的token泄露了,已经帮你撤销"。

这就是为什么GitHub官方一直建议开发者设置合理的过期时间,而不是选"No expiration"。你在本地存的token,一旦推到公开仓库、被日志打出来、被第三方工具读取,就有泄露风险。定期过期相当于一道兜底防线:就算泄露了,黑客拿到手的时候可能已经过期了。

2.3 "过期"和"失效"是两个完全不同的故障

排查PAT问题最忌讳的就是把所有的认证失败都归因于"过期"。我遇到过太多人,明明token还差两个月才到期,却反复生成新token来"续命",结果问题根本不在token本身。

过期指的是token到达了创建时设定的有效期终点,这是GitHub单方面决定的硬时限,没有任何办法延长,只能重新生成。

失效则是一个更宽泛的概念,常见的有这么几类:

  1. 你或其他协作者在后台删除了这个token。
  2. token的scope不满足当前操作要求。
  3. 仓库被转移到了另一个组织/账号,原token的授权关系不再适用。
  4. 开启了2FA之后,旧的凭证缓存失效。
  5. 组织或者企业策略变更,比如管理员强制要求所有token必须在XX天内轮换。

所以在动手重新生成token之前,先判断一下到底是哪一种。这个判断不需要多么高深的手段,一条命令的事,我放在下一节讲。

3. HTTPS推送失败的完整排查链路:从报错到根因,一步步钉死

这里我不直接给答案,而是把完整的排查链路走一遍。原因很简单:你下次遇到的报错不一定和这次一模一样,但你只要掌握了排查思路,就没什么能难住你的。

3.1 先看报错文本,判断故障类型

Git的报错看着乱,其实分三类,每一类指向的根因完全不同。

第一类:"Support for password authentication was removed. Please use a personal access token instead."

这个报错翻译成人话就是:你还在用账号密码登录,GitHub不认了。这种情况最常见于新装的电脑、新建的仓库,或者git配置里的credential helper指向了空的凭据存储。Git把请求发出去,尝试用缓存里的账号密码做认证,GitHub一看是密码认证,直接拒绝。这里的根因不是token失效,而是压根没有使用token。

第二类:"fatal: Authentication failed for 'https://github.com/...'"

这个报错意味着git已经尝试了当前可用的凭证,但GitHub校验之后拒绝了。引起这个报错的可能原因比较多,包括但不限于:token真的到期了、token输入有误、token权限不足,或者远程URL里写死了旧用户名。这类报错是排查的重点区域,下面会展开细说。

第三类:"remote: Invalid username or token."

GitHub明确告诉你用户名或token无效。先检查用户名是不是写错了,很多人GitHub注册的是邮箱,但推送时用了邮箱去认证,GitHub并不接受邮箱作为用户名。如果用户名没问题,再看token本身。

3.2 检查本地缓存的旧凭证:三平台的"清缓存"操作

许多推送失败,问题不在服务器端,而在你的电脑里存了一堆"过期的旧账"。git本身不保存密码,它是通过credential helper去系统里查的。也就是说,你之前可能输入过一次token,git把它交给了系统的凭据管理器保存,之后每次推送都是调用这个缓存的token。当这个token失效后,git不会自动去问你要新的,它只会反复拿旧的去试,直到失败。

这个时候你光在GitHub后台生成新token是不够的,因为git根本不知道有新token的存在。你需要把旧的缓存清掉。

Windows:打开控制面板 -> 用户账户 -> 凭据管理器 -> Windows凭据。在列表里找到git:https://github.com这一项,点开看看,然后删除。下次推送时git会重新弹窗要求输入用户名和token。

macOS:打开"钥匙串访问"应用,搜索github.com,找到对应的条目后删除。macOS使用osxkeychain作为默认credential helper,删除钥匙串条目之后,git会重新交互式地要求输入凭证。注意这里有个小坑:钥匙串里可能同时存在多个github.com相关条目,特别是如果你曾经用SSH方式连接过,还会有一个github.com-remote之类的SSH密码条目,如果没有把握全删,就专门搜https://github.com这种HTTPS相关的。

Linux:取决于你配置的credential helper。如果用的是libsecret,可以用secret-tool search --all service=git.credential列出当前的凭证;如果用的是最朴素的store模式,直接编辑~/.git-credentials文件,把里面那行带token的URL删掉。

清理完缓存之后,建议顺手检查一下远程URL是否干净。运行git remote -v,如果输出类似https://<你的用户名>@github.com/xxx/xxx.git这种把用户名嵌在URL里的形式,需要修正一下:

git remote set-url origin https://github.com/xxx/xxx.git

因为一旦URL里嵌入了旧的用户名,无论你缓存的token有多新,git都会优先用它,导致认证一直失败。

3.3 用curl验证token本身是否有效

清完缓存之后,如果推送还是失败,就要判断token本身到底行不行。这一步不需要git参与,用curl直接打GitHub的API就行。打开终端,执行:

curl -i -u 你的GitHub用户名:你的token https://api.github.com/user

几个关键判断标准:

  • 返回200 OK,说明token有效,问题可能出在git配置或者权限上。
  • 返回401 Unauthorized,说明token无效、过期或者用户名不对。
  • 返回403 Forbidden,说明token是有效的,但权限不够访问这个资源。比如你在一个有SSO的组织里,token没有授权该组织;或者token缺少reposcope。

这个方法最大的好处是绕开了本地git的缓存干扰,直接测试"最底层凭证"是否可用。我把这个curl命令放在所有排查步骤的最前面做,效率最高。

3.4 开启2FA之后推送失败的深坑

标题里专门点到了"2FA后HTTPS推送失败",这是非常高频的场景,值得单独拿出来说。

2FA,即两步验证。它的本意是增强账号安全,登录网页端的时候除了输入账号密码,还要额外提供一个验证码。但问题来了:HTTPS推送走的是git协议,它没有地方让你输验证码。GitHub的处理方式是不允许密码认证的HTTPS请求,强制要求使用token。所以一旦你开启了2FA,原来"用户名+密码"的推送方式就必须改成"用户名+token"。

如果你开完2FA之后没有更新本地缓存的凭证,那推送必然失败。这也是很多人困惑"我什么都没改,怎么突然就挂了"的真正原因。

注意:开了2FA之后,你原来的密码并不会被GitHub作废,它依然可以用于网页登录,只是不能用于HTTPS推送。这是两个独立的认证通道。

遇到这个场景的处理方式很直接:生成一个新token,然后用前面3.2节的方法更新本地凭证缓存。不需要改任何仓库的远程URL,因为URL没有变,变的只是认证材料。

4. 新PAT生成与本地配置:一步到位的完整操作

排查结束之后,就到了实际解决阶段。这一步做得好,至少未来半年内你不用再碰这个问题。我按照"生成token -> 配置本地 -> 推送验证"的顺序完整走一遍,每个环节的注意事项也一起放进来。

4.1 生成PAT时你要认真选的三个选项

登录GitHub后,依次进入Settings->Developer settings->Personal access tokens->Tokens (classic)。如果你之前用的是Fine-grained token,路径略有不同,但经典PAT的使用面更广,先讲它。

点击Generate new token (classic)之后,你需要认真面对三个选项。

第一是过期时间(Expiration)。GitHub默认给的是30天,你可以下拉改成60天、90天,甚至自定义。我的建议是:日常个人开发用90天,自动化脚本/CI环境用30天到60天,并对到期时间做记录。不建议选No expiration,哪怕它看起来省事。为什么?因为过期时间是你token泄露后的最后一道保险,一旦选了永不过期,保险就没了。90天左右比较平衡:一年最多折腾四次,每次花五分钟更新,完全可以接受。

第二是权限范围(Select scopes)。权限选择的核心原则就四个字:最小够用。普通仓库读写,勾repo就够了;你需要推送GitHub Actions的配置文件.github/workflows/,还需要额外勾workflow权限;你需要读取私有容器镜像,勾read:packages;你需要删除仓库,勾delete_repo。不要一把梭把所有权限全勾上,万一token泄露,黑客拿着你的token能做的事就太多了。

第三是token生成后的保存。这一步GitHub只会显示一次token明文,刷新页面就永远消失了。正确做法是生成后立刻复制,粘贴到密码管理器里。我用的是本地加密的密码管理工具KeepassXC,也有很多人用1Password、Bitwarden。千万别直接贴在记事本里,也别发到公司聊天群,明文保存token等于给自己埋雷。

4.2 把token交给git:三平台凭据管理配置

token生成完毕之后,接下来的问题是"怎么让它被git使用"。很多人习惯把token直接拼在远程URL里,比如git push https://用户名:token@github.com/xxx/xxx.git,我强烈不建议这么干。因为这样写的话,远程URL会出现在你项目的.git/config里,一旦你把这个项目的目录分享出去、截图到群里、或者推到别的代码托管平台,token就跟着泄露了。

正确的姿势是配置git的credential helper,让git按需调用系统凭据管理器。

Windows上,现代Git for Windows默认自带manager-core(即Git Credential Manager)。你只需要在第一次推送时,在弹窗里输入GitHub用户名和token(注意是token不是密码),之后git就会自动保存到Windows凭据管理器。可以验证一下:

git config --global credential.helper

输出如果是manager-core或者manager,就说明默认配置OK。如果是空的,执行:

git config --global credential.helper manager-core

macOS上,默认helper是osxkeychain,这个helper会调用系统的钥匙串。所以只要你第一次推送时在弹出的macOS对话框中输入了token,之后git自动从钥匙串读取,无需任何额外配置。确认一下:

git config --global credential.helper

输出osxkeychain就对了。如果没有,安装Git时勾选对应的选项,或者执行:

git config --global credential.helper osxkeychain

Linux上稍微麻烦一点,因为桌面环境不统一。如果你用的是GNOME桌面,可以装libsecret工具链:

sudo apt install libsecret-1-0 gnome-keyring git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret

对于服务器环境(没有图形界面),很多人选择store模式,也就是把凭证明文存在~/.git-credentials文件里。这种方案安全级别低,但确实是最省事的。我给一个折中建议:服务器上用store模式可以,但记得给这个文件设置权限chmod 600 ~/.git-credentials,至少别让别人随便读。

4.3 验证推送:从配置到落地

配置完成后,我在本地仓库执行一次真正的推送验证,同时观察全过程:

git remote -v git push origin main

第一次推送时,git会触发credential helper弹出输入框。Windows上是图形弹窗,macOS上是钥匙串弹窗,Linux终端则可能直接提示输入。这里的用户名填你的GitHub用户名,密码/口令那一栏填token,不是GitHub账号密码。很多人在这一步习惯性地输入了账号密码,结果又收获一次Authentication failed。

如果push成功,你可以再push一次,这一次不会再弹窗,说明凭证已经被系统缓存了,后续一段时间内你的推送都会是静默的、无感的。如果push依然失败,回到第3节把整个链路再排查一遍,尤其是curl验证token那一步。

我的一个额外经验:配置完成之后,最好主动打开Settings->Developer settings->Personal access tokens,看一眼这个token的Last used时间。如果显示的是刚刚,说明GitHub确实收到了来自这个token的请求;如果显示Never,说明你的git请求根本没走到这个token,问题一定出在本地配置上。这个细节能帮你省下好几个小时的排查时间。

5. 治本方案:从PAT到SSH、Fine-grained PAT的迁移与取舍

刚才讲的全是"怎么把PAT用明白"。但这篇文章的标题既然叫"仍高频",那光教人怎么换token是不够的——如果你一年要换四次token,不管是记性多好的人都可能在某一次遗漏。所以我想把更治本的方案也一起放进来,做一次有效取舍。

5.1 SSH Key:一劳永逸的推送方式

SSH和HTTPS是git远端通信的两大协议。HTTPS用用户名+token认证,SSH用公钥/私钥认证。SSH最大的优势在于:一对密钥生成之后基本不需要更换,只要你的私钥不泄露,它可以一直用下去,不存在"90天过期"这种概念。

切换到SSH的完整过程:

第一步,生成密钥对(如果你没有的话):

ssh-keygen -t ed25519 -C "你的邮箱或备注"

一路回车到结束,默认会在~/.ssh/下生成id_ed25519(私钥)和id_ed25519.pub(公钥)。

第二步,把公钥添加到GitHub。打开Settings->SSH and GPG keys->New SSH key,把id_ed25519.pub的内容整个复制进去,起个备注名保存。私钥留在本地,永远不要发给任何人。

第三步,把本地仓库的远程URL从HTTPS切换到SSH:

git remote set-url origin git@github.com:用户名/仓库名.git

第四步,测试连接:

ssh -T git@github.com

看到Hi 用户名! You've successfully authenticated, but GitHub does not provide shell access.就说明配置成功了。

SSH方案有个额外好处是支持GPG签名、支持在~/.ssh/config里做多账号管理,对于同时维护多个GitHub账号、或者需要频繁操作远程仓库的人来说,体验比HTTPS+token高一个档次。

它也有不适合的场景。最典型的就是CI/CD流水线、GitHub Actions的secrets配置、容器构建环境——这些场景需要的是可以在几十秒内生成、用完即弃、细粒度控制权限的临时凭证,SSH密钥不适合在这种场景里大规模分发。

5.2 Fine-grained PAT:2026年的新选择

GitHub在2022年就开始推广Fine-grained PAT(细粒度访问令牌),到2026年,它已经是新建token时的默认推荐选项了。对比经典PAT,Fine-grained PAT有几个关键区别:

维度Classic PATFine-grained PAT
有效期最长不过期最长1年
权限范围scope级别,比较粗单个仓库/组织级别,可以精确到只读问题、只读代码、读写Actions
仓库可见性授权后默认访问所有可见仓库可以精确选定授权的仓库
组织访问需要额外SSO授权创建时直接指定组织范围和权限
过期时间支持永不过期强制设置,最长为1年

Fine-grained PAT适合需要精细控制权限的场景,比如你的token只用来读某个仓库的issues,或者只用来推送某个项目的Actions配置。但它也有门槛:创建流程更复杂,很多老工具/老脚本对Fine-grained PAT的兼容性不如Classic PAT完美,某些CLI工具可能识别不了它的权限结构。如果你不确定你的工具链支不支持Fine-grained PAT,先用Classic PAT保底,等确认兼容之后再把生产环境的token切过来。

5.3 我的建议:按场景选方案

我自己目前的做法是:

  • 本地日常开发推送:优先SSH key。生成一次,用上几年,不用思考过期问题。
  • 临时脚本、CI流程、自动化任务:用Fine-grained PAT,最短有效期+最小权限。
  • 第三方工具需要访问GitHub(比如IDE的内置Git面板、代码扫描工具):分情况,能用SSH就SSH,不能的话用Classic PAT,并把scope限制到最小。

这套组合下来,我大概有将近一年没因为token问题被卡过推送了。不是说PAT不好,而是你得让工具出现在合适的场景里,才能发挥它最大的价值。

6. 我在这个坑里爬出来的几条实操经验

最后这部分不聊技术原理了,聊点踩坑踩出来的肌肉记忆。有些东西文档里不会写,但实际工作中非常管用。

第一条:创建token之后,立即在终端里跑一次git push。很多人创建完token就关掉页面,过了几天才用。等真正推送失败的时候,已经记不清这个token当时勾了哪些权限、设了多长有效期。创建完立刻推一次,既验证了token可用,也把配置链路整体通了一遍,之后再用就心里有底。

第二条:在密码管理器里面记录token的到期时间。我用的KeepassXC支持给每条记录设置过期提醒,我会在token创建那天顺手在备注里写"到期时间:2026-XX-XX",到期前一周收到提醒就去换新。如果你用的密码管理器不支持提醒,那就新建一个日历事件,用手机日历同样能做。看似多了一步,实际能帮你避免在某个赶进度的深夜被推送失败突然打断。

第三条:旧token不要立刻删。当你生成了新token,准备替换旧token时,不要马上在GitHub后台把旧token删除。先确认新token在所有设备、所有CI环境都配置好了,再删旧的。我之前吃过一次亏:只更新了本地电脑的token,忘了CI环境里还写着旧token,一删CI直接全线挂掉。稳妥的做法是:旧token保留48小时,确认无误后再清理。

第四条:小心复制token时带入不可见字符。这个坑非常隐蔽。有时候你从网页复制token,中间会混入换行符或者空格,尤其是当你在终端里粘贴时,部分终端会自动补一个回车。这时候你看到的token和实际发送的token不一样,认证永远失败,但你看不到问题在哪。建议粘贴token之后,在终端里跑一下echo "粘贴的内容"检查有没有多余的空格或换行,再确认无误。

第五条:定期检查GitHub后台的Active tokens列表。GitHub的Security log会记录所有敏感操作,token的创建、删除、授权变更都会留痕。每个月花两分钟扫一眼,如果发现某个token在异常时间被访问、或者出现你自己不记得创建过的token,立刻去后台撤销掉。这种习惯花的时间极少,但可能在某个关键时刻帮你避免一次严重的安全事故。

PAT这个东西,说到底是GitHub在密码和更高级安全方案之间的一个过渡产物。它比密码安全,但比SSH key麻烦;它支持细粒度控制,但配置复杂度又比SSH高。2026年了,还会有大量开发者被困在这个环节,本质上就是因为"过渡"这两个字——它既没有完全退出历史舞台,也没有好用到让人一次配置永久无忧。搞清楚它的机制,配合合适的场景选择Classic PAT、Fine-grained PAT或者SSH key,这件事就能彻底从你的待办清单里划掉。