# 匿名身份验证

## 概述

通常认为，采用“默认拒绝”（deny-by-default）是一种良好的安全实践，在这种情况下，你可以明确指定允许的内容，而不允许其他任何内容。定义未经身份验证的用户可以访问的内容也是类似的情况，特别是对于 Web 应用程序。许多网站要求用户必须对除几个 URL（例如主页和登录页面）以外的任何内容进行身份验证。在这种情况下，最容易为这些特定的 URL 定义访问配置属性，而不是为每个安全资源定义访问配置属性。换句话说，有时说`ROLE_SOMETHING`是缺省情况下需要的，并且只允许此规则的某些例外情况，例如应用程序的登录、注销和主页，这是很好的。你还可以从筛选链中完全省略这些页面，从而绕过访问控制检查，但是由于其他原因，这可能是不希望的，特别是如果对于经过身份验证的用户，页面的行为不同。

这就是我们所说的匿名身份验证。请注意，“匿名身份验证”的用户和未经身份验证的用户之间没有真正的概念上的区别。 Spring 安全性的匿名身份验证为你提供了一种更方便的方式来配置访问控制属性。例如，对 Servlet API 的调用（例如`getCallerPrincipal`）仍将返回 null，即使在`SecurityContextHolder`中实际上存在匿名身份验证对象。

在其他情况下，匿名身份验证是有用的，例如，当审计拦截器查询`SecurityContextHolder`以确定哪个主体负责给定的操作时。如果类知道`SecurityContextHolder`总是包含一个`Authentication`对象，而不是`null`，则可以更可靠地编写类。

## 配置

匿名身份验证支持在使用 HTTP 配置 Spring Security3.0 时自动提供，并且可以使用`<anonymous>`元素进行定制（或禁用）。你不需要配置这里描述的 bean，除非你使用的是传统的 Bean 配置。

三个类一起提供匿名身份验证功能。`AnonymousAuthenticationToken`是`Authentication`的一个实现，并存储适用于匿名主体的`GrantedAuthority`s。有一个对应的`AnonymousAuthenticationProvider`，它被链接到`ProviderManager`中，使得`AnonymousAuthenticationToken`s 被接受。最后，还有一个`AnonymousAuthenticationFilter`，它在正常的身份验证机制之后被链接，并且如果不存在`Authentication`，则自动将一个`AnonymousAuthenticationToken`添加到`SecurityContextHolder`中。过滤器和身份验证提供程序的定义如下：

```
<bean id="anonymousAuthFilter"
	class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
<property name="key" value="foobar"/>
<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>

<bean id="anonymousAuthenticationProvider"
	class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
<property name="key" value="foobar"/>
</bean>
```

`key`在过滤器和身份验证提供程序之间共享，因此前者创建的令牌被后者接受<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>。`userAttribute`以`usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]`的形式表示。这与`InMemoryDaoImpl`的`userMap`属性的等号后面使用的语法相同。

正如前面所解释的，匿名身份验证的好处是，所有 URI 模式都可以对它们应用安全性。例如：

```
<bean id="filterSecurityInterceptor"
	class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="securityMetadata">
	<security:filter-security-metadata-source>
	<security:intercept-url pattern='/index.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
	<security:intercept-url pattern='/hello.htm' access='ROLE_ANONYMOUS,ROLE_USER'/>
	<security:intercept-url pattern='/logoff.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
	<security:intercept-url pattern='/login.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
	<security:intercept-url pattern='/**' access='ROLE_USER'/>
	</security:filter-security-metadata-source>" +
</property>
</bean>
```

## 身份验证 TrustResolver

匿名身份验证讨论的一个补充是`AuthenticationTrustResolver`接口，其对应的`AuthenticationTrustResolverImpl`实现。这个接口提供了`isAnonymous(Authentication)`方法，它允许感兴趣的类考虑到这种特殊类型的身份验证状态。`ExceptionTranslationFilter`在处理`AccessDeniedException`s 时使用该接口。如果抛出了`AccessDeniedException`，并且身份验证是匿名类型的，那么过滤器将启动`AuthenticationEntryPoint`，以使主体能够正确地进行身份验证，而不是抛出 403（禁用）响应。这是一个必要的区别，否则主体将始终被视为“身份验证”，并且永远不会有机会通过 Form、Basic、Digest 或其他一些正常的身份验证机制登录。

在上面的拦截器配置中，你经常会看到`ROLE_ANONYMOUS`属性被替换为`IS_AUTHENTICATED_ANONYMOUSLY`，这在定义访问控制时实际上是相同的。这是`AuthenticatedVoter`的使用示例，我们将在[授权章](../authorization/architecture.html#authz-authenticated-voter)中看到它。它使用`AuthenticationTrustResolver`来处理这个特定的配置属性，并授予匿名用户访问权限。`AuthenticatedVoter`方法更强大，因为它允许你区分匿名、rememe-me 和完全验证的用户。但是，如果你不需要此功能，那么你可以坚持使用`ROLE_ANONYMOUS`，它将由 Spring Security 的标准`RoleVoter`处理。

## 利用 Spring MVC 获得匿名身份验证

[ Spring MVC 解析类型`Principal`]的参数（https://DOCS. Spring.io/ Spring-framework/DOCS/current/reference/html/web.html#mvc-ann-arguments）使用自己的参数解析器。

这意味着像这样的构造：

爪哇

```
@GetMapping("/")
public String method(Authentication authentication) {
	if (authentication instanceof AnonymousAuthenticationToken) {
		return "anonymous";
	} else {
		return "not anonymous";
	}
}
```

Kotlin

```
@GetMapping("/")
fun method(authentication: Authentication?): String {
    return if (authentication is AnonymousAuthenticationToken) {
        "anonymous"
    } else {
        "not anonymous"
    }
}
```

将始终返回“不匿名”，即使是匿名请求。原因是 Spring MVC 使用`HttpServletRequest#getPrincipal`解析参数，当请求是匿名的时，这是`null`。

如果你想在匿名请求中获得`Authentication`，请使用`@CurrentSecurityContext`:

例 1。对匿名请求使用 CurrentSecurityContext

爪哇

```
@GetMapping("/")
public String method(@CurrentSecurityContext SecurityContext context) {
	return context.getAuthentication().getName();
}
```

Kotlin

```
@GetMapping("/")
fun method(@CurrentSecurityContext context : SecurityContext) : String =
		context!!.authentication!!.name
```

---

[1](#_footnoteref_1)。在这里，使用`key`属性不应被视为提供了任何真正的安全性。这只是一次记账练习。如果你正在共享一个`ProviderManager`，其中包含一个`AnonymousAuthenticationProvider`，在这样的场景中，验证客户端可以构造`Authentication`对象（例如通过 RMI 调用），然后，恶意客户端可以提交它自己创建的`AnonymousAuthenticationToken`（使用所选的用户名和权限列表）。如果`key`是可以猜测的，或者可以找到，那么该令牌将被匿名提供者接受。这不是正常使用的问题，但是如果你正在使用 RMI，你最好使用定制的`ProviderManager`，它省略了匿名提供者，而不是共享你用于 HTTP 身份验证机制的提供者。

[OpenID](openid.html)[预先认证](preauth.html)

