16. 基本和摘要身份验证

基本身份验证和摘要身份验证是在 Web 应用程序中流行的替代身份验证机制。基本身份验证通常与 StatelessClient 端一起使用,StatelessClient 端会在每个请求中传递其凭据。将它与基于表单的身份验证结合使用非常普遍,在这种情况下,既可以通过基于浏览器的用户界面也可以通过 Web 服务来使用应用程序。但是,基本身份验证将密码以纯文本形式传输,因此,它只能在加密传输层(例如 HTTPS)上 true 使用。

16.1 BasicAuthenticationFilter

BasicAuthenticationFilter负责处理 HTTPHeaders 中显示的基本身份验证凭据。这可用于验证 Spring 远程协议(例如 Hessian 和 Burlap)以及普通浏览器用户代理(例如 Firefox 和 Internet Explorer)发出的调用。 RFC 1945 第 11 节定义了 ManagementHTTP 基本认证的标准,并且BasicAuthenticationFilter与此 RFC 一致。基本身份验证是一种有吸引力的身份验证方法,因为它已广泛部署在用户代理中,并且实现极其简单(它只是在 HTTPHeaders 中指定的 username:password 的 Base64 编码)。

16.1.1 Configuration

要实现 HTTP 基本身份验证,您需要在过滤器链中添加BasicAuthenticationFilter。应用程序上下文应包含BasicAuthenticationFilter及其必需的协作者:

<bean id="basicAuthenticationFilter"
class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
</bean>

<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
<property name="realmName" value="Name Of Your Realm"/>
</bean>

配置的AuthenticationManager处理每个身份验证请求。如果身份验证失败,将使用配置的AuthenticationEntryPoint重试身份验证过程。通常,您将过滤器与BasicAuthenticationEntryPoint结合使用,BasicAuthenticationEntryPoint返回带有适当 Headers 的 401 响应以重试 HTTP Basic 身份验证。如果身份验证成功,则照常将生成的Authentication对象放入SecurityContextHolder中。

如果身份验证事件成功,或者因为 HTTPHeaders 不包含受支持的身份验证请求而未尝试进行身份验证,则过滤器链将照常 continue。唯一的中断过滤器链的方法是验证失败并调用AuthenticationEntryPoint

16.2 DigestAuthenticationFilter

DigestAuthenticationFilter能够处理 HTTPHeaders 中提供的摘要身份验证凭据。摘要式身份验证试图解决基本身份验证的许多弱点,特别是通过确保凭据不会以明文形式通过网络发送来解决。许多用户代理都支持摘要式身份验证,包括 Mozilla Firefox 和 Internet Explorer。 HTTP 摘要认证的标准由 RFC 2617 定义,该标准更新了 RFC 2069 规定的摘要认证的早期版本。大多数用户代理实现 RFC2617.SpringSecurity 的DigestAuthenticationFilter与“ auth”保护质量(qop)兼容。由 RFC 2617 所规定,它还提供与 RFC 2069 的向后兼容性。如果您需要使用未加密的 HTTP(即没有 TLS/HTTPS)并且希望最大程度地提高身份验证过程的安全性,则摘要身份验证是一种更具吸引力的选择。实际上,如 RFC 2518 第 17.1 节所述,摘要式身份验证是 WebDAV 协议的强制性要求。

Note

您不应该在现代应用程序中使用 Digest,因为它不安全。最明显的问题是您必须以纯文本,加密或 MD5 格式存储密码。所有这些存储格式都被认为是不安全的。相反,您应该使用一种单向自适应密码哈希(即 bCrypt,PBKDF2,SCrypt 等)。

摘要式身份验证的核心是“一次性”。这是服务器生成的值。 Spring Security 的随机数采用以下格式:

base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
expirationTime:   The date and time when the nonce expires, expressed in milliseconds
key:              A private key to prevent modification of the nonce token

DigestAuthenticationEntryPoint具有指定用于生成现时令牌的key的属性,以及用于确定到期时间的nonceValiditySeconds属性(默认为 300,等于五分钟)。在随机数有效的情况下,摘要是通过串联各种字符串来计算的,这些字符串包括用户名,密码,随机数,所请求的 URI,Client 端生成的随机数(仅由用户代理生成每个请求的随机值),领域名称等,然后执行 MD5 哈希。服务器和用户代理都执行此摘要计算,如果它们不同意包含的值(例如密码),则会产生不同的哈希码。在 Spring Security 实现中,如果服务器生成的随机数刚刚到期(但摘要有效),则DigestAuthenticationEntryPoint将发送"stale=true"Headers。这告诉用户代理无需打扰用户(因为密码和用户名等是正确的),而只需使用新的随机数重试即可。

nonceValiditySeconds参数DigestAuthenticationEntryPoint的适当值取决于您的应用程序。极其安全的应用程序应注意,可以使用拦截的身份验证 Headers 来模拟主体,直到达到随机数中所包含的expirationTime为止。这是选择适当设置时的关键原则,但是对于非常安全的应用程序,在最初的情况下不会在 TLS/HTTPS 上运行是不常见的。

由于摘要式身份验证的实施更为复杂,因此经常存在用户代理问题。例如,Internet Explorer 无法在同一会话中的后续请求上提供“不透明”令牌。因此,Spring Security 过滤器将所有状态信息封装到“ nonce”令牌中。在我们的测试中,Spring Security 的实现可与 Mozilla Firefox 和 Internet Explorer 可靠地配合,正确处理随机数超时等。

16.2.1 Configuration

现在,我们已经回顾了该理论,让我们看看如何使用它。要实现 HTTP 摘要验证,必须在过滤器链中定义DigestAuthenticationFilter。应用程序上下文将需要定义DigestAuthenticationFilter及其所需的协作者:

<bean id="digestFilter" class=
	"org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
<property name="userDetailsService" ref="jdbcDaoImpl"/>
<property name="authenticationEntryPoint" ref="digestEntryPoint"/>
<property name="userCache" ref="userCache"/>
</bean>

<bean id="digestEntryPoint" class=
	"org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
<property name="realmName" value="Contacts Realm via Digest Authentication"/>
<property name="key" value="acegi"/>
<property name="nonceValiditySeconds" value="10"/>
</bean>

需要配置的UserDetailsService,因为DigestAuthenticationFilter必须可以直接访问用户的明文密码。如果您在 DAO [15]中使用编码密码,则摘要身份验证将不起作用。 DAO 协作者与UserCache通常直接与DaoAuthenticationProvider共享。 authenticationEntryPoint属性必须为DigestAuthenticationEntryPoint,以便DigestAuthenticationFilter可以获取正确的realmNamekey进行摘要计算。

BasicAuthenticationFilter类似,如果身份验证成功,则Authentication请求令牌将被放入SecurityContextHolder。如果身份验证事件成功,或者由于 HTTPHeaders 不包含摘要身份验证请求而未尝试身份验证,则过滤器链将照常 continue。如上一段所述,只有当身份验证失败并调用AuthenticationEntryPoint时,过滤器链才会被中断。

摘要式身份验证的 RFC 提供了一系列附加功能,以进一步提高安全性。例如,可以在每个请求上更改随机数。尽管如此,Spring Security 实现仍被设计为最小化实现的复杂性(以及无疑会出现的用户代理不兼容性),并避免需要存储服务器端状态。如果您希望更详细地探索这些功能,则可以邀请您阅读 RFC 2617.据我们所知,Spring Security 的实现确实符合该 RFC 的最低标准。


[15]如果DigestAuthenticationFilter.passwordAlreadyEncoded设置为true,则可以将密码编码为 HEX(MD5(username:realm:password))格式。但是,其他密码编码将不适用于摘要身份验证。