详解spring security filter的工作原理
这篇文章介绍filter的工作原理。配置方式为xml。
Filter如何进入执行逻辑的
初始配置:
springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /*
DelegatingFilterProxy这个类继承了GenericFilterBean,GenericFilterBean实现了Filter接口。
这个配置是一切的开始,配置完这个之后,在启动项目的时候会执行Filterd的初始化方法:
@Override publicfinalvoidinit(FilterConfigfilterConfig)throwsServletException{ Assert.notNull(filterConfig,"FilterConfigmustnotbenull"); if(logger.isDebugEnabled()){ logger.debug("Initializingfilter'"+filterConfig.getFilterName()+"'"); } this.filterConfig=filterConfig; //Setbeanpropertiesfrominitparameters. PropertyValuespvs=newFilterConfigPropertyValues(filterConfig,this.requiredProperties); if(!pvs.isEmpty()){ try{ BeanWrapperbw=PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoaderresourceLoader=newServletContextResourceLoader(filterConfig.getServletContext()); Environmentenv=this.environment; if(env==null){ env=newStandardServletEnvironment(); } bw.registerCustomEditor(Resource.class,newResourceEditor(resourceLoader,env)); initBeanWrapper(bw); bw.setPropertyValues(pvs,true); } catch(BeansExceptionex){ Stringmsg="Failedtosetbeanpropertiesonfilter'"+ filterConfig.getFilterName()+"':"+ex.getMessage(); logger.error(msg,ex); thrownewNestedServletException(msg,ex); } } //Letsubclassesdowhateverinitializationtheylike. initFilterBean();//这个方法 if(logger.isDebugEnabled()){ logger.debug("Filter'"+filterConfig.getFilterName()+"'configuredsuccessfully"); } }
在初始化方法中,会执行初始化Filter的方法initFilterBean。这个方法的实现在DelegatingFilterProxy中:
protectedvoidinitFilterBean()throwsServletException{ synchronized(this.delegateMonitor){ if(this.delegate==null){ //Ifnotargetbeannamespecified,usefiltername. if(this.targetBeanName==null){ this.targetBeanName=getFilterName(); } //FetchSpringrootapplicationcontextandinitializethedelegateearly, //ifpossible.Iftherootapplicationcontextwillbestartedafterthis //filterproxy,we'llhavetoresorttolazyinitialization. WebApplicationContextwac=findWebApplicationContext(); if(wac!=null){ this.delegate=initDelegate(wac);//这个方法 } } } }
在这个初始化方法中又调用initDelegate方法进行初始化:
protectedFilterinitDelegate(WebApplicationContextwac)throwsServletException{ StringtargetBeanName=getTargetBeanName(); Assert.state(targetBeanName!=null,"Notargetbeannameset"); Filterdelegate=wac.getBean(targetBeanName,Filter.class); if(isTargetFilterLifecycle()){ delegate.init(getFilterConfig()); } returndelegate; }
在这个方法中,先获取targetBeanName,这个名字是构造方法中赋值的:
publicDelegatingFilterProxy(StringtargetBeanName,@NullableWebApplicationContextwac){ Assert.hasText(targetBeanName,"TargetFilterbeannamemustnotbenullorempty"); this.setTargetBeanName(targetBeanName); this.webApplicationContext=wac; if(wac!=null){ this.setEnvironment(wac.getEnvironment()); } }
这个名字就是web.xml中配置的名字springSecurityFilterChain:
springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain是固定不能改的,如果改了启动时就会报错,这是spring启动时内置的一个bean,这个bean实际是FilterChainProxy。
这样一个Filter就初始化话好了,过滤器chain也初始化好了。
当一个请求进来的时候,会进入FilterChainProxy执行doFilter方法:
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse, FilterChainchain)throwsIOException,ServletException{ booleanclearContext=request.getAttribute(FILTER_APPLIED)==null; if(clearContext){ try{ request.setAttribute(FILTER_APPLIED,Boolean.TRUE); doFilterInternal(request,response,chain); } finally{ SecurityContextHolder.clearContext(); request.removeAttribute(FILTER_APPLIED); } } else{ doFilterInternal(request,response,chain); } }
先获取所有的Filter,然后执行doFilterInternal方法:
privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse, FilterChainchain)throwsIOException,ServletException{ FirewalledRequestfwRequest=firewall .getFirewalledRequest((HttpServletRequest)request); HttpServletResponsefwResponse=firewall .getFirewalledResponse((HttpServletResponse)response); Listfilters=getFilters(fwRequest); if(filters==null||filters.size()==0){ if(logger.isDebugEnabled()){ logger.debug(UrlUtils.buildRequestUrl(fwRequest) +(filters==null?"hasnomatchingfilters" :"hasanemptyfilterlist")); } fwRequest.reset(); chain.doFilter(fwRequest,fwResponse); return; } //最终执行下面的这些代码 VirtualFilterChainvfc=newVirtualFilterChain(fwRequest,chain,filters); vfc.doFilter(fwRequest,fwResponse); }
VirtualFilterChain是一个匿名内部类:
privatestaticclassVirtualFilterChainimplementsFilterChain{ privatefinalFilterChainoriginalChain; privatefinalListadditionalFilters; privatefinalFirewalledRequestfirewalledRequest; privatefinalintsize; privateintcurrentPosition=0; privateVirtualFilterChain(FirewalledRequestfirewalledRequest, FilterChainchain,List additionalFilters){ this.originalChain=chain; this.additionalFilters=additionalFilters; this.size=additionalFilters.size(); this.firewalledRequest=firewalledRequest; } @Override publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse) throwsIOException,ServletException{ if(currentPosition==size){ if(logger.isDebugEnabled()){ logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) +"reachedendofadditionalfilterchain;proceedingwithoriginalchain"); } //Deactivatepathstrippingasweexitthesecurityfilterchain this.firewalledRequest.reset(); originalChain.doFilter(request,response); } else{ currentPosition++; FilternextFilter=additionalFilters.get(currentPosition-1); if(logger.isDebugEnabled()){ logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) +"atposition"+currentPosition+"of"+size +"inadditionalfilterchain;firingFilter:'" +nextFilter.getClass().getSimpleName()+"'"); } nextFilter.doFilter(request,response,this); } } }
filter集合执行的逻辑在VirtualFilterChain的doFilter方法中。
filter是如何执行的
上面说了怎么才能进入filter的执行逻辑,下面说一下filter到底怎么执行,为什么一个
在VirtualFilterChain的doFilter方法可以执行所有的filter。
下面写一个例子,模拟filter的执行逻辑。
定义FilterChain接口、Filter接口:
publicinterfaceFilter{ voiddoFilter(Stringusername,intage,FilterChainfilterChain); }
publicinterfaceFilterChain{ voiddoFilter(Stringusername,intage); }
定义两个Filter实现:
publicclassNameFilterimplementsFilter{ @Override publicvoiddoFilter(Stringusername,intage,FilterChainfilterChain){ username=username+1; System.out.println("username:"+username+"age:"+age); System.out.println("正在执行:NameFilter"); filterChain.doFilter(username,age); } }
publicclassAgeFilterimplementsFilter{ @Override publicvoiddoFilter(Stringusername,intage,FilterChainfilterChain){ age+=10; System.out.println("username:"+username+"age:"+age); System.out.println("正在执行:AgeFilter"); filterChain.doFilter(username,age); } }
定义一个FilterChain实现:
publicclassFilterChainProxyimplementsFilterChain{ privateintposition=0; privateintsize=0; privateListfilterList=newArrayList<>(); publicvoidaddFilter(Filterfilter){ filterList.add(filter); size++; } @Override publicvoiddoFilter(Stringusername,intage){ if(size==position){ System.out.println("过滤器链执行结束"); }else{ Filterfilter=filterList.get(position); position++; filter.doFilter(username,age,this); } } }
测试Filter实现:
publicclassFilterTest{ publicstaticvoidmain(String[]args){ FilterChainProxyproxy=newFilterChainProxy(); proxy.addFilter(newNameFilter()); proxy.addFilter(newAgeFilter()); proxy.doFilter("张三",0); } } ======= username:张三1age:0 正在执行:NameFilter username:张三1age:10 正在执行:AgeFilter 过滤器链执行结束
在这个执行逻辑中,最重要的是【this】,this就是初始化的好的FilterChain实例,在这个测试实例中,this就是FilterChainProxy。
执行FilterChainProxy的doFilter方法的时候,传入了初始参数username和age,进入这个方法后,根据position取出相应的Filter,初次进入position是0,执行Filter的doFilter方法,注意,此时Filter的doFilter方法额外传入了一个this参数,这个参数就是初始化的好的FilterChain实例,在Filter中的doFilter的方法中最后又会执行FilterChain的doFilter方法,相当于第二次调用FilterChain实例的doFilter方法,此时posotion是1,然后再执行Filter的doFilter方法,直到所有的Filter执行完,整个执行过程结束。
VirtualFilterChain的doFilter方法的执行逻辑和这个测试实例中的执行逻辑基本一致。
这样就完成了整个过滤器链的执行。
总结
以前用Filter的时候就非常疑惑过滤器怎么执行的,直到今天才算解决了这个疑惑。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。