如果你使用的是 SSH 方式连接远端,并且设置了一个没有口令的密钥,这样就可以在不输入用户名和密码的情况下安全地传输数据。 然而,这对 HTTP 协议来说是不可能的 —— 每一个连接都是需要用户名和密码的。 这在使用双重认证的情况下会更麻烦,因为你需要输入一个随机生成并且毫无规律的 token 作为密码。
幸运的是,Git 拥有一个凭证系统来处理这个事情。 下面有一些 Git 的选项:
你可以设置 Git 的配置来选择上述的一种方式
$ git config --global credential.helper cache
部分辅助工具有一些选项。 “store” 模式可以接受一个 --file <path> 参数,可以自定义存放密码的文件路径(默认是 ~/.git-credentials )。 “cache” 模式有 --timeout <seconds> 参数,可以设置后台进程的存活时间(默认是 “900”,也就是 15 分钟)。 下面是一个配置 “store” 模式自定义路径的例子:
--file <path>
~/.git-credentials
--timeout <seconds>
$ git config --global credential.helper 'store --file ~/.my-credentials'
Git 甚至允许你配置多个辅助工具。 当查找特定服务器的凭证时,Git 会按顺序查询,并且在找到第一个回答时停止查询。 当保存凭证时,Git 会将用户名和密码发送给 所有 配置列表中的辅助工具,它们会按自己的方式处理用户名和密码。 如果你在闪存上有一个凭证文件,但又希望在该闪存被拔出的情况下使用内存缓存来保存用户名密码,.gitconfig 配置文件如下:
.gitconfig
[credential] helper = store --file /mnt/thumbdrive/.git-credentials helper = cache --timeout 30000
这些是如何实现的呢? Git 凭证辅助工具系统的命令是 git credential,这个命令接收一个参数,并通过标准输入获取更多的参数。
git credential
举一个例子更容易理解。 我们假设已经配置好一个凭证辅助工具,这个辅助工具保存了 mygithost 的凭证信息。 下面是一个使用 “fill” 命令的会话,当 Git 尝试寻找一个服务器的凭证时就会被调用。
mygithost
$ git credential fill <1> protocol=https <2> host=mygithost <3> protocol=https <4> host=mygithost username=bob password=s3cre7 $ git credential fill <5> protocol=https host=unknownhost Username for 'https://unknownhost': bob Password for 'https://bob@unknownhost': protocol=https host=unknownhost username=bob password=s3cre7
凭证系统实际调用的程序和 Git 本身是分开的;具体是哪一个以及如何调用与 credential.helper 配置的值有关。 这个配置有多种格式:
credential.helper
[options="header"] |====== | 配置值 | 行为 | foo | 执行 git-credential-foo | foo -a --opt=bcd | 执行 git-credential-foo -a --opt=bcd | /absolute/path/foo -xyz | 执行 /absolute/path/foo -xyz | !f() { echo "password=s3cre7"; }; f | ! 后面的代码会在 shell 执行 |======
foo
git-credential-foo
foo -a --opt=bcd
git-credential-foo -a --opt=bcd
/absolute/path/foo -xyz
!f() { echo "password=s3cre7"; }; f
!
上面描述的辅助工具可以被称做 git-credential-cache、git-credential-store 之类,我们可以配置它们来接受命令行参数。 通常的格式是 “git-credential-foo [args] ” 标准输入/输出协议和 git-credential 一样,但它们使用的是一套稍微不太一样的行为:
git-credential-cache
git-credential-store
get
store
erase
对于 store 和 erase 两个行为是不需要返回数据的(Git 也会忽略掉)。 然而对于 get,Git 对辅助工具的返回信息十分感兴趣。 如果辅助工具并不知道任何有用的信息,它就会直接退出而没有任何输出,但如果知道的话, 它就会在已存储信息的基础上扩充所提供的信息。 它的输出可看做一系列赋值语句,提供的任何内容都会取代 Git 已知的内容。
如果辅助工具没有任何有用的信息,它可以直接退出而不需要输出任何东西,但如果它有这些信息,它在提供的信息后面增加它所拥有的信息。 这些输出会被视为一系列的赋值语句;每一个提供的数据都会将 Git 已有的数据替换掉。
这有一个和上面一样的例子,但是跳过了 git-credential 这一步,直接到 git-credential-store:
$ git credential-store --file ~/git.store store <1> protocol=https host=mygithost username=bob password=s3cre7 $ git credential-store --file ~/git.store get <2> protocol=https host=mygithost username=bob <3> password=s3cre7
~/git.store 文件的内容类似:
~/git.store
https://bob:s3cre7@mygithost
仅仅是一系列包含凭证信息 URL 组成的行。 osxkeychain 和 wincred 辅助工具使用它们后端存储的原生格式,而 cache 使用它的内存格式(其他进程无法读取)。
osxkeychain
wincred
cache
已经知道 git-credential-store 之类的是和 Git 是相互独立的程序,就不难理解 Git 凭证辅助工具可以是 任意 程序。 虽然 Git 提供的辅助工具覆盖了大多数常见的使用场景,但并不能满足所有情况。 比如,假设你的整个团队共享一些凭证,也许是在部署时使用。 这些凭证是保存在一个共享目录里,由于这些凭证经常变更,所以你不想把它们复制到你自己的凭证仓库中。 现有的辅助工具无法满足这种情况;来看看我们如何自己实现一个。 这个程序应该拥有几个核心功能:
. 我们唯一需要关注的行为是 get;store 和 erase 是写操作,所以当接受到这两个请求时我们直接退出即可。 . 共享的凭证文件格式和 git-credential-store 使用的格式相同。 . 凭证文件的路径一般是固定的,但我们应该允许用户传入一个自定义路径以防万一。
我们再一次使用 Ruby 来编写这个扩展,但只要 Git 能够执行最终的程序,任何语言都是可以的。 这是我们的凭证辅助工具的完整代码:
include::../git-credential-read-only[]
我们把这个辅助工具保存为 git-credential-read-only,放到我们的 PATH 路径下并且给予执行权限。 一个交互式会话类似:
git-credential-read-only
PATH
$ git credential-read-only --file=/mnt/shared/creds get protocol=https host=mygithost protocol=https host=mygithost username=bob password=s3cre7
由于这个的名字是 “git-” 开头,所以我们可以在配置值中使用简便的语法:
$ git config --global credential.helper 'read-only --file /mnt/shared/creds'
正如你看到的,扩展这个系统是相当简单的,并且可以为你和你的团队解决一些常见问题。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8