对于最新的稳定版本,请使用 Spring Security 6.5.3spring-doc.cadn.net.cn

匿名身份验证

概述

通常,采用“默认拒绝”立场被认为是良好的安全实践,即明确指定允许的内容和不允许其他所有内容。定义未经身份验证的用户可以访问的内容也是一种类似的情况,特别是对于 Web 应用程序。许多站点要求用户必须对几个 URL(例如主页和登录页)以外的任何内容进行身份验证。在这种情况下,最简单的方法是为这些特定 URL 而不是为每个安全资源定义访问配置属性。换句话说,有时说是件好事ROLE_SOMETHING默认情况下是必需的,并且仅允许此规则的某些例外,例如应用程序的登录、注销和主页。您也可以从过滤器链中完全省略这些页面,从而绕过访问控制检查,但由于其他原因,这可能是不可取的,特别是当页面对经过身份验证的用户的行为不同时。spring-doc.cadn.net.cn

这就是我们所说的匿名身份验证的意思。请注意,“匿名身份验证”的用户和未经身份验证的用户之间没有真正的概念区别。Spring Security 的匿名身份验证只是为您提供了一种更方便的方式来配置访问控制属性。对 servlet API 调用的调用,例如getCallerPrincipal,仍然返回 null,即使SecurityContextHolder.spring-doc.cadn.net.cn

在其他情况下,匿名身份验证很有用,例如当审计拦截器查询SecurityContextHolder以确定哪个主体负责给定作。如果类知道SecurityContextHolder始终包含一个Authentication对象,并且从不包含null.spring-doc.cadn.net.cn

配置

当您使用 HTTP 配置时,会自动提供匿名身份验证支持(在 Spring Security 3.0 中引入)。您可以使用<anonymous>元素。 除非您使用传统的 Bean 配置,否则无需配置此处描述的 Bean。spring-doc.cadn.net.cn

三个类协同工作以提供匿名身份验证功能。AnonymousAuthenticationTokenAuthentication并存储GrantedAuthorityinstances,适用于匿名主体。有一个对应的AnonymousAuthenticationProvider,它被链接到ProviderManager因此AnonymousAuthenticationToken实例被接受。最后,一个AnonymousAuthenticationFilter在正常的身份验证机制之后链接,并自动添加AnonymousAuthenticationTokenSecurityContextHolder如果没有现有的Authentication保留在那里。过滤器和身份验证提供程序定义如下:spring-doc.cadn.net.cn

<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在过滤器和身份验证提供程序之间共享,以便前者创建的Tokens被后者接受spring-doc.cadn.net.cn

使用key财产不应被视为提供任何真正的担保。这只是一种簿记工作。如果您共享ProviderManager其中包含一个AnonymousAuthenticationProvider在身份验证客户端可以构造Authentication对象(例如使用 RMI 调用),则恶意客户端可能会提交AnonymousAuthenticationToken它已经创建了自己(使用所选的用户名和权限列表)。如果key是可以猜到的或可以找到的,则该Tokens将被匿名提供者接受。这不是正常使用的问题。但是,如果您使用 RMI,则应使用自定义的ProviderManager这会省略匿名提供程序,而不是共享用于 HTTP 身份验证机制的提供程序。spring-doc.cadn.net.cn

userAttributeusernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]. 在等号之后使用相同的语法userMap属性InMemoryDaoImpl.spring-doc.cadn.net.cn

如前所述,匿名身份验证的好处是所有 URI 模式都可以应用安全性,如以下示例所示:spring-doc.cadn.net.cn

<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接口,并具有相应的AuthenticationTrustResolverImpl实现。 此接口提供了一个isAnonymous(Authentication)方法,它允许感兴趣的类考虑这种特殊类型的身份验证状态。 这ExceptionTranslationFilter在处理中使用此接口AccessDeniedException实例。 如果AccessDeniedException被抛出并且身份验证是匿名类型,而不是抛出 403(禁止)响应,过滤器会启动AuthenticationEntryPoint以便主体可以正确进行身份验证。这是一个必要的区别。否则,主体将始终被视为“已验证”,并且永远不会有机会通过表单、基本、摘要或其他一些正常的身份验证机制登录。spring-doc.cadn.net.cn

我们经常看到ROLE_ANONYMOUS属性替换为IS_AUTHENTICATED_ANONYMOUSLY,这在定义访问控制时实际上是一样的。这是使用AuthenticatedVoter,我们将在授权一章中介绍。它使用AuthenticationTrustResolver以处理此特定配置属性并向匿名用户授予访问权限。 这AuthenticatedVoter方法更强大,因为它可以让你区分匿名用户、记住我用户和完全身份验证的用户。但是,如果您不需要此功能,您可以坚持使用ROLE_ANONYMOUS,由 Spring Security 的标准处理RoleVoter.spring-doc.cadn.net.cn

使用 Spring MVC 获取匿名身份验证

这意味着像这样的构造:spring-doc.cadn.net.cn

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

将始终返回“非匿名”,即使对于匿名请求也是如此。原因是 Spring MVC 使用HttpServletRequest#getPrincipal,即null当请求是匿名的时。spring-doc.cadn.net.cn

如果您想获得Authentication在匿名请求中,使用@CurrentSecurityContext相反:spring-doc.cadn.net.cn

将 CurrentSecurityContext 用于匿名请求
@GetMapping("/")
public String method(@CurrentSecurityContext SecurityContext context) {
	return context.getAuthentication().getName();
}
@GetMapping("/")
fun method(@CurrentSecurityContext context : SecurityContext) : String =
		context!!.authentication!!.name