Anonymous Authentication
Overview
一般来说,采用 “deny-by-default” 立场被认为是良好的安全实践,在这种立场上,你明确指定允许的内容并禁止其他所有内容。定义未经身份验证的用户可以访问的内容是类似的情况,特别是对于网络应用程序而言。许多网站要求用户必须对除少数几个 URL(例如主页和登录页面)之外的所有内容进行身份验证。在这种情况下,最简单的办法是为这些特定的 URL 而不是为每个受保护资源定义访问配置属性。换句话说,有时需要明确表示登录的应用程序的主页和注销页面会 ROLE_SOMETHING
要求进行身份验证,并且仅允许对该规则进行某些例外,例如登录页面、注销页面和主页。你也可以从筛选链中完全省略这些页面,从而绕过访问控制检查,但这对于其他原因,特别是如果页面对经过身份验证的用户表现不同,可能是不合适的。
It is generally considered good security practice to adopt a “deny-by-default” stance, where you explicitly specify what is allowed and disallow everything else.
Defining what is accessible to unauthenticated users is a similar situation, particularly for web applications.
Many sites require that users must be authenticated for anything other than a few URLs (for example the home and login pages).
In that case, it is easiest to define access configuration attributes for these specific URLs rather than for every secured resource.
Put differently, sometimes it is nice to say ROLE_SOMETHING
is required by default and allow only certain exceptions to this rule, such as for login, logout, and home pages of an application.
You could also omit these pages from the filter chain entirely, thus bypassing the access control checks, but this may be undesirable for other reasons, particularly if the pages behave differently for authenticated users.
这就是我们所说的匿名身份验证。请注意,“anonymously authenticated” 的用户和未经身份验证的用户之间没有真正概念上的区别。Spring Security 的匿名身份验证只是为您提供了一种更方便的方式来配置您的访问控制属性。调用 servlet API 调用(如 getCallerPrincipal
)仍会返回 null,即使 SecurityContextHolder
中实际上有一个匿名身份验证对象。
This is what we mean by anonymous authentication.
Note that there is no real conceptual difference between a user who is “anonymously authenticated” and an unauthenticated user.
Spring Security’s anonymous authentication just gives you a more convenient way to configure your access-control attributes.
Calls to servlet API calls, such as getCallerPrincipal
, still return null, even though there is actually an anonymous authentication object in the SecurityContextHolder
.
匿名身份验证在其他情况下也很有用,例如当审计拦截器查询 SecurityContextHolder
以识别哪个主体负责某项操作时。如果它们知道 SecurityContextHolder
始终包含 Authentication
对象并且从不包含 null
,则可以更加稳健地编写类。
There are other situations where anonymous authentication is useful, such as when an auditing interceptor queries the SecurityContextHolder
to identify which principal was responsible for a given operation.
Classes can be authored more robustly if they know the SecurityContextHolder
always contains an Authentication
object and never contains null
.
Configuration
当您使用 HTTP 配置(在 Spring Security 3.0 中引入)时,会自动提供匿名身份验证支持。您可以使用 <anonymous>
元素自定义(或禁用)它。除非您使用传统的 bean 配置,否则不需要配置此处描述的 bean。
Anonymous authentication support is provided automatically when you use the HTTP configuration (introduced in Spring Security 3.0).
You can customize (or disable) it by using the <anonymous>
element.
You need not configure the beans described here unless you are using traditional bean configuration.
三个类共同协作以提供匿名身份验证功能。AnonymousAuthenticationToken
是 Authentication
的实现,它存储适用于匿名主体的 GrantedAuthority
实例。有一个相应的 AnonymousAuthenticationProvider
,它链接到 ProviderManager
,这样就不会接受 AnonymousAuthenticationToken
实例。最后,一个 AnonymousAuthenticationFilter
在正常身份验证机制之后链接,并且如果 SecurityContextHolder
中不存在 Authentication
,它会自动向 SecurityContextHolder
中添加 AnonymousAuthenticationToken
。过滤器和身份验证提供程序定义如下:
Three classes work together to provide the anonymous authentication feature.
AnonymousAuthenticationToken
is an implementation of Authentication
and stores the GrantedAuthority
instances that apply to the anonymous principal.
There is a corresponding AnonymousAuthenticationProvider
, which is chained into the ProviderManager
so that AnonymousAuthenticationToken
instances are accepted.
Finally, an AnonymousAuthenticationFilter
is chained after the normal authentication mechanisms and automatically adds an AnonymousAuthenticationToken
to the SecurityContextHolder
if there is no existing Authentication
held there.
The filter and authentication provider is defined as follows:
<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
在过滤器和身份验证提供程序之间共享,以便前者创建的令牌被后者接受
The key
is shared between the filter and authentication provider, so that tokens created by the former are accepted by the latter
此处不应将 The use of the |
userAttribute
以 usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]
的形式表示。InMemoryDaoImpl
的 userMap
属性在等号后面也使用相同的语法。
The userAttribute
is expressed in the form of usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]
.
The same syntax is used after the equals sign for the userMap
property of InMemoryDaoImpl
.
如前所述,匿名身份验证的好处是所有 URI 模式都可以对其应用安全性,如下例所示:
As explained earlier, the benefit of anonymous authentication is that all URI patterns can have security applied to them, as the following example shows:
<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(禁止)响应,而是开始 AuthenticationEntryPoint
,以便主体可以正确地进行身份验证。这是一个必要的区别。否则,主体将始终被视为 “authenticated”,并且永远不会有机会通过表单、基本、摘要或其他一些正常身份验证机制登录。
Rounding out the anonymous authentication discussion is the AuthenticationTrustResolver
interface, with its corresponding AuthenticationTrustResolverImpl
implementation.
This interface provides an isAnonymous(Authentication)
method, which allows interested classes to take into account this special type of authentication status.
The ExceptionTranslationFilter
uses this interface in processing AccessDeniedException
instances.
If an AccessDeniedException
is thrown and the authentication is of an anonymous type, instead of throwing a 403 (forbidden) response, the filter, instead, commences the AuthenticationEntryPoint
so that the principal can authenticate properly.
This is a necessary distinction. Otherwise, principals would always be deemed “authenticated” and never be given an opportunity to login through form, basic, digest, or some other normal authentication mechanism.
我们经常看到早先拦截器配置中的 ROLE_ANONYMOUS
属性被替换为 IS_AUTHENTICATED_ANONYMOUSLY
,这在定义访问控制时实际上是同一回事。这是 AuthenticatedVoter
使用的一个示例,我们在 authorization chapter 中涵盖了它。它使用 AuthenticationTrustResolver
处理此特定配置属性并授予匿名用户访问权限。AuthenticatedVoter
方法更强大,因为它可以区分匿名用户、记住我的用户和完全经过身份验证的用户。但是,如果您不需要此功能,您可以坚持使用 ROLE_ANONYMOUS
,它是由 Spring Security 的标准 RoleVoter
处理的。
We often see the ROLE_ANONYMOUS
attribute in the earlier interceptor configuration replaced with IS_AUTHENTICATED_ANONYMOUSLY
, which is effectively the same thing when defining access controls.
This is an example of the use of the AuthenticatedVoter
, which we cover in the authorization chapter.
It uses an AuthenticationTrustResolver
to process this particular configuration attribute and grant access to anonymous users.
The AuthenticatedVoter
approach is more powerful, since it lets you differentiate between anonymous, remember-me, and fully authenticated users.
If you do not need this functionality, though, you can stick with ROLE_ANONYMOUS
, which is processed by Spring Security’s standard RoleVoter
.
Getting Anonymous Authentications with Spring MVC
@ {s1} 使用自己的参数解析程序。
Spring MVC resolves parameters of type Principal
using its own argument resolver.
这意味着像这样的结构:
This means that a construct like this one:
-
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"
}
}
即使对于匿名请求,也总能返回“不是匿名”。原因是 Spring MVC 使用 HttpServletRequest#getPrincipal
来解析参数,当请求是匿名的时,HttpServletRequest#getPrincipal
为 null
。
will always return "not anonymous", even for anonymous requests.
The reason is that Spring MVC resolves the parameter using HttpServletRequest#getPrincipal
, which is null
when the request is anonymous.
如果您想在匿名请求中获取 Authentication
,请改用 @CurrentSecurityContext
:
If you’d like to obtain the Authentication
in anonymous requests, use @CurrentSecurityContext
instead:
-
Java
-
Kotlin
@GetMapping("/")
public String method(@CurrentSecurityContext SecurityContext context) {
return context.getAuthentication().getName();
}
@GetMapping("/")
fun method(@CurrentSecurityContext context : SecurityContext) : String =
context!!.authentication!!.name