匿名認證
概述
普遍認為,採用“預設拒絕”姿態是良好的安全實踐,即明確指定允許的內容,並拒絕其他所有內容。特別是對於 Web 應用程式,定義對未認證使用者可訪問的內容與此類似。許多站點要求除了少數 URL(例如首頁和登入頁面)之外,使用者必須經過認證才能訪問任何內容。在這種情況下,最簡單的做法是為這些特定的 URL 定義訪問配置屬性,而不是為每個受保護的資源定義。換句話說,有時最好預設要求 ROLE_SOMETHING
,只允許某些例外規則,例如應用程式的登入、登出和首頁。您也可以將這些頁面完全排除在過濾器鏈之外,從而繞過訪問控制檢查,但這可能因其他原因而不可取,特別是如果這些頁面對於已認證使用者表現不同的話。
這就是我們所說的匿名認證。請注意,“匿名認證”的使用者與未認證使用者之間沒有真正的概念差異。Spring Security 的匿名認證只是為您配置訪問控制屬性提供了一種更便捷的方式。即使 SecurityContextHolder
中實際存在匿名認證物件,對 servlet API 呼叫(例如 getCallerPrincipal
)的呼叫仍然返回 null。
在其他情況下,匿名認證也很有用,例如當審計攔截器查詢 SecurityContextHolder
以識別哪個主體負責給定操作時。如果類知道 SecurityContextHolder
總是包含一個 Authentication
物件而永遠不包含 null
,那麼它們的編寫會更健壯。
配置
當您使用 HTTP 配置(在 Spring Security 3.0 中引入)時,會自動提供匿名認證支援。您可以使用 <anonymous>
元素自定義(或停用)它。除非您使用傳統的 Bean 配置,否則無需配置此處描述的 Bean。
三個類協同工作以提供匿名認證功能。AnonymousAuthenticationToken
是 Authentication
的實現,儲存應用於匿名主體的 GrantedAuthority
例項。有一個相應的 AnonymousAuthenticationProvider
,它被連結到 ProviderManager
中,以便接受 AnonymousAuthenticationToken
例項。最後,AnonymousAuthenticationFilter
被連結在正常的認證機制之後,如果在 SecurityContextHolder
中沒有現有 Authentication
,則自動新增一個 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(禁止訪問)響應,而是開始執行 AuthenticationEntryPoint
,以便主體可以正確認證。這是一個必要的區別。否則,主體將始終被視為“已認證”,並且永遠不會有機會透過表單、基本、摘要或其他正常認證機制登入。
我們經常看到早期攔截器配置中的 ROLE_ANONYMOUS
屬性被替換為 IS_AUTHENTICATED_ANONYMOUSLY
,這在定義訪問控制時實際上是同一回事。這是 AuthenticatedVoter
使用的一個示例,我們在授權章節中會介紹。它使用 AuthenticationTrustResolver
來處理此特定配置屬性並授予匿名使用者訪問許可權。AuthenticatedVoter
方法更強大,因為它允許您區分匿名、記住我(remember-me)和完全認證使用者。但是,如果您不需要此功能,則可以繼續使用由 Spring Security 的標準 RoleVoter
處理的 ROLE_ANONYMOUS
。
在 Spring MVC 中獲取匿名認證
這意味著像這樣的構造
-
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