☰
Git认证失败排查:公钥设置与Access Token完整解决路径
2026/10/21 4:20:54 网站建设 项目流程

做后端或运维的同学,大概率被这句报错“问候”过:Incorrect username or password ( access token )。它经常在拉取代码、推送分支、更新子模块的时候突然蹦出来,明明密码是对的,公钥也配好了,Git 却死活不认账。这篇文章就围绕代码托管平台最让人头疼的两个认证问题展开:公钥的设置与修改,以及access token 导致认证失败时的完整解决路径。我会把 HTTPS 和 SSH 两条链路分开讲,先把报错的根源拆清楚,再给出可以直接抄的实操步骤,顺便把我踩过的坑和总结出来的排查顺序都交代清楚。不管你是刚入门的新手,还是被这个问题卡了一下午的人,按这个思路走,基本能自己定位和修复。

1. 先搞懂报错到底在说哪层认证

1.1 同一句报错,两种完全不同的原因

很多人看到Incorrect username or password的第一反应是“我是不是密码改过忘了”,于是先去网页端改密码,改完回来继续拉代码,结果照样报错。说实话,我第一次遇到这个问题时也干过这种蠢事,折腾了快一个小时才反应过来:Git 在这里报的错,不是“你的密码不对”,而是“你这次请求携带的凭据没有通过服务端校验”。

这里的关键在于,Git 的远程地址有两种完全不同的协议形态。一种是 HTTPS,走的是 HTTP 基础认证,服务端只认用户名 + 密码或者用户名 + access token;另一种是 SSH,走的是公钥认证机制,服务端通过你预先添加的公钥来判断本地是否持有配对的私钥。

所以那句Incorrect username or password绝大多数情况下只会出现在 HTTPS 协议下,是服务端对你 HTTP Basic Auth 的通用拒绝信息。注意,它不会告诉你到底哪个字段错了,也不会区分你是填错用户名、填错密码、还是 token 过期。只要认证没过去,就是这同一句话。而 SSH 如果失败,报的通常是Permission denied (publickey)。因此,看到报错之后的第一件事,不是去折腾公钥,而是先确认当前 remote 地址用的到底是什么协议。

1.2 为什么 access token 是 HTTPS 的“密码”,而不是公钥

很多教程都会讲“把公钥添加到平台”,于是不少人就形成了一个印象:只要公钥配好了,怎么拉代码都没问题。这个理解在 SSH 协议下成立,但换到 HTTPS 协议下就完全不是一回事。

你可以把 SSH 公钥理解成“门禁卡系统里的卡号”,服务器那边记录了你的卡号,你的电脑里保存着对应的“刷卡动作”。HTTPS 则像是“密码锁”,服务器不关心你有没有门禁卡,它只关心你输入的用户名和密码是否正确。因为现在很多代码托管平台出于安全考虑,已经不允许直接用账号登录密码来执行 Git 操作,而是要求你在网页端手动生成一个私人令牌,也就是 access token / 私人令牌,再把令牌当作 HTTPS 的密码来用。

这也是为什么你的 SSH 公钥设置得再完美,只要 remote 地址是 HTTPS,出现Incorrect username or password ( access token )就是家常便饭。因为你根本不是在跟公钥打交道,而是在跟 token 打交道。标题里把“公钥的设置和修改”和“access token 报错”放在一起,其实就是两种高频场景的合集:SSH 链路要看公钥,HTTPS 链路要看 token,两个都得会。

1.3 30秒判断你当前用的是哪种认证方式

判断方法非常简单,打开终端,在任意一个 Git 项目目录下执行:

git remote -v

看到类似这样的输出:

origin https://example.com/username/repo.git (fetch) origin https://example.com/username/repo.git (push)

那说明当前项目走的是 HTTPS,你要去处理 access token 和本地凭据缓存的问题。

如果输出是:

origin git@example.com:username/repo.git (fetch) origin git@example.com:username/repo.git (push)

那才是 SSH,你要去检查公钥、私钥、ssh-agent 这些东西。

这一步 30 秒就能完成,但它决定了你后续排查的方向。很多人一纠结就往公钥那一侧钻,结果方向错了,时间全浪费了。

2. 公钥的设置:从生成到生效的完整链路

2.1 生成密钥对之前,先理解公钥和私钥的分工

公钥和私钥的关系,我用一个生活化的类比来说:公钥是锁,私钥是钥匙。你把锁(公钥)交给平台,挂在你的账号名下;钥匙(私钥)留在你本地,绝对不传给任何人。SSH 握手的时候,服务器那边拿着你公开的“锁”,向你发起一个挑战:如果你能证明你持有对应的“钥匙”,就放你通过。服务器只知道公钥,不知道你的私钥;你的私钥也不会在网络传输中出现。

这里最重要的一个原则就是:公钥可以随便给别人看,但私钥一旦泄露,就等于你这把锁的钥匙被人配了一把,任何持有私钥的人都能以你的身份访问代码仓库。所以私钥文件一定要注意保护,不要放到公共网盘,更不要随手粘贴到聊天工具里发给别人。

2.2 命令行生成密钥对的实操

在本地生成密钥对,推荐优先使用 Ed25519 算法,它的安全性高,密钥短,验证速度快。命令是:

ssh-keygen -t ed25519 -C "your_email@example.com"

-C后面的内容是一个注释,通常写你的邮箱,方便在平台后台识别这把公钥是谁的。执行后,终端会问你要保存到哪个文件:

Generating public/private ed25519 key pair. Enter file in which to save the key (/home/yourname/.ssh/id_ed25519):

直接回车,使用默认路径即可。接着会提示输入口令(passphrase):

Enter passphrase (empty for no passphrase):

建议设置一个强口令。虽然每次 SSH 连接都要输一遍口令有点烦,但后面搭配 ssh-agent 就能省掉重复输入。口令的作用很大,即使私钥文件不小心被人拿走,没有口令他也用不了。

如果你的协作环境比较老,某些服务器只支持 RSA 密钥,那就改用:

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

RSA 用 4096 位长度基本是标配,低于 2048 的已经不推荐了。生成完成后,~/.ssh目录下会出现一对文件:id_ed25519(或id_rsa)是私钥,id_ed25519.pub(或id_rsa.pub)是公钥。这两个文件的权限也要注意,私钥权限不能太开放,否则 SSH 会拒绝使用它。

2.3 公钥添加进平台,私钥交给本机 ssh-agent

公钥的内容可以用下面的命令查看:

cat ~/.ssh/id_ed25519.pub

输出是一整行以ssh-ed25519 AAAA...开头的字符串。你需要把这整行复制下来,然后登录代码托管平台的网页端,找到“安全设置”或者“SSH 公钥管理”之类的入口,填一个便于识别的标题(比如“我的笔记本”),再把公钥粘贴进去,保存。

接来下是本机私钥的管理。为了让 ssh-agent 帮你管理私钥,可以在终端执行:

eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519

ssh-add会把私钥加载到内存中的 ssh-agent 进程里。如果你设置了口令,这一步骤会询问口令;加载之后,只要电脑不重启,后续 SSH 连接都不需要再输口令。macOS 用户还可以用ssh-add --apple-use-keychain把私钥写进钥匙串。

有一个非常容易犯的错误:把公钥当私钥传给了平台,或者复制时少复制了几个字符,导致后台显示的公钥不完整。粘贴公钥的时候,一定要复制完整的一行,不要手动换行,不要加多余空格。

2.4 用 ssh -T 验证链路是否打通

配置完成后,验证是否生效:

ssh -T git@example.com

如果看到类似Hi <用户名>! You've successfully authenticated的提示,说明 SSH 公钥链路已经打通。如果看到Permission denied (publickey),就说明服务器没能识别你的私钥。

这时候按顺序排查:

  1. 确认本机~/.ssh/id_ed25519这个文件存在,且~/.ssh/config没有干扰项。
  2. 确认平台账号里已经添加了对应的公钥,而且公钥内容与本地id_ed25519.pub完全一致。
  3. 如果配置了多个私钥,可以用-i手动指定:
ssh -T -i ~/.ssh/id_ed25519 git@example.com
  1. 确认~/.ssh/known_hosts里面没有记录的旧主机指纹和当前服务器不一致的情况,可以把对应行删掉再试一次。

这一条链路要完整走通,才算真正解决了 SSH 下的认证问题。

3. 公钥的修改与多设备场景下的管理

3.1 什么时候需要修改公钥

很多人以为公钥设置好之后就一劳永逸了,实际上它也是需要维护的。常见触发修改公钥的场景有这么几类:

  • 换了新电脑,想把旧电脑上的访问权限收回来。
  • 旧设备丢失或怀疑私钥泄露,需要立即让旧公钥失效。
  • 团队账号或者个人账号更换了常用邮箱,想更新公钥注释。
  • 平台后台误删了公钥,导致本地 SSH 连接突然失败。
  • 一台电脑上要同时管理多个代码托管平台的多个账号,需要为不同场景准备不同密钥。

关键在于,代码托管平台通常允许一个账号添加多个公钥,所以“修改”不一定是把旧的删掉再新增一个,有时候只是“新增一个新公钥”而已。在删除旧公钥之前,先想清楚这台设备上还有没有正在使用的自动化任务,比如定时脚本、CI/CD 机器,别一头热把旧公钥删了,结果别的机器全断连。

3.2 修改公钥的完整动作

如果需要修改公钥,我建议按以下顺序操作:

  1. 在本地重新生成一对新的密钥:
ssh-keygen -t ed25519 -C "new_comment@example.com" -f ~/.ssh/id_ed25519_new
  1. 查看新公钥内容:
cat ~/.ssh/id_ed25519_new.pub
  1. 登录平台后台,把旧公钥删除,把新公钥添加进去。如果你只是想新增一台设备,可以保留旧公钥,直接添加新设备对应的公钥。

  2. 在本机配置好新的私钥路径,加载到 ssh-agent:

ssh-add ~/.ssh/id_ed25519_new
  1. 验证新密钥:
ssh -T -i ~/.ssh/id_ed25519_new git@example.com
  1. 最后在项目目录里拉取一次代码,确认远端操作正常。

这里有一个细节需要注意:如果把旧公钥删掉了,但本地 git 项目里某些 remote 地址仍然指向旧的 SSH 别名,或者 SSH config 里还绑定了旧私钥路径,即使公钥已经换了,连接还是会走错。所以修改公钥之后,最好把~/.ssh/config和项目的.git/config都整体检查一遍。

3.3 多台设备的公钥管理:一份公钥的复制与多账号配置

多台设备用同一份私钥,在技术上是可以的,但我个人不推荐。每台设备生成独立密钥对,再把对应的公钥添加到同一个平台账号下,才是更稳妥的做法。这样即使某台设备出了问题,其他设备不受影响,只需要在后台删除出问题的那台公钥即可。

当然,如果你确实需要在一台新电脑上快速复用旧电脑的私钥,也不是不能做。把旧电脑上的私钥文件复制到新电脑的~/.ssh/目录下,注意修改文件权限为600,然后加载到 ssh-agent:

chmod 600 ~/.ssh/id_ed25519 ssh-add ~/.ssh/id_ed25519

但这条路只适合在你自己完全可信的设备之间做,而且私钥一定要有口令保护。有一次我把私钥从主力机复制到备用机,忘了一口令是空的,后来备用机丢了,只能立刻去平台后台把旧公钥删掉,重新生成一对。踩过那次坑之后,我再也没有复制过私钥,全部改为每台设备独立生成。

3.4 使用SSH config解决多密钥切换

当同一台电脑要连接两个不同的平台账号,或者要同时使用公司内部 Git 服务和个人的代码托管平台时,默认的id_ed25519往往不够用。因为在 SSH 连接时,客户端默认只会尝试加载符合默认名称的私钥;如果服务器不认识这把私钥对应的公钥,连接就失败。

这时候可以在~/.ssh/config文件里做多主机配置:

Host work HostName git.example.com User git IdentityFile ~/.ssh/id_ed25519_work Host personal HostName git.example.org User git IdentityFile ~/.ssh/id_ed25519_personal

这样配置后,远程地址就可以写成:

git remote add origin git@work:username/repo.git

或者:

git remote add origin git@personal:username/repo.git

SSH 会根据别名work和personal自动选择对应的私钥。注意,HostName是你真实要连接的服务器地址,User在绝大多数 Git 托管平台上都是git。这个文件里也可以统一设置StrictHostKeyChecking ask等安全选项,避免第一次连接时被中间人攻击。

4. access token 的生成与报错实战排查

4.1 token正确生成,为什么还是报错

tsc 只要 remote 是 HTTPS,就会遇到Incorrect username or password ( access token ),哪怕你已经按照说明生成了 token,也还是可能报错。根据我的经验,常见的隐藏原因有这么几个:

  • token 复制不完整,或者多复制了一个空格。很多网页端在复制 token 时会在末尾带入换行符,粘贴到终端里就会出现问题。
  • token 权限不够。有的平台生成 token 时会让用户勾选权限范围,比如projects、repository、read/write,如果只勾了读权限,你执行 push 操作当然会失败。
  • token 设了有效期且已经过期。有些平台强制要求设定有效期,过期之后不会自动续期,只能重新生成。
  • 本地凭据管理器缓存了旧密码。这是最隐蔽的一个原因,系统会优先使用缓存里的凭据,而不会弹窗重新询问。
  • 用户名填错。有的平台要求用户名,有的平台要求邮箱,有的平台两种都接受,但如果你填的格式不对,照样失败。
  • token 中包含@、/、:等特殊字符,直接拼接在 URL 里时没有编码,导致 URL 解析出错。

4.2 让remote URL切换到带token的HTTPS地址

最简单粗暴的方式是重新设置 remote 地址,把 token 直接写在 URL 里:

git remote set-url origin https://username:your_token@example.com/username/repo.git

然后执行git pull验证。但我不推荐这种做法,因为 token 会明文写在.git/config文件里,只要有人能拿到你的仓库目录,就等于拿到了 token。更好的做法是先把 remote 地址改成不带 token 的 HTTPS 地址:

git remote set-url origin https://example.com/username/repo.git

然后执行:

git pull

这时候 Git 会弹出交互式认证窗口,用户名填你的平台用户名,密码栏粘贴 token。凭据管理器会记住新的 token,后续操作就不再报错。

如果你非要手动修改.git/config,也很简单,找到[remote "origin"]段,把url改成对应的 HTTPS 地址即可。注意 URL 的格式,https://后面先跟用户名,用户名和 token 之间用冒号,再跟@和服务端地址,顺序不能反。

4.3 清空本地凭据缓存,强制重新认证

如果你已经重新生成了 token,但操作时还是提示旧凭据不对,那基本可以确定是本地缓存的问题。不同操作系统清理方式不太一样。

Linux 下如果使用store模式,凭据会保存在~/.git-credentials文件里,直接把对应行删掉,或者执行:

git config --global --unset credential.helper

如果使用cache模式,可以等待超时自动过期,也可以执行:

git credential-cache exit

Windows 下最常见的是“凭据管理器”缓存,打开控制面板 → 用户账户 → 凭据管理器 → Windows 凭据,找到包含代码托管平台地址的条目,删除即可。也可以用命令行:

cmdkey /list | findstr /i "example.com"

找到目标后执行:

cmdkey /delete:target=git:https://example.com

macOS 下则是钥匙串访问,在“钥匙串访问”App 里搜索平台地址,把对应条目删除;或者用命令:

git credential-osxkeychain erase

按照提示输入主机名和协议后回车,再清掉。

清完缓存之后,再次执行git pull,Git 会重新要求输入用户名和密码。这时候填新的 token,基本就能通过了。

4.4 问题速查表

场景报错信息大概率原因解决办法
SSH 连接被拒Permission denied (publickey)公钥未添加 / 私钥不匹配检查后台公钥、私钥路径、ssh-agent
SSH 卡在口令输入Enter passphrase for key私钥设置了口令,未加载到 agent执行 ssh-add
HTTPS 拉取报密码错Incorrect username or password ( access token )token 过期或权限不足重新生成 token,检查权限
HTTPS 拉取报密码错Incorrect username or password ( access token )本地凭据缓存旧值清理凭据管理器 / 钥匙串
新电脑无法 clonePermission denied (publickey)新电脑没有私钥生成新密钥,添加公钥到平台
同一台电脑多账号冲突Permission denied (publickey)SSH config 未配置,加载错私钥编写 ~/.ssh/config 指定 IdentityFile
平台后台公钥删了Permission denied (publickey)公钥已失效重新添加公钥

这张表是我自己整理的,不一定覆盖所有平台的所有细节,但绝大多数情况都能在里面找到对应方向。

5. 一次完整排查实录

5.1 现场现象

前阵子有个朋友 A 找我,说他在新电脑上 clone 公司内部的代码仓库时,遇到了Incorrect username or password ( access token )的报错。他在旧电脑上是能正常操作的,新电脑也按照教程生成了 SSH 公钥并添加到了平台后台,但 clone 依然失败。

当时他的第一反应是公钥没配好,又去后台检查了几遍,还重新生成了一次公钥,结果问题照旧。我让他把报错现场发过来,代码如下:

git clone https://example.com/username/repo.git Cloning into 'repo'... remote: Incorrect username or password ( access token ) fatal: Authentication failed for 'https://example.com/username/repo.git'

到这里其实问题已经很明确了:clone 使用的是 HTTPS 地址,不是 SSH 地址,所以公钥无论怎么配都是白搭,因为服务端走的是 HTTP Basic Auth,根本不看公钥。

5.2 逐步排查与处理结果

我按下面的顺序带他处理:

第一步,确认 remote 协议。因为 clone 命令里写的就是 HTTPS,所以直接确定是 HTTPS 认证问题。

第二步,检查 token 权限。他登录平台后台,重新生成一个新的 token,并确保勾选了仓库读写权限。

第三步,清理本地凭据缓存。新电脑虽然没有缓存旧凭据,但为了保险,还是把凭据管理器里可能有的杂项清理了一遍。

第四步,执行 clone 命令,不要直接改 remote,而是让 Git 弹出认证窗口。在提示输入用户名时,填平台用户名;提示输入密码时,粘贴刚才生成的 token。

这一次 clone 成功。事后我问他,为什么会在新电脑上跑去配置 SSH 公钥?他说看了一些教程,说是先配公钥再 clone。实际上,教程里的公钥配置主要服务于git@开头的 SSH 地址,他拿 HTTPS 地址去操作,自然是南辕北辙。这也是我反复强调“先看 remote,再决定动公钥还是动 token”的原因。

6. 日常维护建议

6.1 把身份认证信息从 URL 里剥离

很多人为了省事,喜欢把用户名和 token 直接写进 remote URL。这样做短时间内能通过认证,但存在两个隐患:一是 token 明文留存,二是后续只能手动改 URL 才能更换凭据。我个人的做法是统一用不带凭据的 HTTPS 地址,让系统凭据管理器来存 token,这样每次换 token 只需要清理一次缓存,不需要改动项目配置。

6.2 别把所有密钥都用默认文件名

当你有多个代码托管平台账号时,如果所有密钥都叫id_ed25519,SSH 会默认使用同一个私钥去连接不同服务器,很容易出现“在这台机器上配了 A 平台的公钥,连 B 平台时却用了 A 平台的私钥”这种错乱。给每个平台、每个账号起不同的密钥文件名,再通过~/.ssh/config做映射,是更清晰的做法。

6.3 定期清理失效公钥

我见过不少团队成员的平台账号下挂着三四把公钥,其中一半是已经不用了的旧电脑生成的。公钥越多,安全隐患就越大。建议每隔半年检查一次后台公钥列表,把不认识的、不再使用的公钥删掉,并为每把公钥写上清晰的备注,比如“2024款笔记本”“公司台式机”,方便日后识别。

6.4 关于报错信息的文字游戏

Incorrect username or password ( access token )这句报错真的很误导人,它把所有认证失败都归成同一句话。实际排查时,不要被这句话牵着走。你可以把它拆成三个问题:协议对不对(HTTPS/SSH)、凭据新不新(token 是否过期)、本地缓存的凭据有没有被系统优先使用。按这个顺序排查,基本五分钟就能定位问题。

我个人在实际操作中的体会是,认证问题最怕的就是“凭感觉乱试”。先看git remote -v,再用ssh -T测 SSH 链路,最后才碰 token 和缓存,这套顺序我用了很久,几乎没有失手过。希望这篇内容也能帮你少走一些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询