对于最新的稳定版本,请使用 Spring Security 6.4.1! |
匿名身份验证
概述
通常认为采用“默认拒绝”立场是一种良好的安全实践,其中您明确指定允许的内容并禁止其他所有内容。
定义未经身份验证的用户可以访问的内容也是一种类似的情况,尤其是对于 Web 应用程序。
许多网站要求用户必须对除少数 URL(例如主页和登录页面)之外的任何内容进行身份验证。
在这种情况下,最简单的方法是为这些特定 URL 定义访问配置属性,而不是为每个受保护的资源定义访问配置属性。
换句话说,有时说起来很好ROLE_SOMETHING
默认情况下是必需的,并且仅允许此规则的某些例外,例如应用程序的登录、注销和主页。
您也可以从过滤器链中完全省略这些页面,从而绕过访问控制检查,但这可能是由于其他原因而不希望的,特别是当页面对经过身份验证的用户的行为不同时。
这就是我们所说的匿名身份验证。
请注意,“匿名身份验证”的用户和未经身份验证的用户之间在概念上没有真正的区别。
Spring Security 的匿名身份验证只是为您提供了一种更方便的方式来配置访问控制属性。
对 servlet API 调用的调用,例如getCallerPrincipal
,仍然返回 null,即使SecurityContextHolder
.
在其他情况下,匿名身份验证也很有用,例如,当审计侦听器查询SecurityContextHolder
确定哪个主体负责给定作。
如果类知道SecurityContextHolder
始终包含一个Authentication
object 且从 不包含null
.
配置
当您使用 HTTP 配置(在 Spring Security 3.0 中引入)时,会自动提供匿名身份验证支持。
您可以使用<anonymous>
元素。
除非您使用传统的 bean 配置,否则无需配置此处描述的 bean。
三个类协同工作以提供匿名身份验证功能。AnonymousAuthenticationToken
是Authentication
并存储GrantedAuthority
实例。
有一个对应的AnonymousAuthenticationProvider
,它被链接到ProviderManager
因此AnonymousAuthenticationToken
接受实例。
最后,一个AnonymousAuthenticationFilter
在正常的身份验证机制之后被链接,并自动添加AnonymousAuthenticationToken
到SecurityContextHolder
如果没有Authentication
被关押在那里。
筛选条件和身份验证提供程序定义如下:
<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]
.
在userMap
的属性InMemoryDaoImpl
.
如前所述,匿名身份验证的好处是所有 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
interface 及其相应的AuthenticationTrustResolverImpl
实现。
此接口提供了一个isAnonymous(Authentication)
方法,它允许感兴趣的类考虑这种特殊类型的身份验证状态。
这ExceptionTranslationFilter
在处理过程中使用此接口AccessDeniedException
实例。
如果AccessDeniedException
,并且身份验证是匿名类型的,而不是引发 403(禁止)响应,而是开始AuthenticationEntryPoint
,以便 Principal 可以正确进行身份验证。
这是一个必要的区别。否则,主体将始终被视为“经过身份验证”,并且永远不会有机会通过 form、basic、digest 或其他一些正常的身份验证机制登录。
我们经常看到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