`
Dead_knight
  • 浏览: 1206106 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
博客专栏
752c8642-b795-3fe6-946e-a4e845bffdec
Spring Securi...
浏览量:241621
33caa84e-18a6-3036-a82b-6e2106a4de63
clojure专题
浏览量:49490
E17ca077-44df-3816-a3fe-471c43f6e1e5
WebLogic11g
浏览量:237615
社区版块
存档分类
最新评论

Spring Security3源码分析-Filter链排序分析

阅读更多
通过前面Spring Security提供的各种Filter的分析,大体上知道每个Filter具体的用途了。
Spring Security一共提供了20个Filter,我目前只分析了13个(如果http的auto-config="true",那默认的filter列表都包含在这13个里面了),另外7个在后面的源码分析中碰到时会逐个讲解。
在分析http标签时,已经提到filter排序的问题了,但是没有深入。

现在再回头看filter是如何排序的。下面的代码片段截取自HttpSecurityBeanDefinitionParser类,
        //定义未排序filter集合。该集合中的对象为OrderDecorator实例。
        List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
        //添加http、认证相关的filter集合
        unorderedFilterChain.addAll(httpBldr.getFilters());
        unorderedFilterChain.addAll(authBldr.getFilters());
        //定义RequestCacheAwareFilter过滤器,并添加到unorderedFilterChain中
        BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);
        requestCacheAwareFilter.getPropertyValues().addPropertyValue("requestCache", authBldr.getRequestCache());
        unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));
        //添加自定义filter
        unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
        //根据排序规则进行排序
        Collections.sort(unorderedFilterChain, new OrderComparator());
        //检查每个filter与前一个filter的位置是否相同
         //这里的检查主要是防止自定义filter直接配置position属性,造成与默认的filter产生order冲突
        checkFilterChainOrder(unorderedFilterChain, pc, source);
        //重新定义filterChain,把经过排序的filter依次添加到filterChain集合中
        List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();

        for (OrderDecorator od : unorderedFilterChain) {
            filterChain.add(od.bean);
        }

细心的同学会发现httpBldr.getFilters()、authBldr.getFilters()两个方法返回的就是OrderDecorator对象的集合列表。并且OrderDecorator对象已经将filter与SecurityFilters中的order关联了起来
        unorderedFilterChain.addAll(httpBldr.getFilters());
        unorderedFilterChain.addAll(authBldr.getFilters());


顺便看一下创建自定义过滤器部分
    List<OrderDecorator> buildCustomFilterList(Element element, ParserContext pc) {
        List<Element> customFilterElts = DomUtils.getChildElementsByTagName(element, Elements.CUSTOM_FILTER);
        List<OrderDecorator> customFilters = new ArrayList<OrderDecorator>();

        final String ATT_AFTER = "after";
        final String ATT_BEFORE = "before";
        final String ATT_POSITION = "position";
        //循环自定义标签列表custom-filter
        for (Element elt: customFilterElts) {
            String after = elt.getAttribute(ATT_AFTER);
            String before = elt.getAttribute(ATT_BEFORE);
            String position = elt.getAttribute(ATT_POSITION);

            String ref = elt.getAttribute(ATT_REF);

            if (!StringUtils.hasText(ref)) {
                pc.getReaderContext().error("The '" + ATT_REF + "' attribute must be supplied", pc.extractSource(elt));
            }

            RuntimeBeanReference bean = new RuntimeBeanReference(ref);

            if(WebConfigUtils.countNonEmpty(new String[] {after, before, position}) != 1) {
                pc.getReaderContext().error("A single '" + ATT_AFTER + "', '" + ATT_BEFORE + "', or '" +
                        ATT_POSITION + "' attribute must be supplied", pc.extractSource(elt));
            }
            //如果指定了position,直接将filter与order产生关联关系
            if (StringUtils.hasText(position)) {
                customFilters.add(new OrderDecorator(bean, SecurityFilters.valueOf(position)));
            //如果指定了after,将filter与after值加一产生关联关系
            } else if (StringUtils.hasText(after)) {
                SecurityFilters order = SecurityFilters.valueOf(after);
                if (order == SecurityFilters.LAST) {
                    customFilters.add(new OrderDecorator(bean, SecurityFilters.LAST));
                } else {
                    customFilters.add(new OrderDecorator(bean, order.getOrder() + 1));
                }
            //如果指定了before,将filter与before-1产生关联关系
            } else if (StringUtils.hasText(before)) {
                SecurityFilters order = SecurityFilters.valueOf(before);
                if (order == SecurityFilters.FIRST) {
                    customFilters.add(new OrderDecorator(bean, SecurityFilters.FIRST));
                } else {
                    customFilters.add(new OrderDecorator(bean, order.getOrder() - 1));
                }
            }
        }

        return customFilters;
    }


这里用到三个重要的与排序相关的类及枚举,分别是OrderDecorator、OrderComparator以及SecurityFilters枚举
首先看SecurityFilters枚举定义
enum SecurityFilters {
    FIRST (Integer.MIN_VALUE),
    //order=100
    CHANNEL_FILTER,
    //order=200
    CONCURRENT_SESSION_FILTER,
    //依次递增……
    SECURITY_CONTEXT_FILTER,
    LOGOUT_FILTER,
    X509_FILTER,
    PRE_AUTH_FILTER,
    CAS_FILTER,
    FORM_LOGIN_FILTER,
    OPENID_FILTER,
    LOGIN_PAGE_FILTER,
    DIGEST_AUTH_FILTER,
    BASIC_AUTH_FILTER,
    REQUEST_CACHE_FILTER,
    SERVLET_API_SUPPORT_FILTER,
    REMEMBER_ME_FILTER,
    ANONYMOUS_FILTER,
    SESSION_MANAGEMENT_FILTER,
    EXCEPTION_TRANSLATION_FILTER,
    FILTER_SECURITY_INTERCEPTOR,
    SWITCH_USER_FILTER,
    LAST (Integer.MAX_VALUE);
    //这里设置100,主要给自定义过滤器提供after、before的预留位置
    //也就是说,在某个默认的过滤器前后只能自定义99个过滤器,虽然可能性几乎为0
    private static final int INTERVAL = 100;
    private final int order;
    //返回的order值=序号*间隔100
    private SecurityFilters() {
        order = ordinal() * INTERVAL;
    }

    private SecurityFilters(int order) {
        this.order = order;
    }
    //主要通过该方法返回Filter的位置
    public int getOrder() {
       return order;
    }
}

由此可见,该类维护了Spring Security中每个filter的顺序

接着看OrderDecorator类。这个类实现org.springframework.core.Ordered接口
class OrderDecorator implements Ordered {
    BeanMetadataElement bean;
    int order;
    //构造函数传递两个参数1.bean定义;2.filter在链中的位置
    public OrderDecorator(BeanMetadataElement bean, SecurityFilters filterOrder) {
        this.bean = bean;
        this.order = filterOrder.getOrder();
    }

    public OrderDecorator(BeanMetadataElement bean, int order) {
        this.bean = bean;
        this.order = order;
    }
    //实现接口方法getOrder
    public int getOrder() {
        return order;
    }

    public String toString() {
        return bean + ", order = " + order;
    }
}

OrderComparator类的路径是org.springframework.core.OrderComparator,实际上是spring core包的一个比较器,可以顺便看下OrderComparator源码。下面截取的只是部分核心代码
	public int compare(Object o1, Object o2) {
		boolean p1 = (o1 instanceof PriorityOrdered);
		boolean p2 = (o2 instanceof PriorityOrdered);
		if (p1 && !p2) {
			return -1;
		}
		else if (p2 && !p1) {
			return 1;
		}
		//前面几行代码主要针对PriorityOrdered,这里不做分析

                      //分别获取Ordered接口实现类的getOrder方法得到order值
		int i1 = getOrder(o1);
		int i2 = getOrder(o2);
                   //对得到的order进行比较
		return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
	}
          //获取Ordered接口的实现类,获取getOrder值
	protected int getOrder(Object obj) {
		return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : Ordered.LOWEST_PRECEDENCE);
	}


通过以上的分析,可以总结如下

1.由SecurityFilters维持位置order
2.由OrderDecorator维持filter与order的对应关系
3.由OrderComparator负责比较OrderDecorator的先后顺序


附上默认的过滤器顺序列表
order过滤器名称
100ChannelProcessingFilter
200ConcurrentSessionFilter
300SecurityContextPersistenceFilter
400LogoutFilter
500X509AuthenticationFilter
600RequestHeaderAuthenticationFilter
700CasAuthenticationFilter
800UsernamePasswordAuthenticationFilter
900OpenIDAuthenticationFilter
1000DefaultLoginPageGeneratingFilter
1100DigestAuthenticationFilter
1200BasicAuthenticationFilter
1300RequestCacheAwareFilter
1400SecurityContextHolderAwareRequestFilter
1500RememberMeAuthenticationFilter
1600AnonymousAuthenticationFilter
1700SessionManagementFilter
1800ExceptionTranslationFilter
1900FilterSecurityInterceptor
2000SwitchUserFilter

以上标注红色的都已经分析完毕
分享到:
评论
6 楼 youjianbo_han_87 2013-12-08  
Dead_knight 写道
youjianbo_han_87 写道
Dead_knight 写道
leyou 写道
已经找到解决办法了,使用CompositeFilter就行了

办法很多种啊,可以使用一个after+一个before。或者直接使用position属性直接指定位置

可以自定义 LogoutFilter不。因为我这边session信息是写到缓存里面的。当登出的时候,需要将缓存里面的权限信息删除。

在logoutfilter前面增加一个自定义过滤器清除缓存的就行了

谢谢,我想到了另外一个思路,应用这边都不需要做特别的配置。
我增加一个监听器,来保存和移除缓存中权限信息,一个过滤器,来恢复当服务器宕机重启或者切换时的权限信息。
5 楼 Dead_knight 2013-12-07  
youjianbo_han_87 写道
Dead_knight 写道
leyou 写道
已经找到解决办法了,使用CompositeFilter就行了

办法很多种啊,可以使用一个after+一个before。或者直接使用position属性直接指定位置

可以自定义 LogoutFilter不。因为我这边session信息是写到缓存里面的。当登出的时候,需要将缓存里面的权限信息删除。

在logoutfilter前面增加一个自定义过滤器清除缓存的就行了
4 楼 youjianbo_han_87 2013-12-07  
Dead_knight 写道
leyou 写道
已经找到解决办法了,使用CompositeFilter就行了

办法很多种啊,可以使用一个after+一个before。或者直接使用position属性直接指定位置

可以自定义 LogoutFilter不。因为我这边session信息是写到缓存里面的。当登出的时候,需要将缓存里面的权限信息删除。
3 楼 Dead_knight 2013-11-28  
leyou 写道
已经找到解决办法了,使用CompositeFilter就行了

办法很多种啊,可以使用一个after+一个before。或者直接使用position属性直接指定位置
2 楼 leyou 2013-11-28  
已经找到解决办法了,使用CompositeFilter就行了
1 楼 leyou 2013-11-28  
你好,非常感谢你的文章,看了受益匪浅,不过我想请问一下,关于custom-filter,比如,我需要在LogoutFilter前自定义两个Filter,但是before只能写一个 before="LOGOUT_FILTER",这个该如何解决呢?

相关推荐

    spring security源码分析.pdf

    ### Spring Security 源码分析知识...以上内容涵盖了 Spring Security 3 的源码分析中几个关键点的具体内容。通过对这些内容的深入学习和理解,可以更好地掌握 Spring Security 的工作原理及其在实际项目中的应用技巧。

    游戏论坛网站

    Spring Security可以用于论坛的安全管理,Spring MVC可以与Servlet和JSP一起构建MVC架构,Spring Data简化了数据库访问。 10. **前端技术**:除了后端的JavaEE,前端通常使用HTML、CSS和JavaScript构建用户交互界面...

    基于CST软件的三维超材料能带计算及其应用

    内容概要:本文介绍了如何利用CST软件进行三维超材料的能带计算。首先概述了三维超材料的独特性质及其广泛应用前景,接着简要介绍了CST软件的功能特点。随后详细阐述了能带计算的具体步骤,包括模型建立、材料参数设置、网格划分与求解设置以及最终的计算与结果分析。最后给出了一段Python代码示例,展示了如何处理CST输出的数据并绘制能带图。文章强调了计算机模拟技术对于深入了解超材料电子结构和物理性质的重要性。 适合人群:从事材料科学研究的专业人士,尤其是对三维超材料和电磁场模拟感兴趣的科研工作者和技术人员。 使用场景及目标:适用于希望借助CST软件开展三维超材料能带计算的研究项目,旨在提高对超材料的理解,推动相关领域的技术创新和发展。 其他说明:文中提供的Python代码仅为示例,在实际操作时可根据具体情况进行调整优化。同时,掌握CST软件的基本操作和电磁理论基础知识有助于更好地理解和应用本文内容。

    基于FPGA的永磁同步伺服系统设计:电流环及矢量控制实现

    内容概要:本文详细介绍了基于FPGA的永磁同步伺服系统的矢量控制设计,涵盖了从电流环到速度环的关键模块实现。具体包括Clarke和Park变换、PI调节器、AD7606采样、正交编码器反馈以及SVPWM生成等部分。文中提供了详细的Verilog代码片段,展示了各个模块的具体实现方法和技术细节。特别强调了定点数处理、时序设计和跨时钟域处理等方面的技术挑战及其解决方案。 适合人群:具备一定FPGA开发经验和电机控制基础知识的研发人员。 使用场景及目标:适用于希望深入了解FPGA在电机控制应用中的具体实现方式,特别是矢量控制和电流环设计的专业人士。目标是掌握FPGA平台下高效、低延迟的电机控制系统设计方法。 阅读建议:由于涉及大量具体的Verilog代码和硬件设计细节,建议读者在阅读过程中结合实际项目进行实验和调试,以便更好地理解和掌握相关技术。

    飞机大战:从运动方程到战斗系统的全链路设计解析

    经典飞机大战游戏是理解实时交互系统设计的绝佳载体。本文将深入剖析现代空战游戏的核心模块,涵盖刚体运动学、弹道轨迹优化、碰撞检测算法等关键技术,揭示二维游戏背后复杂的三维数学建模过程。

    scratch少儿编程逻辑思维游戏源码-冰塔.zip

    scratch少儿编程逻辑思维游戏源码-冰塔.zip

    scratch少儿编程逻辑思维游戏源码-弹跳(4).zip

    scratch少儿编程逻辑思维游戏源码-弹跳(4).zip

    COMSOL焊接热源模型解析:双椭球、高斯旋转体与柱状体热源的应用与优化

    内容概要:本文详细介绍了COMSOL软件中三种常见的焊接热源模型——双椭球热源、高斯旋转体热源和柱状体热源。双椭球热源适用于模拟移动热源(如激光焊、电弧焊),通过调整轴向系数a1和a2来控制热流分布;高斯旋转体热源适合小范围焊接,采用三维高斯函数描述热流密度;柱状体热源则用于深熔焊场景,特点是计算速度快。文中还提供了每种模型的具体代码实现,并强调了调试时需要注意的关键点,如时间步长、网格加密等。此外,作者分享了一些实用技巧,如将热源参数设置为全局变量并利用参数扫描功能提高调试效率。 适合人群:从事焊接工艺仿真、材料加工领域的研究人员和技术人员,以及对COMSOL建模感兴趣的工程技术人员。 使用场景及目标:帮助用户选择合适的热源模型进行焊接模拟,确保模拟结果的准确性;提供具体的代码实现和调试方法,使用户能够快速掌握并应用于实际项目中。 其他说明:文中提到的热源模型不仅限于理论介绍,还包括实际操作中的注意事项和优化建议,有助于提升模拟效果和工作效率。

    基于RBF神经网络的PID控制器在PMSM转速环中的Simulink模型设计与性能分析

    内容概要:本文介绍了将基于RBF神经网络的PID控制器应用于永磁同步电机(PMSM)转速环控制的方法及其性能优势。传统的PID控制器在面对非线性和时变系统时存在参数整定困难的问题,而引入RBF神经网络可以实现实时在线调参,提高系统的灵活性和鲁棒性。文中详细描述了Simulink模型的设计,特别是Matlab s-function模块中RBF神经网络的具体实现,包括高斯函数激活和带惯性的权值更新机制。实验结果显示,在转速突变情况下,改进后的控制器能够迅速稳定系统,超调量控制在2%以内,调节时间较传统方法缩短约40%,并且在负载变化时表现出色,无需重新整定参数。 适合人群:从事电机控制系统研究和开发的技术人员,尤其是对PID控制器优化感兴趣的工程师。 使用场景及目标:适用于需要提升PMSM转速环控制精度和响应速度的应用场合,如工业自动化设备、机器人等领域。目标是通过引入智能算法解决传统PID控制器参数整定难题,提高系统性能。 阅读建议:关注RBF神经网络与PID控制器结合的具体实现细节,特别是在Matlab s-function模块中的编码技巧以及参数调整策略。同时,注意学习率的选择和动量项的作用,这对于实际应用至关重要。

    scratch少儿编程逻辑思维游戏源码-GTA 6.zip

    scratch少儿编程逻辑思维游戏源码-GTA 6.zip

    scratch少儿编程逻辑思维游戏源码-仓鼠跑酷.zip

    scratch少儿编程逻辑思维游戏源码-仓鼠跑酷.zip

    scratch少儿编程逻辑思维游戏源码-超级麦克世界.zip

    scratch少儿编程逻辑思维游戏源码-超级麦克世界.zip

    scratch少儿编程逻辑思维游戏源码-400年.zip

    scratch少儿编程逻辑思维游戏源码-400年.zip

    少儿编程scratch项目源代码文件案例素材-气球足球.zip

    少儿编程scratch项目源代码文件案例素材-气球足球.zip

    少儿编程scratch项目源代码文件案例素材-沙漠迷城.zip

    少儿编程scratch项目源代码文件案例素材-沙漠迷城.zip

    scratch少儿编程逻辑思维游戏源码-比谁高.zip

    scratch少儿编程逻辑思维游戏源码-比谁高.zip

    少儿编程scratch项目源代码文件案例素材-乾坤大挪移.zip

    少儿编程scratch项目源代码文件案例素材-乾坤大挪移.zip

    scratch少儿编程逻辑思维游戏源码-菜鸟跳跃.zip

    scratch少儿编程逻辑思维游戏源码-菜鸟跳跃.zip

    【C++编程语言】核心特性详解、学习笔记

    内容概要:本文档详细介绍了C++语言的基础知识、高级特性及其应用。首先,文档回顾了C++对C语言的扩展,包括面向对象编程的支持、增强的语法特性(如命名空间、引用、常量处理等)。接着,深入探讨了类和对象的使用,涵盖构造函数、析构函数、拷贝构造函数、深浅拷贝等重要概念。文档还讲解了单例模式的设计与实现、C++面向对象模型的核心要素(如this指针、静态成员、友元函数)、继承与派生的关系及其实现细节、多态性的原理与应用。此外,文档详细介绍了C++的模板机制、类型转换、异常处理机制、输入输出流操作、STL(标准模板库)的容器和算法等内容。每个部分都通过具体的代码示例和解释,帮助读者理解和掌握C++的关键特性和最佳实践。 适合人群:具备一定编程基础,尤其是熟悉C语言的开发者;希望深入了解C++语言特性和面向对象编程思想的程序员;从事C++开发工作的工程师和技术爱好者。 使用场景及目标:①掌握C++语言的核心概念和高级特性;②理解并能够应用面向对象编程的基本原则和模式;③学习如何使用STL容器和算法优化代码性能;④提升C++程序的健壮性和可维护性,特别是在处理复杂数据结构和算法时;⑤掌握异常处理和类型转换的最佳实践,确保程序的稳定性和安全性。 其他说明:本文档不仅提供了理论知识,还结合了大量实例代码,便于读者边学边练。对于每一个知识点,文档都力求做到详尽解释,确保读者能够透彻理解并灵活运用。文档内容全面覆盖了C++编程的各个方面,从基础语法到高级特性,适合不同层次的学习者逐步深入学习。

    电气铁路柔性过分相技术:60°换相波形稳定分析及其实现方法

    内容概要:本文详细探讨了电气铁路柔性过分相技术中的60°换相波形稳定问题。首先介绍了柔性过分相的基本概念及其重要性,接着重点讨论了60°换相波形稳定对系统运行效率和稳定性的影响。文中还阐述了实现换相波形稳定的多种方法,如先进控制算法、实时监测设备以及滤波器的应用。最后,通过引用相关文献和实例,进一步验证了换相波形稳定的重要性和可行性。 适合人群:从事电气铁路系统设计、维护的技术人员,以及对该领域感兴趣的科研人员。 使用场景及目标:适用于希望深入了解电气铁路柔性过分相技术的专业人士,旨在帮助他们掌握60°换相波形稳定的相关理论和技术手段,提升系统性能。 其他说明:文章结合了理论分析与实际案例,提供了丰富的参考资料,有助于读者全面理解并应用于实际工作中。

Global site tag (gtag.js) - Google Analytics