安全认证框架Shiro (二)- shiro过滤器工作原理

    • 安全认证框架Shiro 二- shiro过滤器工作原理
  • 第一前言
  • 第二ShiroFilterFactoryBean入口
  • 第三请求到来解析过程
  • 第四过滤器执行原理
  • 第五总结

第一:前言

由于工作原因,写上篇文章安全认证框架Shiro (一)- ini配置文件过了好久,这里补上Shiro的后续学习经历。

第二:ShiroFilterFactoryBean入口

ShiroFilterFactoryBean实现FactoryBean,说明它是ShiroFilter的工厂类。它是怎么初始化让Shiro能很好的工作的呢,该类的入口方法是createInstance(),该方法实现了几个功能
1.创建了一个过滤器管理类FilterChainManager,该类主要管理shiro里的过滤器,里面有2个重要的属性
1.1 filters:管理全部过滤器,包括默认的关于身份验证和权限验证的过滤器,这些过滤器分为两组,一组是认证过滤器,有anon,authcBasic,auchc,user,一组是授权过滤器,有perms,roles,ssl,rest,port。同时也包含在xml里filters配置的自定义过滤器。在其它地方使用时都是从过滤器管理类里filters里拿的。且过滤器是单例的,整个Shiro框架只维护每种类型过滤器的单例。
1.2 filterChains:过滤链。它是我们重点关注的东西,是一个Map对象,其中key就是我们请求的url,value是一个NamedFilterList对象,里面存放的是与url对应的一系列过滤器。这后面会详细讲解。

2.将过滤器管理类设置到PathMatchingFilterChainResolver类里,该类负责路径和过滤器链的解析与匹配。根据url找到过滤器链。

我们以如下的xml配置为例讲解,如下:

<!-- Shiro的Web过滤器 --><bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/><property name="loginUrl" value="/login.jsp"/><property name="filters"><util:map><entry key="authc" value-ref="formAuthenticationFilter"/><entry key="logout" value-ref="logoutFilter" /></util:map></property><property name="filterChainDefinitionMap" ref="filterChainDefinitionMap" /></bean><bean id="filterChainDefinitionMap"class="com.haedrig.shiro.spring.ChainDefinitionSectionMetaSource"><!-- 默认的连接配置 --><property name="filterChainDefinitions"><value>/login.jsp = anon/login = authc/logout = logout/authenticated = authc/views/**=anon/** = authc, perms</value></property></bean>

createInstance()源码如下:

 protected AbstractShiroFilter createInstance() throws Exception {log.debug("Creating Shiro Filter instance.");SecurityManager securityManager = getSecurityManager();if (securityManager == null) {String msg = "SecurityManager property must be set.";throw new BeanInitializationException(msg);}if (!(securityManager instanceof WebSecurityManager)) {String msg = "The security manager does not implement the WebSecurityManager interface.";throw new BeanInitializationException(msg);}//创建默认的过滤器管理器FilterChainManager manager = createFilterChainManager();//Expose the constructed FilterChainManager by first wrapping it in a// FilterChainResolver implementation. The AbstractShiroFilter implementations// do not know about FilterChainManagers - only resolvers:PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();//将过滤器管理器设置到PathMatchingFilterChainResolver对象里。chainResolver.setFilterChainManager(manager);//Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built//FilterChainResolver.  It doesn't matter that the instance is an anonymous inner class//here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts//injection of the SecurityManager and FilterChainResolver:return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);}

该方法创建过滤器管理器,同时设置到PathMatchingFilterChainResolver对象里,当有请求过来里,shiro会通过PathMatchingFilterChainResolver解析得到请求的url对应的过滤器链。

createFilterChainManager()方法:

 protected FilterChainManager createFilterChainManager() {DefaultFilterChainManager manager = new DefaultFilterChainManager();//获得shiro默认的过滤器,同时将xml里配置的loginUrl,successUrl,unauthorizedUrl设置到不同过滤器里Map<String, Filter> defaultFilters = manager.getFilters();//apply global settings if necessary:for (Filter filter : defaultFilters.values()) {applyGlobalPropertiesIfNecessary(filter);}//将自定义过滤器添加到filters里让管理器管理,注意,filters是Map,所以key同名的会被覆盖。//Apply the acquired and/or configured filters:Map<String, Filter> filters = getFilters();if (!CollectionUtils.isEmpty(filters)) {for (Map.Entry<String, Filter> entry : filters.entrySet()) {String name = entry.getKey();Filter filter = entry.getValue();applyGlobalPropertiesIfNecessary(filter);if (filter instanceof Nameable) {((Nameable) filter).setName(name);}//'init' argument is false, since Spring-configured filters should be initialized//in Spring (i.e. 'init-method=blah') or implement InitializingBean:manager.addFilter(name, filter, false);}}//build up the chains://获得xml里filterChainDefinitions配置Map<String, String> chains = getFilterChainDefinitionMap();if (!CollectionUtils.isEmpty(chains)) {for (Map.Entry<String, String> entry : chains.entrySet()) {String url = entry.getKey();String chainDefinition = entry.getValue();//根据url创建url对应的过滤链**重点**重点**重点,每个配置过的url都对应一个过滤链manager.createChain(url, chainDefinition);}}return manager;}

讲解:
1.applyGlobalPropertiesIfNecessary(filter);方法是将配置的loginUrl,successUrl,unauthorizedUrl设置到不同过器里。其中loginUrl赋值到所以继承自AccessControlFilter的过滤器里,successUrl赋值到所以继承自AuthenticationFilter的过滤器里,unauthorizedUrl赋值到所以继承自AuthorizationFilter的过滤器里。
当然其实过滤器可以自己设置loginUrl,successUrl,unauthorizedUrl,自定义赋值的也覆盖全局指定的。

2.读取filters配置的自定义过滤器,将它们纳入到过滤器管理器里。

3.最后读取filterChainDefinitions配置,根据配置设置每个url对应的过滤链,filterChains保存这些配置,它是一个Map集合,key就是url,value就是过滤器组成的NamedFilterList集合。其实当请求过来时,解析出请求路径,会从filterChains里找到url对应的过滤链,按过滤器的策略一个一个执行下去。
同时会调用PathMatchingFilter的processPathConfig()方法做些赋值操作。下面会专门讲将从PathMatchingFilter开始工作的过程。

4.一步一步分析下来过滤器管理器里的过滤链filterChains如下:

{/login.jsp=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@6ed97422,/login=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@592dbd8,/logout=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@1141badb,/authenticated=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@2d1b876e,/views/**=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@31106774,/**=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@14cd7d10,/index.jsp=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@712384a,/admin.jsp=org.apache.shiro.web.filter.mgt.SimpleNamedFilterList@7643ef31
}

可以看到每个url对应一个SimpleNamedFilterList对象,SimpleNamedFilterList是个List子类对象,保存的是过滤器集。

第三:请求到来解析过程

我们知道DelegatingFilterProxy过滤器的代理类来实现拦截的,任何请求都会先经过shiro先过滤,直到成功才会执行javaweb本身的过滤器。
一个请求过来时,先到达AbstractShiroFilter.executeChain()方法,去根据request解析出来的url找到对应的过滤链,然后执行过滤器链。
executeChain()方法如下:

 protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)throws IOException, ServletException {//得到过滤器链FilterChain chain = getExecutionChain(request, response, origChain);chain.doFilter(request, response);}

进入getExecutionChain()方法:

 protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {FilterChain chain = origChain;//resolver即是前面说的PathMatchingFilterChainResolver对象。里面保存有过滤器管理器实例FilterChainResolver resolver = getFilterChainResolver();if (resolver == null) {log.debug("No FilterChainResolver configured.  Returning original FilterChain.");return origChain;}//进入PathMatchingFilterChainResolver对象里,根据解析出来的requestURI找到对应的过滤器链并返回FilterChain resolved = resolver.getChain(request, response, origChain);if (resolved != null) {log.trace("Resolved a configured FilterChain for the current request.");chain = resolved;} else {log.trace("No FilterChain configured for the current request.  Using the default.");}return chain;}

最终返回的过滤器链是:

而backingList里数据如下:

我们们在xml里配置的是一样的。

/login = authc

再往下一步一步走,中间过程省略看图

直到FormAuthenticationFilter.doFilterInternal()方法

public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)throws ServletException, IOException {Exception exception = null;try {boolean continueChain = preHandle(request, response);if (log.isTraceEnabled()) {log.trace("Invoked preHandle method.  Continuing chain?: [" + continueChain + "]");}if (continueChain) {executeChain(request, response, chain);}postHandle(request, response);if (log.isTraceEnabled()) {log.trace("Successfully invoked postHandle method");}} catch (Exception e) {exception = e;} finally {cleanup(request, response, exception);}}

这行代码boolean continueChain = preHandle(request, response);其实是真正直到的单个过滤器里了,要想知道这里面在干什么,需要往下继续看。

第四:过滤器执行原理

先从PathMatchingFilter类讲解开始,匹配路径的过滤器,因为所以的过滤器都会继承PathMatchingFilter类,它的作用是路径匹配。过滤器单独维护自己需要过滤的url。
整体看一下类的属性:

分别介绍属性详情:
appliedPaths:是个map类,存放需要过滤的URL的。
比如如下的配置:

会将这红框中的属性添加到appliedPaths集合里。
结果如下:

pathMatcher:匹配器,就是当请求过来时,匹配哪个url对应哪个过滤器的。
processPathConfig:解析xml里配置的url对应的过滤器,分别加到appliedPaths要应用的map集合里,由于每个过滤器都继承PathMatchingFilter,故每个过滤器都会经过这步操作。
比如如下图,刚启动web服务时过滤器管理器会解析xml里filterChainDefinitions配置的过滤链,在根据配置给每个url创建过滤器链时,会调用不同过滤器的processPathConfig方法让过滤器自己把url添加到appliedPaths集合里,因为过滤器是单例的,过滤器管理器的filters里也只维护过滤器的单个实例:

preHandle方法:请求过来时该方法匹配url路径是否是该过滤器要处理的。遍历appliedPaths里所有的url直到完全匹配成功或遍历完为止。如果匹配成功则表明该url请求是需要该过滤器处理。然后就会进入onPreHandle方法。
onPreHandle方法:该方法在路径匹配成功时决定对url是否需要身份验证。默认是返回true,意思是不需要验证的。但子类需要根据业务逻辑自己重写该方法。
看看对该方法的实现类有哪些。

其中重点看看AccessControlFilter和AnonymousFilter。一个是需要身份验证的,一个是匿名访问的。
AnonymousFilter就比较简单了,任何对象访问都一直返回true,表明任何用AnonymousFilter过滤的请求都不需要验证。因为它一直返回true。

AccessControlFilter是需要身份验证的过滤器。当请求在过滤器里匹配成功后。后续验证处理在这里。如图:

如果isAccessAllowed()验证成功则返回true,否则交由onAccessDenied()方法处理,最后将onAccessDenied()方法处理的结果返回。
isAccessAllowed()方法是决定了当前请求的subject是否允许访问。如果以前做过验证则返回true,否则返回false。
onAccessDenied()方法是在被拒绝访问时处理。AccessControlFilter类有很多子类重载了该方法。以FormAuthenticationFilter类的onAccessDenied()方法为例。

如果是登陆请求,则执行登陆操作,否则保存请求链接跳转到登陆请求界面。
executeLogin()方法就比较简单了。创建 token,获得Subject对象,然后执行login()最后到realm里查询数据库做比较,这里先不讲。
saveRequestAndRedirectToLogin()方法会执行WebUtils.saveRequest()将请求保存到session里

redirectToLogin()即是服务器跳转到登陆界面。
流程如下:


2017-11-16补


有必要再补充PathMatchingFilter里属性appliedPaths和过滤器链的关系:
如下图:

每个过滤器自己在appliedPaths集合维护自己需要处理的url集合,而url对应的过滤器链可能串连多个,它只要求对应的过滤器有自己的url即可,不管过滤器是否还要处理其它url.

第五:总结

入口在ShiroFilterFactoryBean,每个过滤器都会根据类型不同拥有loginUrl,successUrl,unauthorizedUrl中的一个或多个配置,过滤器管理器负责为配置的每个url创建过滤器链,对于没配置的url则对应到“/**”路径的过滤器链上。当请求过来时,ShiroFilterFactoryBean负责接收请求并让过滤器管理器通过一定的策略找到url对应的过滤器链执行过滤器链,这里有个特殊情况,登陆请求,如果是登陆请求且是POST方式提交的话,如果没登陆会去执行登陆操作。

未完这里,未完待续…..

安全认证框架Shiro (二)- shiro过滤器工作原理相关推荐

  1. 全自动过滤器:全自动过滤器工作原理

    全自动过滤器工作原理: 水由入口进入,首先经过粗滤网滤掉较大颗粒的杂质,然后到达细滤网.在过滤过程中,细滤网逐渐累积水中的脏物.杂质,形成过滤杂质层,由于杂质层堆积在细滤网的内侧,因此在细滤网的内.外 ...

  2. android camera(二):摄像头工作原理、s5PV310 摄像头接口(CAMIF)

    关键词: android  camera CMM 模组 camera参数  CAMIF 平台信息: 内核: linux 系统: android 平台:S5PV310(samsung exynos 42 ...

  3. 全自动过滤器:全自动叠片过滤器工作原理及应用范围

    在一些发达国家,全自动叠片过滤器的使用已经相当普遍,因其能够在十分严苛的条件下精确稳定工作且不需大量维护工作而被用于市政供水.自来水厂.废水处理.化工企业以及其它应急情况过滤等.该过滤器采用模块化设计 ...

  4. 自洁式排气水过滤器工作原理

    自洁式排气水过滤器产品介绍: LJG系列自洁式排气水过滤器为多系统部件,具有压差自动报警.气水分离以及过滤功能.过滤器自洁能力强,不用经常清洗过滤网.对水系统中的氧化铁皮.老垢.污泥和空气等杂质进行排 ...

  5. 全自动刷式过滤器工作原理

    全自动刷式过滤器工作原理: 全自动刷式过滤器是一种利用滤网直接拦截水中的杂质,去除水体悬浮物.颗粒物,降低浊度,净化水质,减少系统污垢.菌藻.锈蚀等产生,以净化水质及保护系统其他设备正常工作的精密设备 ...

  6. Filter过滤器工作原理

    Filter过滤器工作原理 Filter简介 Filter也称之为过滤器,它是Servlet技术中最激动人心的技术之一,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Js ...

  7. 由浅入深了解Thrift(二)——Thrift工作原理

    相关示例代码见:http://download.csdn.net/detail/hjx_1000/8374829 三.  Thrift的工作原理 1.普通的本地函数调用过程 例如,有如下关于本地函数的 ...

  8. mysql布隆过滤器源码_通过实例解析布隆过滤器工作原理及实例

    布隆过滤器 布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 "一定不存在或者可能存在& ...

  9. 布隆过滤算法c语言,通过实例解析布隆过滤器工作原理及实例

    布隆过滤器 布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 "一定不存在或者可能存在& ...

最新文章

  1. Bootstrap:关于bootstrap单页面中多Modal的问题
  2. 目标检测--Rich feature hierarchies for accurate object detection and semantic segmentation(CVPR 2014)
  3. 一些通过SAP ABAP代码审查得出的ABAP编程代码优化建议
  4. 深入理解和应用Float属性
  5. Android 常见错误
  6. Linux下VsFTP和ProFTP用户管理高级技巧 之一
  7. Bailian2687 数组逆序重放【逆序处理】
  8. 想进入互联网公司,怎能不知道互联网的技术架构!
  9. 【肌电信号】基于matlab GUI肌电信号处理【含Matlab源码 966期】
  10. 串口IEC103协议
  11. ISD9160学习笔记08_结项总结
  12. 华三交换机dhcp 保留地址
  13. 在Cfree5里使用多行快捷注释的方法
  14. 电脑如何控制点击android手机,安卓手机怎么控制电脑?红米手机远程控制操作电脑方法...
  15. Find a Mother Vertex in a Graph
  16. Life feelings--13--青春不毕业,那些心里念念叨叨难以忘怀的记忆
  17. Flink报错:exceeded checkpoint tolerable failure threshould
  18. 初始Ext.net(二)
  19. 如何修改、缩小截图图片大小,压缩图片。
  20. Leo的怯懦,由3000块钱而起的故事(3)大结局

热门文章

  1. 大数据量的五种处理方式
  2. Windows C盘清理方法
  3. Android简单计时器
  4. java 转成数字,java汉字转成数字
  5. Crosswalk 运行报错问题
  6. Unity 使用陀螺仪判断手机方向
  7. 关于mysql数据库三范式
  8. bash shell 数组元素遍历操作 foreach
  9. 高等流体力学复习02
  10. 带你简化理解Spring 基于注解配置的原理