关于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());
    }

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

发布了30 篇原创文章 · 获赞 15 · 访问量 9846

猜你喜欢

转载自blog.csdn.net/weixin_41884118/article/details/88051185
今日推荐