由於我們配置了訪問控制(授權)的默認攔截器org.springframework.security.web.access.intercept.FilterSecurityInterceptor。其主要業務方法是InterceptorStatusToken beforeInvocation(Object object)
該方法會將URL傳給SecurityMetadataSource獲取匹配該URL所有參數ConfigAttribute(擁有權限的角色)的集合。
如果該用戶尚未認證(登錄),或攔截器配置了“始終認證”,則攔截器會將該用戶的登錄信息(未登錄則跳轉到登陸頁面)重新認證,並加載角色信息。
隨後將用戶認證信息(Authentication),用戶請求訪問的URL,以及配置集合(Collection<ConfigAttribute>)交由accessDecisionManager的decide方法。通過則方法繼續,否則拋出AccessDeniedException。
調用runAsManager嘗試轉換認證信息,這是爲了方便適應兩層訪問控制架構。runAsManager就可以將外部公開的認證,轉換爲內部認證,繼續之後的訪問。
之後返回包含訪問攔截信息的對象InterceptorStatusToken。以便afterInvocation(InterceptorStatusToken, Object)方法運行。
安全信息元數據提供者:
默認實現DefaultFilterInvocationSecurityMetadataSource構造時通過addSecureUrl(String pattern, String method, Collection<ConfigAttribute> attrs)方法將傳入參數轉換爲私有成員變量Map<String, Map<Object, Collection<ConfigAttribute>>> httpMethodMap緩存,並通過Collection<ConfigAttribute> lookupAttributes(String url, String method)獲得第一個能匹配的上的URL模式,並返回其所需要的角色集合Collection<ConfigAttribute>
訪問控制管理者:
AbstractAccessDecisionManager的三個子類AffirmativeBased, UnanimousBased,ConsensusBased。
RoleVoter實現了AccessDecisionVoter接口,其vote方法是這樣寫的。
- public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
- int result = ACCESS_ABSTAIN;
- Collection<GrantedAuthority> authorities = extractAuthorities(authentication);
- for (ConfigAttribute attribute : attributes) {
- if (this.supports(attribute)) {
- result = ACCESS_DENIED;
- // 查找匹配角色
- for (GrantedAuthority authority : authorities) {
- if (attribute.getAttribute().equals(authority.getAuthority())) {
- return ACCESS_GRANTED;
- }
- }
- }
- }
- return result;
- }
- Collection<GrantedAuthority> extractAuthorities(Authentication authentication) {
- return authentication.getAuthorities();
- }
public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
int result = ACCESS_ABSTAIN;
Collection<GrantedAuthority> authorities = extractAuthorities(authentication);
for (ConfigAttribute attribute : attributes) {
if (this.supports(attribute)) {
result = ACCESS_DENIED;
// 查找匹配角色
for (GrantedAuthority authority : authorities) {
if (attribute.getAttribute().equals(authority.getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
Collection<GrantedAuthority> extractAuthorities(Authentication authentication) {
return authentication.getAuthorities();
}
ps:DefaultFilterInvocationSecurityMetadataSource初始化過程詳解
Spring Security 3 訪問驗證模塊的初始化,主要是FilterInvocationSecurityMetadataSource對象加載配置信息的過程。
而FilterInvocationSecurityMetadataSource的實現,我選用了框架提供的默認實現DefaultFilterInvocationSecurityMetadataSource。
在DefaultFilterInvocationSecurityMetadataSource 構造的時候,需要UrlMatcher和LinkedHashMap<RequestKey, Collection<ConfigAttribute>>兩個參數。
前者是解析Url的匹配器,框架提供兩種選擇:一個是AntUrlPathMatcher,另一個是正則匹配。當然也可以自己寫,只要實現UrlMatcher接口就行。
第二個參數需要提供 所有的請求URL模板,與其對應所有的配置屬性(在RBAC系統中即爲“角色碼”)。
例如:RequestKey爲 /user!add**, Collection<ConfigAttribute>爲[ROLE_HR, ROLE_ADMIN]。就是說,能有權限訪問用戶添加模塊的角色爲HR,和管理員。
在DefaultFilterInvocationSecurityMetadataSource 的構造方法當中,其通過addSecureUrl(String pattern, String method, Collection<ConfigAttribute> attrs)方法將傳入參數轉換爲私有成員變量Map<String, Map<Object, Collection<ConfigAttribute>>> httpMethodMap;
key是Http方法:主要“GET”"和POST",Value是Key的HTTP方法對應的配置屬性集合。key爲null時,value爲不特定方法的配置屬性集合。而Value也是一個Map,它的主鍵是編譯後的URL模板,值爲各種角色的ConfigAttribute對象。