1. 报错场景与根因分析
最近连续有好几个人私信问我同一个问题,都是项目里在跑git clone或git push的时候突然弹出一行看起来很严肃的提示:
remote: Invalid username or token. remote: Password authentication is not supported. fatal: unable to access 'https://xxx/xxx.git/': The requested URL returned error: 403如果你是在拉取或推送私有仓库时遇到的这个报错,大概率不是网断了,也不是仓库权限被回收,而是认证方式没跟上服务端的新规则。很多人在第一次看到Password authentication is not supported时都很懵,以为是自己密码输错,反复重试之后依然被拒。这个报错背后其实是一个很典型的机制升级:代码托管平台已经逐步关闭了“用户名+密码”直接访问仓库的通道,改成了更安全的令牌认证或者 SSH 密钥认证。
下面我把整个链路拆开讲清楚,从错误信息本身到三种可行方案,再到实际踩坑记录,一次性帮你理顺。
1.1 错误信息到底在说什么
先看报错里的几行关键内容:
remote: Invalid username or token.
服务端在明确告诉你,它收到了认证请求,但无法识别提交过来的用户名或令牌。这句话表达得很克制,但实际含义通常是“你给的凭证不对”或者“凭证根本没有被当成令牌来解析”。remote: Password authentication is not supported.
这句话才是重点。服务端是在说:我不支持密码认证了,请换用令牌或者其他方式。如果你在终端提示里输入的是账号密码,不管输得对不对,都会被直接拒绝,因为服务端在认证阶段就关掉了这个通道。fatal: unable to access ... The requested URL returned error: 403
这是 Git 客户端的最终表现。403 代表服务端理解请求但拒绝执行。到这里,整个拉取或推送就被中断了。
很多人把 403 误认为权限不足,跑去改仓库权限,折腾半天也没用。真正的逻辑链条是:服务端不支持密码认证 → 你还在用密码/错误的用户名 → 认证失败 → 返回 403。只要把认证方式换成令牌或 SSH,问题通常就迎刃而解。
1.2 为什么服务端要禁用密码认证
早期很多私有仓库都支持直接在 HTTPS 地址里填写用户名和密码,确实简单方便,但安全问题越来越多。密码一旦泄露,意味着整个账号完全暴露,尤其当用户在不同平台复用同一个密码时,风险更大。而且密码不适合做细粒度授权,要么能访问全部仓库,要么全部不能访问,很难做到只给某个项目一个临时只读权限。
令牌是对密码的一种替代机制。它本质上是一串随机生成的字符串,但可以设置有效期、可以限定权限范围、可以随时单独吊销。就算某个令牌泄露,也不会影响到账号密码,也不牵连其他设备上的令牌。SSH 密钥也是类似思路,用户手里保留私钥,平台上只存公钥,私钥不出本地,安全性更高。
所以看到Password authentication is not supported的时候,不要觉得是服务商故意为难你,这是行业整体的安全趋势。你不该再去找“还能不能用密码”的办法,而应该顺势切换到新机制。
1.3 哪些场景最容易触发这类报错
根据我平时在各种环境里排查的经验,最容易踩到这个报错的有四类场景:
| 场景 | 表现 | 核心原因 |
|---|---|---|
| 新环境首次 clone 私有仓库 | 刚装完 Git,clone 到一半报 403 | 远程地址是 HTTPS,本地没有配置令牌 |
| 换电脑或重装系统后 push | 凭据管理器里没有保存新凭证 | 没生成或没填入令牌 |
| 开启了双重认证的老账号 | 密码明明能登录网页,Git 却连不上 | 双重认证下密码必须配合令牌使用 |
| 自动化脚本/CI 里使用旧密码 | 脚本里写死用户名:密码 | 服务端已禁用密码认证,必须改用令牌 |
在这些场景里,报错信息往往一闪而过,不会给你太多上下文。你需要在脑中建立一条排查顺序:先确认远程地址,再确认认证方式,最后再检查令牌权限和有效期。
2. 方案一:先用个人访问令牌顶上去
如果你的第一诉求是“赶紧让我把代码拉下来”,那最快的方法就是生成一个个人访问令牌,用令牌代替密码。
个人访问令牌在不同平台有不同的叫法,有的叫访问令牌,有的叫私人令牌,英文一般都叫 Personal Access Token,缩写 PAT。不管名字怎么变,生成和使用的逻辑是相通的。
2.1 令牌应该怎么生成
生成令牌的入口一般在代码托管平台的账户设置里。具体路径通常是这样:
- 登录平台网页端,点击右上角头像或菜单,进入账号设置。
- 找到“开发者设置”“安全设置”或类似的入口。
- 找到“Personal access tokens”或“访问令牌”页面。
- 点击“Generate new token”,系统会要求你先输入一次账户密码或完成二次验证。
- 进入令牌配置页面,设置令牌名称、有效期和权限范围。
- 权限范围里勾选
repo这类仓库读写权限;如果涉及到 CI/CD 或自动化工作流,可能还要勾选workflow等额外权限。 - 点击生成后,页面上会出现一串明文令牌,记得立刻复制保存。很多页面在你跳转之后就不允许再次查看完整令牌了。
这里有个细节值得多说几句:令牌生成时,权限范围务必按最小权限原则选。比如你只用来 clone 公开仓库的某些配置,那也许只需要只读权限;如果平时还要拉取私有仓库并推送代码,至少需要repo权限。一次把权限拉到最大,虽然方便,但一旦泄漏,破坏范围也更大。
2.2 在 clone 和 push 命令里使用令牌
拿到令牌之后,你可以用下面几种方式把它传给 Git。
第一种,直接在远程地址里带上用户名和令牌:
git clone https://你的用户名:你的令牌@host/组织名/仓库名.git这种写法虽然直观,但我并不推荐日常使用。令牌明文出现在命令里,很容易被终端日志、shell 历史记录,甚至是屏幕分享时看到。只适合临时测试或者服务器上一次性初始化。
第二种,用不带令牌的地址,让 Git 单独提示你输入用户名和令牌:
git clone https://你的用户名@host/组织名/仓库名.git执行之后 Git 会提示输入密码,这时候把令牌粘贴进去作为密码即可。用户名如果输错,后面令牌填得再正确也没用。
第三种,也是我认为最可控的方式,是不在 URL 里预设用户名,直接在终端交互提示中输入:
git clone https://host/组织名/仓库名.git Username: 你的用户名 Password: 你的令牌这样可以避免 shell 历史记录里留下令牌痕迹,也便于观察到底是哪一步认证出了问题。
push 和 clone 的认证逻辑一致。第一次 push 之后,如果你配置了凭据管理器,Git 会记住这次使用的用户名和令牌,后续命令就不需要重复输入了。
2.3 令牌的权限范围与有效期,别贪省事
令牌不是永久的。常见的有效期选项是 7 天、30 天、90 天或自定义时长。建议根据自己的实际需求来设置。如果这个令牌是给个人电脑长期开发用的,可以设得长一点,但至少要有一个明确的到期日,强迫自己在到期前重新走一遍认证流程。如果是给临时任务或 CI 用的,短期令牌更合适。
权限范围也要看清楚。不要一上来就把所有权限全部勾选。很多平台把仓库读、仓库写、删除、分支管理、工作流等权限分开列出来了。你只需要普通 push 的话,重点勾选仓库写权限。如果你从不在命令行里操作 workflow 配置,没必要把那个权限也打开。
我之前就犯过一个错误:为了省事,生成一个全权限令牌放在配置文件夹里,结果脚本被误提交到了公共仓库,令牌随仓库一起暴露。好在及时发现后吊销并重新生成,才没有造成实际损失。从那以后,我的令牌都严格按照“最小够用”来配置。
提示:令牌一旦泄露,无论过期时间有多远,都要立刻去平台设置里吊销并重新生成。不要存在项目目录里,更不要提交到仓库。
3. 方案二:换成 SSH,一次性根治
如果你已经受够了令牌过期、权限勾选、输入一堆字符串这些麻烦,SSH 是更省心的长期方案。SSH 不像 HTTPS 那样每次都要考虑“这次该用哪个用户名和密码”,而是靠一对公私钥完成身份认证。私钥放在你本地,公钥放在平台账户里,之后 clone、push、pull 都走加密通道,不需要在命令中反复输入凭证。
3.1 为什么建议直接把线路切到 SSH
HTTPS 加令牌虽然能解决问题,但属于“修复症状”。SSH 的作用更像是“换一条更稳的路”。
首先是免密体验。SSH 密钥配置好之后,除非你给私钥设置了密码短语,否则后续所有 Git 操作都不会再弹输入框。对于频繁 push 的人来说,这个体验差距非常明显。
其次是权限隔离。一个 SSH 密钥对应一个账户,你可以在不同机器上生成不同的密钥对,分别加入不同账户。即使某一台电脑上的私钥泄漏,你也可以只删除该密钥对应的授权,不影响其他设备。
最后是稳定性。很多企业和学校网络策略比较严格,HTTPS 端口经常被代理拦截,但 SSH 的 22 端口或 443 端口相对更容易放行。这在实际工作中是个非常实用的优势。
3.2 生成并配置 SSH 密钥
生成密钥很简单。如果你用的是较新版本的 Git,直接执行:
ssh-keygen -t ed25519 -C "你的常用备注"命令会让你选择私钥保存路径和设置密码短语。默认路径是~/.ssh/id_ed25519,如果本机之前没有生成过其他密钥,直接回车用默认路径就行。密码短语这一项可以留空,也可以设置一个本地口令。留空就代表完全免密,设置了口令后每次使用私钥时都会要求输入一次。
如果你的系统或服务端对 ed25519 支持不佳,可以改用 RSA,比如:
ssh-keygen -t rsa -b 4096 -C "你的常用备注"生成完成后,屏幕上会输出公钥文件路径。一般公钥是.pub结尾的那个文件。用下面的命令查看内容:
cat ~/.ssh/id_ed25519.pub复制这段公钥,到代码托管平台的设置里找到“SSH keys”或者“SSH 公钥”页面,粘贴保存。保存时会要求你为这个公钥起一个名字,一般写“公司电脑”“家用笔记本”“服务器”之类便于区分的名称。
密钥添加成功后,先用一行命令确认连通性:
ssh -T git@host如果配置正确,你会看到类似“成功认证”或者“欢迎回来”的提示文字。如果看到权限拒绝,检查公钥内容和用户名是否正确,尤其注意git@host里的host要替换成你实际使用的域名。
3.3 把现有仓库切到 SSH
对于已经用 HTTPS 克隆下来的仓库,切换远程地址只需要一条命令:
git remote set-url origin git@host:组织名/仓库名.git执行后可以验证一下:
git remote -v看到origin后的地址从https://...变成了git@...就说明切换成功。之后你再执行git push或git pull,会发现终端不再询问用户名和密码了。
如果机器上有多个 SSH 密钥对应多个账户,需要借助~/.ssh/config文件区分。比如:
Host work HostName host1 User git IdentityFile ~/.ssh/id_ed25519_work Host personal HostName host2 User git IdentityFile ~/.ssh/id_ed25519_personal配置之后,clone 地址可以写成:
git clone work:组织名/仓库名.gitHostName是真正的平台域名,IdentityFile是对应私钥路径。这样可以避免一台电脑上多个账户互相混淆。
4. 方案三:凭据管理器与多账户切换
有些场景下,你不想换 SSH,只想继续用 HTTPS,但又不愿意每次 push 都手动粘贴令牌。这时候凭据管理器就能发挥作用。
4.1 让 Git 记住令牌,减少重复输入
Git 本身不自带存储能力,它会把凭据委托给外部程序。常见的凭据管理器有三种:
git config --global credential.helper manager-coremanager-core是很多 Windows 自带 Git 环境默认的凭据管理器,它会把你输入的密码或令牌保存到系统凭据保险库中。后续访问同一个远程地址时,Git 自动读取并使用保存的凭据。
如果你的系统没有 manager-core,也可以使用 store 模式:
git config --global credential.helper store这个模式会在你~/.git-credentials文件中明文保存用户名和令牌。优点是配置简单,缺点是安全性很差。只要有人能读到你这个文件,就能直接获取你的令牌。不建议在公用电脑上使用这种模式。
还有一种更灵活的做法,是使用缓存模式:
git config --global credential.helper 'cache --timeout=3600'这种模式会在内存中缓存凭据,3600 秒后失效,需要重新输入。适合本机临时开发使用,不会落盘,相对更安全。
4.2 多账户场景下凭据为什么会串
使用凭据管理器后最大的坑就是多账户串号。假设你在一台电脑上同时使用两个账户,分别是工作账号和个人账号。第一次 clone 工作仓库时输入了工作令牌,凭据管理器记住了这个地址对应的登录信息。第二次 clone 个人仓库时,由于远程域名相同,凭据管理器可能会把工作令牌当作默认凭证提交过去,结果报出Invalid username or token或者权限不足。
解决思路有两个方向。一个是彻底抛弃 HTTPS 多账户,全部走 SSH 密钥 +~/.ssh/config,这个前面已经提到过。另一个方向是坚持用 HTTPS,并通过 Git 的 URL 重写规则区分不同用户。
比如你希望访问host平台时默认用userA,可以为特定路径设置别名:
git config --global url."https://userA@host/".insteadOf "https://host/"这样在 clone 或 push 时,https://host/org/repo.git会被自动改写成https://userA@host/org/repo.git,Git 就会用userA的身份去认证,从而避免和userB的凭据混淆。
这种方案看起来能用,但维护起来比较麻烦。每次新增账户、修改用户名、切换身份,都要动全局配置,稍不注意就把一堆仓库的访问目标都改乱了。对于日常开发,我还是优先建议用 SSH。
4.3 巧用 insteadOf 规则统一切换协议
除了多账户场景,insteadOf还能干一件很省心的事:把现有 HTTPS 地址统一转成 SSH 地址,而不需要逐个仓库去git remote set-url。
方法如下:
git config --global url."git@host:".insteadOf "https://host/"设置之后,所有指向https://host/的远程地址,在 Git 内部都会被替换为git@host:。这意味着你不需要改仓库的远程配置,push 时就直接走 SSH 接口了。
但这个规则要注意副作用。它是全局规则,会作用到所有符合前缀匹配的仓库。如果你公司内部有些仓库只开放 HTTPS 入口、并不支持 SSH,那么这个规则会把那些仓库也强制切到 SSH,导致无法访问。碰到这种情况,可以临时去掉这条全局规则,或者把它改成仅针对某些仓库路径生效。
git config --global --unset url."git@host:".insteadOf我个人只在个人电脑上使用这种规则,公司电脑上并不敢乱加,因为公司内部仓库环境太复杂了。
5. 常见问题排查与避坑实录
前面已经把三种方案讲完了,但你实际执行时,大概率还会遇到一些奇怪现象。这里我把排查过程按场景整理成速查表,你可以直接对照着看。
5.1 令牌明明是对的,还是报 Invalid username or token
这个问题出现频率最高。我遇到过的原因大致有下面几种:
第一种,用户名不对。很多平台的用户名并不是你的邮箱,而是一个单独的账号名。如果你填的是注册邮箱,服务端可能直接认为用户名无效。请在平台账户主页确认用户名,再重新尝试。
第二种,令牌在 URL 里被 shell 特殊字符干扰。令牌中如果出现了!、$、&、*等特殊字符,未加引号时会被 Shell 解析,导致实际发送的令牌已经被截断或替换。解决办法是使用单引号包裹整个 URL:
git clone 'https://user:token@host/org/repo.git'或者在交互提示时粘贴令牌,而不是把它写在命令里。
第三种,令牌权限范围不够。如果你只勾选了只读权限,却尝试 push,服务端一样会拒绝。这种情况下报错往往是403或Invalid username or token。去平台查看令牌的权限明细,确认是否包含仓库写权限。
第四种,令牌已经过期。尤其当你早先生成的是 7 天有效期令牌,一周时间一过,第二次使用时必然报错。重新生成一个即可。
第五种,代理或防火墙干预。某些企业网络的 web 代理会在认证环节直接返回错误,导致 Git 收到的响应并不是平台实际返回的认证结果。这时可以先取消代理测试:
git config --global --get http.proxy如果确认开了代理,临时关闭后重试。
5.2 “Support for password authentication was removed”报错
这个报错和Password authentication is not supported非常相似,通常出现在你的远程地址还是旧版 HTTPS 形式,并且命令里明显使用了密码认证。这类报错一般不是令牌配置问题,而是你还没开始使用令牌。
处理方式很简单:
- 确认远程地址。
- 如果地址仍然是
https://...,且你的工具链还停留在密码认证阶段,那就按第 2 部分生成令牌。 - 在命令行交互提示的密码位置输入令牌,避免在 URL 中明文传输。
- 如果已经改了地址还是报错,继续看下面的 5.3。
5.3 明明改了 remote,git 还是走 https
遇到这种问题,我会依次检查三处地方。
第一处,仓库本身的远程地址:
git config --get remote.origin.url如果输出的还是https://...,说明set-url没生效或者改错了仓库。重新执行一次:
git remote set-url origin git@host:org/repo.git第二处,全局配置文件里是否有insteadOf规则在捣乱:
git config --global -l | grep insteadOf如果有url."https://host/".insteadOf "git@host:"之类的反方向规则,它会把你刚刚改好的 SSH 地址重新换成 HTTPS。把对应规则删掉即可。
第三处,凭据管理器里残留的旧凭据。有些平台在同一会话中会持续使用旧的凭据,导致即使远程地址换了,推送时依然报错。你可以在系统的凭据管理里删除与该平台相关的凭据,然后在终端里重新认证。
下面是一张汇总表,方便你在真实排障时逐项核对:
| 现象 | 可能原因 | 快速验证方法 | 解决动作 |
|---|---|---|---|
| URL 里带令牌仍报错 | 特殊字符被 shell 转义 | 用交互方式重新输入 | 将 URL 整体用单引号包裹 |
| 令牌正确但权限不够 | 勾选权限不足 | 去平台查看令牌权限 | 重新生成或补权 |
| push 时被拒绝 | 令牌只读 / 已过期 | 查看令牌有效期 | 重新生成短期令牌 |
| SSH 测试失败 | 公钥未正确添加或私钥路径不对 | 查看~/.ssh文件 | 检查密钥并重新配置 |
| 多账户串号 | 凭据管理器记住了旧用户 | 查看系统凭据列表 | 清理凭据或用 SSH config |
| 远程地址显示错误 | insteadOf反向规则 | 执行 grep 检查配置 | 删除反向规则 |
6. 从实际操作中总结的几条经验
这套报错我前前后后处理过很多次,现在基本形成了自己的固定动作。新机器拿来,我会优先配 SSH 密钥,因为长期来看是最省心的。如果只是临时给某个自动化任务提供一个 token,我会选择生成一个短有效期、最小权限的令牌,并且在任务脚本里用环境变量引用,而不是把令牌明文写进命令。
最后一次和特别提醒:如果你身边的朋友也遇到Password authentication is not supported,先帮他确认远程地址写在配置里到底长什么样,然后再谈要不要生成令牌。很多人在这一步就已经跑偏了,一直在重复检查密码本身,却忽略了服务端已经关闭了密码认证这条通道。先看远程地址,再选认证方式,最后查权限,整个排查过程其实用不了两分钟。