關於spring security的URL路徑驗證問題

SecureResourceFilterInvocationDefinitionSource部分

/** 
 * RegexUrlPathMatcher默認不進行小寫轉換,而AntUrlPathMatcher默認要進行小寫轉換 
 */  
    public void afterPropertiesSet() throws Exception {  
          
        // default url matcher will be RegexUrlPathMatcher  
        this.urlMatcher = new RegexUrlPathMatcher();  
          
        if (useAntPath) {  // change the implementation if required  
            this.urlMatcher = new AntUrlPathMatcher();  
        }  
          
        // Only change from the defaults if the attribute has been set  
        if ("true".equals(lowercaseComparisons)) {  
            if (!this.useAntPath) {  
                ((RegexUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(true);  
            }  
        } else if ("false".equals(lowercaseComparisons)) {  
            if (this.useAntPath) {  
                //是否對URL全部轉換成小寫格式  
                ((AntUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(false);  
            }  
        }  
          
    }  
  
//這個方法主要會在FilterSecurityInterceptor->AbstractSecurityInterceptor->beforeInvocation中用到  
    public ConfigAttributeDefinition getAttributes(Object filter) throws IllegalArgumentException {  
          
        FilterInvocation filterInvocation = (FilterInvocation) filter;  
        String requestURI = filterInvocation.getRequestUrl();  
        Map<String, String> urlAuthorities = this.getUrlAuthorities(filterInvocation);  
          
        String grantedAuthorities = null;  
        for(Iterator<Map.Entry<String, String>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {  
            Map.Entry<String, String> entry = iter.next();  
              
            //url表示從資源表取出的值,在這裏代表的是相應的URL  
            String url = entry.getKey();  
              
            //這段代碼表示數據庫內的需要驗證的資源URL與當前請求的URL相匹配時進行驗證  
            if(urlMatcher.pathMatchesUrl(url, requestURI)) {  
                //grantedAuthorities表示每個資源對應的角色,如果有多個角色,則以','隔開  
                grantedAuthorities = entry.getValue();  
                break;  
            }  
        }  
          
        if(grantedAuthorities != null) {  
            ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();  
            configAttrEditor.setAsText(grantedAuthorities);  
            return (ConfigAttributeDefinition) configAttrEditor.getValue();  
        }  
          
        //返回null表示不會驗證  
        return null;  
    }  

這個方法的主要作用是從數據庫的resource表加載出所有的資源URL值,通過它與request請求的URL相比較,而比較器一般採用AntUrlPathMatcher,如果需要更加靈活的方式,可以使用RegexUrlPathMatcher,先介紹一下上面的getAttributes是怎麼通過URL進行驗證的。從上面的for循環可以看出,首先遍歷資源URL的列表,如果發現有與當前request請求URL相匹配的,就進行驗證,如果當前用戶擁有的角色與此資源URL對應的角色相同,則通過驗證,否則禁止訪問。見下圖:
在這裏插入圖片描述
上面一張簡單的圖,基本能說明意思,需要說明的是隻要發現有與當前請求路徑相匹配的,就會進行驗證,而且只驗證一次,沒有通過驗證也不會再檢查是否匹配第二個資源URL,的當然如果請求的URL與第一個資源URL不匹配,就會繼續向下查找,直到找到與請求URL匹配的資源URL爲止,如果遍歷完以後,還是沒有查到,就到棄權處理,至於所有投票者都棄權以後,該怎麼處理,前一篇博客有介紹的。由此可知,這個資源的URL路徑的順序就比較重要了。如果遍歷出來的第一個資源URL爲/**,而普通用戶角色對應的資源URL沒有它,那麼即使普通角色擁有其它的資源URL權限也是不能訪問到相應的頁面的。但是如果普通用戶有/**的權限,而/**是匹配所有路徑的,如/admin/index.jsp也是會匹配的,這樣就相當於普通用戶擁有任何路徑的權限,這也是不行的。所以遍歷出來的資源URL順序很重要。

在基於XML的方式授權時就是把/**放在最後面的,如:

<intercept-url pattern="/secure/extreme/**" access="ROLE_SUPERVISOR"/>  
        <intercept-url pattern="/secure/**" access="IS_AUTHENTICATED_REMEMBERED" />  
        <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />  

也就是說,這個路徑放的順序很重要,在數據庫裏存放URL時也要遵循這樣的規則,如上面的XML放在數據庫裏應該這樣:
在這裏插入圖片描述
這裏需要多做的一個步驟是,取出來後不要改變取數據庫記錄的順序。在SecurityManagerSupport中的代碼:

public Map<String, String> loadUrlAuthorities() {  
        Map<String, String> urlAuthorities = new LinkedHashMap<String, String>();  
          
        @SuppressWarnings("unchecked")  
        List<Resource> urlResources = getHibernateTemplate().find("FROM Resource resource WHERE resource.type = ?", "URL");  
        for(Resource resource : urlResources) {  
            urlAuthorities.put(resource.getValue(), resource.getRoleAuthorities());  
        }  
        return urlAuthorities;  
    }  

要保持取出的數據的順序不改變,需要使用LinkedHashMap,這樣SecureResourceFilterInvocationDefinitionSource在驗證URL的順序就與數據庫裏面存的順序一致了,表面上看的確有些不靈活,而實際上,在使用中時資源URL是固定的,用戶不能改變的,所以這也沒什麼影響。

關於AntUrlPathMatcher的匹配規則也很簡單,文檔中有說明:

* <li>? matches one character</li>  
* <li>* matches zero or more characters</li>  
* <li>** matches zero or more 'directories' in a path</li>  

//
//
//
//

Spring Security通過URL模式匹配的聲明式權限控制

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable().authorizeRequests()
                .antMatchers(HttpMethod.GET, "/??/??/??/**").permitAll() //不驗證token
                .anyRequest().authenticated()
                .and()
                //.addFilter(new JWTLoginFilter(authenticationManager()))
                .addFilter(jwtAuthenticationFilterBean());
    }

"/??/??/??/** " 不要漏掉 / **

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章