通常认为采用“默认拒绝”是一种良好的安全实践,在这种情况下,您可以明确指定允许的内容,并禁止其他内容。定义未经身份验证的用户可以访问的内容也是类似的情况,特别是对于 web 应用程序。许多站点要求用户必须对除了一些 url (例如主页和登录页面)以外的任何内容进行身份验证。在这种情况下,为这些特定 url 定义访问配置属性比为每个安全资源定义访问配置属性更容易。换句话说,有时候说 role_something 是默认需要的,并且只允许该规则的某些异常,比如用于应用程序的登录、注销和主页。您还可以从过滤器链中完全省略这些页面,从而绕过访问控制检查,但这可能由于其他原因而不可取,特别是如果页面对于经过身份验证的用户的行为不同。
这就是我们所说的匿名身份验证。注意,在“匿名身份验证”用户和未经身份验证的用户之间没有真正的概念差异。Spring Security 的匿名身份验证为您配置访问控制属性提供了更方便的方法。例如,对于 getCallerPrincipal 等 servlet API 的调用仍然返回 null,即使 SecurityContextHolder 中实际上存在匿名身份验证对象。
在其他情况下,匿名身份验证也很有用,例如审计拦截器查询 SecurityContextHolder 以确定哪个主体负责给定的操作。如果类知道 SecurityContextHolder 总是包含一个 Authentication 对象,而且从不使用 null,则可以更有效地创建类。
Configuration
当使用 HTTP 配置 Spring Security 3.0时,会自动提供匿名身份验证支持,并且可以使用 < Anonymous > 元素进行定制(或禁用)。您不需要配置这里描述的 bean,除非您使用传统的 bean 配置。
共同提供匿名身份验证特性的三个类。Token 是身份验证的一个实现,它存储应用于匿名主体的 GrantedAuthoritys。有一个相应的 AnonymousAuthenticationProvider,它被链接到 ProviderManager 中,以便 AnonymousAuthenticationToken 被接受。最后,还有一个 AnonymousAuthenticationFilter,它被链接在正常的身份验证机制之后,如果没有现有的身份验证,它会自动向 SecurityContextHolder 添加 AnonymousAuthenticationTokens 。过滤器和身份验证提供者的定义如下:
<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>
密钥在过滤器和身份验证提供者之间共享,以便前者创建的标记被后者接受[1]。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>
AuthenticationTrustResolver
有关匿名身份验证的讨论的最后一个例子是AuthenticationTrustResolver接口,以及它对应AuthenticationTrustResolverImpl实现。该接口提供了isAnonymous(身份验证)方法,它允许感兴趣的类考虑这种特殊类型的身份验证状态。ExceptionTranslationFilter在处理AccessDeniedExceptions时使用此接口。如果抛出AccessDeniedException异常,并且身份验证是匿名类型的,则筛选器不会抛出403(禁止)响应,而是开始AuthationEntryPoint,以便主体可以正确地进行身份验证。
这是一个必要的区别,否则主体将始终被认为是“经过身份验证的”,并且永远不会有机会通过表单、基本、摘要或一些其他正常的身份验证机制进行登录。
您经常会看到上述拦截器配置中的ROLE_ANONYMONY属性被替换为IS_AUTHENTIAD_ANAMENTIAL,这在定义访问控制时实际上是相同的。这是一个使用身份验证Voter的示例,我们将在授权一章中看到。它使用身份验证TrustResolver来处理此特定配置属性,并向匿名用户授予访问权限。AuthatedVoter方法更强大,因为它允许您区分匿名、记住我和完全经过身份验证的用户。但是,如果您不需要这个功能,那么您可以坚持使用ROLE_ANONYMON,它将由Spring Security的标准RoleVoter处理。
使用 Spring MVC 获取匿名认证
Springmvc 使用自己的参数解析器解析 Principal 类型的参数。
@GetMapping("/")
public String method(Authentication authentication) {
if (authentication instanceof AnonymousAuthenticationToken) {
return "anonymous";
} else {
return "not anonymous";
}
}
将始终返回“not anonymous”,即使对于匿名请求也是如此。其原因是,Spring MVC使用HttpServletRequestgetMaster来解析参数,当请求是匿名的时,该参数为空。
如果你想在匿名请求中获得身份验证,可以使用@CurrentSecurityContext:
@GetMapping("/")
public String method(@CurrentSecurityContext SecurityContext context) {
return context.getAuthentication().getName();
}
[1] 使用密钥财产不应被视为在这里提供任何真正的安全。这只是一项记账工作。如果在身份验证客户端可以构造身份验证对象(例如使用RMI调用)的场景中共享包含匿名身份验证提供者的ProviderManager,则恶意客户端可能会提交自己创建的匿名身份验证令牌(带有选定的用户名和授权列表)。如果密钥是可猜测的或可以找到的,则该令牌将被匿名提供者接受。这对于正常使用来说不是问题,但如果您使用的是RMI,那么最好使用定制的ProviderManager,它省略了匿名提供者,而不是共享您用于HTTP身份验证机制的提供者。