一般认为,采用 “默认拒绝 “是一种良好的安全做法,即明确规定允许什么,不允许其他一切。定义未经认证的用户可以访问的内容是一种类似的情况,特别是对于Web应用程序。许多网站要求,除了几个URL(例如主页和登录页面)之外,用户必须经过认证才能使用。在这种情况下,为这些特定的URL定义访问配置属性是最容易的,而不是为每一个安全资源定义。换句话说,有时说 ROLE_SOMETHING 是默认需要的,只允许这个规则的某些例外,比如一个应用程序的登录、注销和主页。你也可以从过滤器链中完全省略这些页面,从而绕过访问控制检查,但由于其他原因这可能是不可取的,特别是如果这些页面对认证用户有不同的行为。 这就是我们所说的匿名认证。请注意,”匿名认证 “的用户和未认证的用户之间没有真正的概念上的区别。Spring Security的匿名认证只是给你提供了一种更方便的方式来配置你的访问控制属性。例如,对servlet API的调用,如getCallerPrincipal,仍然会返回null,即使SecurityContextHolder中实际上有一个匿名认证对象。 在其他情况下,匿名认证也很有用,比如当审计拦截器查询SecurityContextHolder以确定哪个委托人负责某个特定操作时。如果知道SecurityContextHolder总是包含一个Authentication对象,并且从不为空,那么就可以更稳健地编写类了。
Configuration
在使用HTTP配置Spring Security 3.0时,会自动提供匿名认证支持,并且可以使用
元素进行自定义(或禁用)。除非你使用传统的Bean配置,否则你不需要配置这里描述的Bean。 三个类共同提供了匿名认证功能。AnonymousAuthenticationToken是Authentication的一个实现,它存储适用于匿名委托人的GrantedAuthoritys。有一个相应的AnonymousAuthenticationProvider,它被链入ProviderManager,以便接受AnonymousAuthenticationTokens。最后,还有一个AnonymousAuthenticationFilter,它被放在正常认证机制之后,如果SecurityContextHolder中没有现有的认证,它就会自动添加一个AnonymousAuthenticationToken。过滤器和认证提供者的定义如下:
<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,grantAuthority[,grantAuthority]的形式表示。这与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(Authentication)方法,它允许感兴趣的类考虑到这种特殊类型的认证状态。ExceptionTranslationFilter在处理AccessDeniedExceptions时使用这个接口。如果一个AccessDeniedException被抛出,并且认证是匿名类型的,过滤器不会抛出一个403(禁止)响应,而是会启动AuthenticationEntryPoint,以便委托人能够正确地进行认证。这是一个必要的区别,否则委托人将总是被视为 “已认证”,而永远不会有机会通过form、basic、digest或其他正常认证机制登录。
你经常会看到上述拦截器配置中的ROLE_ANONYMOUS属性被替换为IS_AUTHENTICATED_ANONYMOUSLY,这在定义访问控制时实际上是一样的。这是一个使用AuthenticatedVoter的例子,我们将在授权章节中看到。它使用一个AuthenticationTrustResolver来处理这个特殊的配置属性,并授予匿名用户访问权。AuthenticatedVoter的方法更强大,因为它允许你区分匿名、记住我和完全认证的用户。如果你不需要这个功能,那么你可以坚持使用ROLLE_ANONYMOUS,它将被Spring Security的标准RoleVoter处理。
Getting Anonymous Authentications with Spring MVC
Spring MVC使用自己的参数解析器来解析Principal类型的参数。
@GetMapping("/")
public String method(Authentication authentication) {
if (authentication instanceof AnonymousAuthenticationToken) {
return "anonymous";
} else {
return "not anonymous";
}
}
将总是返回 “not anonymous”,即使是匿名请求。原因是Spring MVC使用HttpServletRequest#getPrincipal来解析参数,当请求为匿名时,该参数为空。
如果你想在匿名请求中获得认证,请使用@CurrentSecurityContext代替。
@GetMapping("/")
public String method(@CurrentSecurityContext SecurityContext context) {
return context.getAuthentication().getName();
}