spring Security 随笔

HttpSecurityBeanDefinitionParser 解析spring-security.xml文件的类,在这里注册的springSecurityFilterChain的另一半,spring 容器里的部分
parse方法是解析整个类的入口,

public BeanDefinition parse(Element element, ParserContext pc) {
        CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
        pc.pushContainingComponent(compositeDef);
        registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
        BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition("org.springframework.security.filterChains");
        List<BeanReference> filterChains = (List)listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();
        filterChains.add(this.createFilterChain(element, pc));
        pc.popAndRegisterContainingComponent();
        return null;
    }

其中:
registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));注册了springSecurityFilterChain的另一半,请看方法的最后一句

 static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
        if (!pc.getRegistry().containsBeanDefinition("org.springframework.security.filterChainProxy")) {
            BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
            listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
            pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, "org.springframework.security.filterChains"));
            BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
            fcpBldr.getRawBeanDefinition().setSource(source);
            fcpBldr.addConstructorArgReference("org.springframework.security.filterChains");
            fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
            BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
            pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, "org.springframework.security.filterChainProxy"));
            pc.getRegistry().registerAlias("org.springframework.security.filterChainProxy", "springSecurityFilterChain");
        }
    }

filterChains.add(this.createFilterChain(element, pc));注册了整个spring security的filterchain

这个方法会调用类 HttpConfigurationBuilder,它构造函数里定义并初始化了spring security的默认filterchain,构造函数里由这么一段:

this.createCsrfFilter();
this.createSecurityContextPersistenceFilter();
this.createSessionManagementFilters();
this.createWebAsyncManagerFilter();
this.createRequestCacheFilter();
this.createServletApiFilter(authenticationManager);
this.createJaasApiFilter();
this.createChannelProcessingFilter();
this.createFilterSecurityInterceptor(authenticationManager);
this.createAddHeadersFilter();
this.createCorsFilter();

其中有一个比较重要的filter,FilterSecurityInterceptor。这个过滤器spring security是会注册的,应该是不可修改的。但是这个FilterSecurityInterceptorauthenticationManageraccessDecisionManagersecurityMetadataSource都不是自定义的,所以需要配置FilterSecurityInterceptor这个过滤器,把自己实现的类注入进去

<beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="accessDecisionManager" ref="accessDecisionManager" />
    <beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean>

然后把FilterSecurityInterceptor配置到<http>里的<custom-filter>标签里,

<http>
    ...... 省略
    <custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
    ......<custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
</http>

这里一定要使用before,位置就在FILTER_SECURITY_INTERCEPTOR。千万不能携程position,如果这样在启动的时候会报错Filter beans '" + filter.bean + "' and '" + previous.bean + "' have the same 'order' value. When using custom filters, please make sure the positions do not conflict with default filters. Alternatively you can disable the default filters by removing the corresponding child elements from <http> and avoiding the use of <http auto-config='true'>.
因为spring security在2400的位置上注册了一个FilterSecurityInterceptor,我们又通过custom-filter的方式在2400的位置上注册了一个,两个位置相同了。

可能会担心,我注册了两个FilterSecurityInterceptor,会不会出现重复调用的情况,因为一个request是要走完整个定义的filterchain的。我在2399的位置调用了自己定义的FilterSecurityInterceptor,在2400的位置是不是又走一遍,造成重复鉴权,降低效率??

通过代码跟踪,确实会第二次走FilterSecurityInterceptor,但是有个方法处理了

public void invoke(FilterInvocation fi) throws IOException, ServletException {
    if (fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
    } else {
        if (fi.getRequest() != null) {
            fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
        }

        InterceptorStatusToken token = super.beforeInvocation(fi);

        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.finallyInvocation(token);
        }

        super.afterInvocation(token, (Object)null);
    }

}

第一遍的时候,第一个if
fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null会返回一个false,从而进入else语句,也就调用了super.beforeInvocation(fi);,这样就会调用父类AbstractSecurityInterceptorbeforeInvocation(Object object),在这个地方就是使用我们自定义metadata、决策器的入口了。

第二次调用的时候,第一个if
fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null就会返回一个true,至今不知道为什么。
这样就进入了第一个if里,直接就fi.getChain().doFilter(fi.getRequest(), fi.getResponse());,等于是啥逻辑也不知行了,空执行了一遍,所以也没有进行重复鉴权。
其实也没有耗费太多性能。

猜你喜欢

转载自blog.csdn.net/gezilan/article/details/80250339
今日推荐