本手册描述的是 Subversion 1.4 版本。如果你运行的是 Subversion 的更新版本,我们强烈建议你访问 https://svnbook.subversion.org.cn/,并查阅适合你 Subversion 版本的书籍。

网络模型

在某些情况下,你需要了解 Subversion 客户端如何与其服务器通信。Subversion 的网络层是抽象的,这意味着无论 Subversion 客户端与哪种类型的服务器交互,其基本行为都是一样的。无论是通过 HTTP 协议 (http://) 与 Apache HTTP 服务器通信,还是通过自定义 Subversion 协议 (svn://) 与 svnserve 通信,其基本网络模型都是一样的。在本节中,我们将解释这种网络模型的基础知识,包括 Subversion 如何管理身份验证和授权问题。

请求和响应

Subversion 客户端大部分时间都在管理工作副本。但是,当它需要从远程仓库获取信息时,它会发出网络请求,服务器会用相应的答案进行响应。网络协议的细节对用户来说是隐藏的——客户端尝试访问一个 URL,然后根据 URL 方案使用特定的协议来联系服务器(参见 仓库 URL)。

提示

运行 svn --version 查看客户端支持的 URL 方案和协议。

当服务器进程收到客户端请求时,它通常要求客户端进行身份验证。它向客户端发出身份验证挑战,客户端通过向服务器提供 凭证 来进行响应。身份验证完成后,服务器会用客户端最初请求的信息进行响应。请注意,这种系统与 CVS 等系统不同,在 CVS 中,客户端在发出任何请求之前都会预先向服务器提供凭证(“登录”)。在 Subversion 中,服务器会在适当的时候向客户端发出“拉取”凭证的请求,而不是客户端“推送”凭证。这使得某些操作更加优雅。例如,如果服务器被配置为允许世界上任何人读取仓库,那么当客户端尝试 svn checkout 时,服务器将不会发出身份验证挑战。

如果客户端发出的特定网络请求导致仓库中创建了新的版本(例如 svn commit),那么 Subversion 会使用与这些请求相关的已验证用户名作为版本作者。也就是说,已验证用户的名称被存储为新版本 svn:author 属性的值(参见 名为“Subversion 属性”的部分)。如果客户端没有进行身份验证(换句话说,服务器从未发出身份验证挑战),那么版本的 svn:author 属性为空。

客户端凭证缓存

许多服务器被配置为要求在每次请求时都进行身份验证。如果用户被迫一遍又一遍地输入密码,这将是一件非常烦人的事情。幸运的是,Subversion 客户端对此有一个解决方案——一个内置的系统,用于在磁盘上缓存身份验证凭证。默认情况下,每当命令行客户端成功响应服务器的身份验证挑战时,它都会将凭证保存在用户的私有运行时配置区域 (~/.subversion/auth/ 在类 Unix 系统上或 %APPDATA%/Subversion/auth/ 在 Windows 上;参见 名为“运行时配置区域”的部分,以获取有关运行时配置系统的更多详细信息)。成功的凭证会被缓存在磁盘上,并根据服务器的主机名、端口和身份验证域的组合进行键值匹配。

当客户端收到身份验证挑战时,它会首先在用户的磁盘缓存中查找相应的凭证。如果似乎合适的凭证不存在,或者如果缓存的凭证最终无法进行身份验证,那么客户端默认会回退到提示用户输入所需信息。

有安全意识的读者会立即怀疑这里存在着令人担忧的理由。“在磁盘上缓存密码?这太糟糕了!你永远不应该这样做!

Subversion 开发人员认识到这种担忧的合理性,因此 Subversion 与操作系统和环境提供的可用机制协同工作,以尽量减少泄露此信息的风险。以下是针对最常见平台的用户,对这意味着什么进行了详细说明

  • 在 Windows 2000 及更高版本上,Subversion 客户端使用标准 Windows 加密服务来加密磁盘上的密码。由于加密密钥由 Windows 管理,并与用户的登录凭证绑定,因此只有用户才能解密缓存的密码。(请注意,如果用户的 Windows 帐户密码被管理员重置,则所有缓存的密码都将无法解密。Subversion 客户端的行为将如同这些密码不存在一样,并在需要时提示输入密码。)

  • 类似地,在 Mac OS X 上,Subversion 客户端将所有仓库密码存储在登录密钥环中(由钥匙串服务管理),该密钥环由用户的帐户密码保护。用户偏好设置可以实施额外的策略,例如要求用户在每次使用 Subversion 密码时都输入其帐户密码。

  • 对于其他类 Unix 操作系统,不存在标准的“密钥环”服务。但是,auth/ 缓存区域仍然受到权限保护,因此只有用户(所有者)才能从中读取数据,而不能由全世界读取。操作系统自身的权限保护了密码。

当然,对于真正的偏执狂来说,所有这些机制都无法达到完美的标准。因此,对于那些愿意牺牲便利性以获得终极安全性的用户来说,Subversion 提供了多种方法来完全禁用其凭证缓存系统。

要禁用单个命令的缓存,请传递 --no-auth-cache 选项

$ svn commit -F log_msg.txt --no-auth-cache
Authentication realm: <svn://host.example.com:3690> example realm
Username:  joe
Password for 'joe':

Adding         newfile
Transmitting file data .
Committed revision 2324.

# password was not cached, so a second commit still prompts us

$ svn delete newfile
$ svn commit -F new_msg.txt
Authentication realm: <svn://host.example.com:3690> example realm
Username:  joe
…

或者,如果你想永久禁用凭证缓存,你可以编辑运行时配置区域中的 config 文件,并将 store-auth-creds 选项设置为 no。这将阻止存储在受影响的计算机上执行的任何 Subversion 交互中使用的凭证。通过修改系统范围的运行时配置区域(如 名为“配置区域布局”的部分 中所述),可以将其扩展到覆盖计算机上的所有用户。

[auth]
store-auth-creds = no

有时,用户希望从磁盘缓存中删除特定凭证。为此,你需要导航到 auth/ 区域并手动删除相应的缓存文件。凭证被缓存在单独的文件中;如果你查看每个文件内部,你将看到键和值。svn:realmstring 键描述了与该文件关联的特定服务器域

$ ls ~/.subversion/auth/svn.simple/
5671adf2865e267db74f09ba6f872c28
3893ed123b39500bca8a0b382839198e
5c3c22968347b390f349ff340196ed39

$ cat ~/.subversion/auth/svn.simple/5671adf2865e267db74f09ba6f872c28

K 8
username
V 3
joe
K 8
password
V 4
blah
K 15
svn:realmstring
V 45
<https://svn.domain.com:443> Joe's repository
END

找到相应的缓存文件后,只需将其删除。

关于 svn 的身份验证行为,最后说一句,尤其是关于 --username--password 选项。许多客户端子命令接受这些选项,但重要的是要理解使用这些选项不会自动将凭证发送到服务器。如前所述,服务器会在必要时从客户端“拉取”凭证;客户端不能随意“推送”它们。如果用户名和/或密码作为选项传递,只有在服务器请求时才会将其呈现给服务器。[20] 这些选项通常用于以与 Subversion 默认选择不同的用户(例如你的系统登录名)进行身份验证,或者在尝试避免交互式提示时(例如从脚本调用 svn 时)使用。

以下是一份最终总结,描述了 Subversion 客户端在收到身份验证挑战时的行为。

  1. 首先,客户端检查用户是否在命令行选项 (--username 和/或 --password) 中指定了任何凭证。如果没有,或者如果这些选项无法成功进行身份验证,那么

  2. 客户端会在运行时 auth/ 区域中查找服务器的主机名、端口和域,查看用户是否已经缓存了相应的凭证。如果没有,或者如果缓存的凭证无法进行身份验证,那么

  3. 最后,客户端会回退到提示用户(除非通过 --non-interactive 选项或其客户端特定的等效项指示不要这样做)。

如果客户端通过上述任何方法成功进行身份验证,它将尝试在磁盘上缓存凭证(除非用户禁用了此行为,如前所述)。



[20] 再次强调,一个常见的错误是错误地配置服务器,使其永远不会发出身份验证挑战。当用户向客户端传递 --username--password 选项时,他们会惊讶地发现这些选项从未被使用,即新的版本似乎仍然是匿名提交的!