匿名认证 Authentication
本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
概述
一般来说,采取 “deny-by-default” (默认拒绝)的立场被认为是良好的安全实践,即明确规定允许什么,不允许其他一切。定义未经认证的用户可以访问的内容是一个类似的情况,特别是对于Web应用程序。许多网站要求,除了几个URL(例如主页和登录页面)之外,用户必须经过认证。在这种情况下,为这些特定的URL定义访问配置属性是最容易的,而不是为每一个安全的资源定义。换句话说,有时说 ROLE_SOMETHING
是默认需要的,只允许这个规则的某些例外,例如一个应用程序的登录、注销和主页。你也可以从过滤器链中完全省略这些页面,从而绕过访问控制检查,但由于其他原因这可能是不可取的,特别是如果这些页面对认证用户有不同的行为。
这就是我们所说的匿名认证。请注意,"匿名认证" 的用户和未认证的用户之间没有真正的概念上的区别。Spring Security 的匿名认证只是给你提供了一种更方便的方式来配置你的访问控制属性。对servlet API的调用,如 getCallerPrincipal
,仍然返回 null
,即使在 SecurityContextHolder
中实际上有一个匿名认证对象。
在其他情况下,匿名认证也很有用,比如当审计拦截器查询 SecurityContextHolder
以确定哪个委托人(principal)负责某个特定操作时。如果知道 SecurityContextHolder
总是包含一个 Authentication
对象,并且从不包含 null
,那么就可以更稳健地编写类了。
配置
当你使用HTTP配置(在Spring Security 3.0中引入)时,会自动提供匿名认证支持。你可以通过使用 <anonymous>
元素来定制(或禁用)它。除非你使用传统的Bean配置,否则你不需要配置这里描述的Bean。
三个类一起工作以提供匿名认证功能。AnonymousAuthenticationToken
是 Authentication
的一个实现,它存储适用于匿名委托人(principal)的 GrantedAuthority
实例。有一个相应的 AnonymousAuthenticationProvider
,它被链入 ProviderManager
,以便 AnonymousAuthenticationToken
实例被接受。最后,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>
该 key
在过滤器和认证提供者之间共享,因此前者创建的令牌可被后者接受。
使用 |
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(Authentication)
方法,它允许感兴趣的类考虑到这种特殊类型的认证状态。ExceptionTranslationFilter
在处理 AccessDeniedException
实例时使用这个接口。如果抛出一个 AccessDeniedException
,并且认证是匿名类型的,那么过滤器不会抛出一个403(forbidden)响应,而是开始进行 AuthenticationEntryPoint
,这样委托人就可以正常认证了。这是一个必要的区别。否则,委托人将总是被视为 “authenticated” (已认证),而永远不会有机会通过表单、基本、摘要或其他正常认证机制登录。
我们经常看到早期拦截器配置中的 ROLE_ANONYMOUS
属性被替换为 IS_AUTHENTICATED_ANONYMOUSLY
,这在定义访问控制时实际上是一样的。这是一个使用 AuthenticatedVoter
的例子,我们在 授权章节 中会介绍到。它使用一个 AuthenticationTrustResolver
来处理这个特殊的配置属性,并授予匿名用户访问权。 AuthenticatedVoter
的方法更强大,因为它可以让你区分匿名、记住我和完全认证的用户。不过,如果你不需要这个功能,你可以坚持使用 ROLE_ANONYMOUS
,它是由Spring Security的标准 RoleVoter
处理的。
在 Spring MVC 中获得匿名认证
Spring MVC 使用自己的 参数解析器来解析Principal类型的参数。
这意味着,像这样的结构。
-
Java
-
Kotlin
@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"
}
}
将总是返回 "not anonymous",即使是匿名请求。原因是 Spring MVC 使用 HttpServletRequest#getPrincipal
来解析参数,当请求为匿名时,该参数为 null
。
如果你想在匿名请求中获得 Authentication
,请使用 @CurrentSecurityContext
代替。
-
Java
-
Kotlin
@GetMapping("/")
public String method(@CurrentSecurityContext SecurityContext context) {
return context.getAuthentication().getName();
}
@GetMapping("/")
fun method(@CurrentSecurityContext context : SecurityContext) : String =
context!!.authentication!!.name