下载

1下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 Spring技术内幕:深入解析Spring架构与设计原理

Spring技术内幕:深入解析Spring架构与设计原理.doc

Spring技术内幕:深入解析Spring架构与设计原理

只想做你世界里的唯一无止境
2019-05-23 0人阅读 举报 0 0 暂无简介

简介:本文档为《Spring技术内幕:深入解析Spring架构与设计原理doc》,可适用于IT/计算机领域

Spring技术内幕深入解析Spring架构与设计原理(一)引子缘起已经很久没有写帖子了,现在总算是有点时间写些东西,也算是对自己的一个记录吧。刚刚完成了一个软件产品,从概念到运营都弄了一下,正在推广当中,虽然还没有能够达到盈亏平衡,但是这个过程,对自己也算是一种历练。先不管结果如何,好呆走过这么一遭了。我打算用这个帖子,把自己在这个过程中的一些心得,特别是对Spring新的理解,记录下来。使用这个帖子的标题,持续下来。简单来说,自己的软件产品是一个基于互联网的SaaS协同软件平台,操作简单,支持流程定义,管理和多种客户端像短信,MSN,智能手机什么的(我这里就不多做什么广告了),也有一个企业版的版本,使用的技术框架是HibernateSpringWicket,下面是Linux和MySQL,还有云计算的平台的使用,以支持其扩展性,虽然现在还没有可扩展性的需求,但似乎不难从SaaS上,就会想到云计算,其实,它们真的是天生的一对!关于云计算,自己对这个技术很感兴趣,觉得和开源软件的结合,是很有意思的,因为它们都有基于服务的基因,在云计算平台的使用上,也有一些初步的实践。云计算是一个很有意思的话题,但在这里主要是想谈Spring,所以对云计算,这里就先不多说了,但非常欢迎有兴趣的朋友和一起另外找地方讨论!回到正题,在我自己的产品中,其中除了Wicket和云计算外,其他都是大家非常熟知的了,像Hibernate,Spring,MySQL什么的。在这个过程中,发现自己对一些技术点也有了新的认识,最有体会的是Spring。当然,在这个过程中,更大的收获是对产品开发整个过程的认识,在这点上,真是一言难尽回到自己还算了解的Spring,这次我使用的是的代码,所以,有机会也把这些代码读了几遍,比原来的理解要加深了许多,也发现了不少和代码不同的地方,以及自己一些对Spring的新的理解,这些,就让我就用这个帖子系列,给自己总结一下,也算是对自己以前的那个代码分析的帖子做一个新的交代吧。自己对Spring一点小小的见解简化Java企业应用的开发,是Spring框架的目标就是我们熟知的当年的那个interface,也亦非吴下阿蒙了,由它演进出来的Spring,以及由它带来的崭新开发理念,也早已伴随着这个开源框架的广泛应用,而飞入寻常百姓家。与此同时,伴随着Spring的成熟,开源社区的成ory)Configurethebeandefinitionreaderwiththiscontext'sresourceloadingenvironment这里设置ResourceLoader,因为XmlWebApplicationContext是DefaultResource的子类,所以这里同样会使用DefaultResourceLoader来定位BeanDefinitionbeanDefinitionReadersetResourceLoader(this)beanDefinitionReadersetEntityResolver(newResourceEntityResolver(this))Allowasubclasstoprovidecustominitializationofthereader,thenproceedwithactuallyloadingthebeandefinitionsinitBeanDefinitionReader(beanDefinitionReader)这里使用定义好的XmlBeanDefinitionReader来载入BeanDefinitionloadBeanDefinitions(beanDefinitionReader)}protectedvoidinitBeanDefinitionReader(XmlBeanDefinitionReaderbeanDefinitionReader){}如果有多个BeanDefinition的文件定义,需要逐个载入,都是通过reader来完成的,这个初始化过程是由refreshBeanFactory方法来完成的,这里只是负责载入BeanDefinitionprotectedvoidloadBeanDefinitions(XmlBeanDefinitionReaderreader)throwsBeansException,IOException{StringconfigLocations=getConfigLocations()if(configLocations!=){for(StringconfigLocation:configLocations){readerloadBeanDefinitions(configLocation)}进入DispatcherServlet和MVC实现完成了在Web环境中,IoC容器的建立以后,也就是在完成对ContextLoaderListener的初始化以后,Web容器开始初始化DispatcherServlet,接着,会执行DispatcherServlet持有的IoC容器的初始化过程,在这个初始化过程中,一个新的上下文被建立起来,这个DispatcherServlet持有的上下文,被设置为根上下文的子上下文。可以大致认为,根上下文是和Web应用相对应的一个上下文,而DispatcherServlet持有的上下文是和Servlet对应的一个上下文,在一个Web应用中,往往可以容纳多个Servlet存在与此相对应,对于应用在Web容器中的上下体系,也是很类似的,一个根上下文可以作为许多Servlet上下文的双亲上下文。在DispatcherServlet,我们可以看到对MVC的初始化,是在DispatcherServlet的initStrategies完成的。在这个初始化完成以后,会在上下文中建立器一个执行器于url的对应关系,这个对应关系可以让在url请求到来的时候,MVC可以检索到相应的控制器来进行处理,如以下代码所示:Java代码protectedObjectgetHandlerInternal(HttpServletRequestrequest)throwsException{这里从request中得到请求的url路径StringlookupPath=thisurlPathHelpergetLookupPathForRequest(request)这里使用得到的url路径对Handler进行匹配,得到对应的Handler,如果没有对应的Hanlder,返回,这样默认的Handler会被使用Objecthandler=lookupHandler(lookupPath,request)if(handler==){Weneedtocareforthedefaulthandlerdirectly,sinceweneedtoexposethePATHWITHINHANDLERMAPPINGATTRIBUTEforitaswellObjectrawHandler=if(""equals(lookupPath)){rawHandler=getRootHandler()}if(rawHandler==){rawHandler=getDefaultHandler()}if(rawHandler!=){validateHandler(rawHandler,request)handler=buildPathExposingHandler(rawHandler,lookupPath,)}}if(handler!=loggerisDebugEnabled()){loggerdebug("Mapping"lookupPath"tohandler'"handler"'")}elseif(handler==loggerisTraceEnabled()){loggertrace("Nohandlermappingfoundfor"lookupPath"")}returnhandler}lookupHandler是根据url路径,启动在handlerMap中对handler的检索,并最终返回handler对象protectedObjectlookupHandler(StringurlPath,HttpServletRequestrequest)throwsException{DirectmatchObjecthandler=thishandlerMapget(urlPath)if(handler!=){validateHandler(handler,request)returnbuildPathExposingHandler(handler,urlPath,)}PatternmatchStringbestPathMatch=for(StringregisteredPath:thishandlerMapkeySet()){if(getPathMatcher()match(registeredPath,urlPath)(bestPathMatch==||bestPathMatchlength()<registeredPathlength())){bestPathMatch=registeredPath}}if(bestPathMatch!=){handler=thishandlerMapget(bestPathMatch)validateHandler(handler,request)StringpathWithinMapping=getPathMatcher()extractPathWithinPattern(bestPathMatch,urlPath)Map<String,String>uriTemplateVariables=getPathMatcher()extractUriTemplateVariables(bestPathMatch,urlPath)returnbuildPathExposingHandler(handler,pathWithinMapping,uriTemplateVariables)}Nohandlerfoundreturn}最后,我们可以结合在DispatcherServlet中,对请求的分发处理来了解一个url请求到来时,MVC的实现和协同处理过程,如以下代码所示:Java代码protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{HttpServletRequestprocessedRequest=requestHandlerExecutionChainmappedHandler=intinterceptorIndex=这里为视图准备好一个ModelAndView,这个ModelAndView持有handler处理请求的结果try{ModelAndViewmv=booleanerrorView=falsetry{processedRequest=checkMultipart(request)Determinehandlerforthecurrentrequest根据请求得到对应的handler,hander的注册以及getHandler的实现在前面已经分析过mappedHandler=getHandler(processedRequest,false)if(mappedHandler==||mappedHandlergetHandler()==){noHandlerFound(processedRequest,response)return}ApplypreHandlemethodsofregisteredinterceptors调用hander的拦截器,从HandlerExecutionChain中取出Interceptor进行前处理HandlerInterceptorinterceptors=mappedHandlergetInterceptors()if(interceptors!=){for(inti=i<interceptorslengthi){HandlerInterceptorinterceptor=interceptorsiif(!interceptorpreHandle(processedRequest,response,mappedHandlergetHandler())){triggerAfterCompletion(mappedHandler,interceptorIndex,processedRequest,response,)return}interceptorIndex=i}}Actuallyinvokethehandler这里是实际调用handler的地方,在执行handler之前,用HandlerAdapter先检查一下handler的合法性:是不是按Spring的要求编写的handlerhandler处理的结果封装到ModelAndView对象,为视图提供展现数据HandlerAdapterha=getHandlerAdapter(mappedHandlergetHandler())这里通过调用HandleAdapter的handle方法,实际上触发对Controller的handleRequest方法的调用mv=hahandle(processedRequest,response,mappedHandlergetHandler())Doweneedviewnametranslationif(mv!=!mvhasView()){mvsetViewName(getDefaultViewName(request))}ApplypostHandlemethodsofregisteredinterceptorsif(interceptors!=){for(inti=interceptorslengthi>=i){HandlerInterceptorinterceptor=interceptorsiinterceptorpostHandle(processedRequest,response,mappedHandlergetHandler(),mv)}}}catch(ModelAndViewDefiningExceptionex){loggerdebug("ModelAndViewDefiningExceptionencountered",ex)mv=exgetModelAndView()}catch(Exceptionex){Objecthandler=(mappedHandler!=mappedHandlergetHandler():)mv=processHandlerException(processedRequest,response,handler,ex)errorView=(mv!=)}Didthehandlerreturnaviewtorender这里使用视图对ModelAndView数据的展现if(mv!=!mvwasCleared()){render(mv,processedRequest,response)if(errorView){WebUtilsclearErrorRequestAttributes(request)}}else{if(loggerisDebugEnabled()){loggerdebug("ModelAndViewreturnedtoDispatcherServletwithname'"getServletName()"':assumingHandlerAdaptercompletedrequesthandling")}}TriggeraftercompletionforsuccessfuloutcometriggerAfterCompletion(mappedHandler,interceptorIndex,processedRequest,response,)}catch(Exceptionex){TriggeraftercompletionforthrownexceptiontriggerAfterCompletion(mappedHandler,interceptorIndex,processedRequest,response,ex)throwex}catch(Errorerr){ServletExceptionex=newNestedServletException("Handlerprocessingfailed",err)TriggeraftercompletionforthrownexceptiontriggerAfterCompletion(mappedHandler,interceptorIndex,processedRequest,response,ex)throwex}finally{Cleanupanyresourcesusedbyamultipartrequestif(processedRequest!=request){cleanupMultipart(processedRequest)}}}通过MVC框架,实际上是DispatcherServlet的协调运作,得到了ModelAndView对象作为数据处理结果,最后,DispatcherServlet把获得的模型数据交给特定的视图对象,从而完成这些数据的视图呈现工作,这个视图呈现由视图对象的render方法来完成,毫无疑问,对应于不同的视图对象,render方法会完成不同的视图呈现处理,从而为用户提供丰富的WebUI表现。关于这些不同的视图展现,还可以看到很多很有参考意义的开源软件的灵活使用,限于篇幅,这里就不详细说了。对SpringMVC框架的个人理解对Spring作为应用平台的Web应用开发而言,Spring为它们提供了SpringMVC框架,作为一个像struts这样的Web框架的替代当然,作为应用平台,Spring并不会强制应用对Web框架的选择。但对Web应用开发而言,选择直接使用SpringMVC,可以给应用开发带来许多便利。因为SpringMVC,毫无疑问,很好的提供了与Web环境中的IoC容器的集成。同时,和其他Web应用一样,使用SpringMVC,应用只需要专注于处理逻辑和视图呈现的开发(当然这些开发需要符合SpringMVC的开发习惯),在视图呈现部分,SpringMVC同时也集成了许多现有的WebUI实现,比如像Excel,PDF这些文档视图的生成,因为,集成第三方解决方案,实在可以说是Spring的拿手好戏,从这种一致性的开发模式上看,它在很大程度上降低了Web应用开发的门槛。深入解析Spring架构与设计原理(五)Spring与远端调用在应用开发中,常常涉及服务器系统中各种不同进程之间的通信与计算交互,远端调用(RMI)是实现这种计算场景的一种有效方式。此外,还存在着另一种情况,在这种应用场景中,与那些典型的基于HTML的BS应用不同,客户端程序需要完成对服务器端应用的直接调用,这也是需要远端调用大显身手的场合。Spring中提供了轻量级的远端调用模块,从而为我们在上面提到的应用场景开发,提供平台支持。根据Spring的既定策略,它依然只是起到一个集成平台的作用,而并不期望在实现方案上,与已有的远端调用方案形成竞争。也就是说,在Spring远端调用架构中,具体的通信协议设计、通信实现,以及在服务器和客户端对远端调用的处理封装,Spring没有将其作为实现重点,在这个技术点上,并不需要重新发明轮子。对Spring来说,它所要完成的工作,是在已有远端调用技术实现的基础上,通过IoC与AOP的封装,让应用更方便地使用这些远端调用服务,并能够更方便灵活地与现有应用系统实现集成。通过Spring封装以后,应用使用远端过程调用非常方便,既不需要改变原来系统的相关实现接口,也不需要为远端调用功能增加新的封装负担。因此,这种使用方式,在某种程度上,可以称为轻量级的远端调用方案。在实现远端调用的过程中,往往需要涉及客户端和服务器端的相关设置,这些设置通过Spring的IoC容器就可以很好的完成,这是我们已经很熟悉的IoC容器的强项了。同时,Spring为远端调用的实现,提供了许多不同的方案,玲琅满目,任君选择。如RMI、HTTP调用器、第三方远端调用库HessianBurlap、基于JavaRMI的解决方案,等等。Spring对不同的远端调用的实现封装,基本上,都采用了类似的模式来完成,比如在客户端,都是通过相关的ProxyFactoryBean和ClientInterceptor来完成的,在服务器端是通过ServiceExporter来导出远端的服务对象的。有了这些统一的命名规则,应用配置和使用远端调用会非常方便,同时,通过对这些Spring远端调用基础设施实现原理的分析,还可以看到一些常用处理方法的技术实现,比如对代理对象的使用、拦截器的使用、通过afterPropertiesSet来启动远端调用基础设施的建立,等等,这些都是在Spring中常用的技术。HTTP调用器客户端的实现在HtttpInvokerProxyFactory中,设置了serviceProxy对象作为远端服务的本地代理对象同时,在依赖注入完成以后,通过afterPropertiesSet来对远端调用完成设置。Java代码publicclassHttpInvokerProxyFactoryBeanextendsHttpInvokerClientInterceptorimplementsFactoryBean<Object>{这是远端对象的代理privateObjectserviceProxyOverride在注入完成之后,设置远端对象代理publicvoidafterPropertiesSet(){superafterPropertiesSet()需要配置远端调用的接口if(getServiceInterface()==){thrownewIllegalArgumentException("Property'serviceInterface'isrequired")}这里使用ProxyFactory来生成远端代理对象,注意这个this,因为HttpInvokerProxyFactoryBean的基类是HttpInvokerClientInterceptor,所以代理类的拦截器被设置为HttpInvokerClientInterceptorthisserviceProxy=newProxyFactory(getServiceInterface(),this)getProxy(getBeanClassLoader())}FactoryBean生产对象的入口。返回的是serviceProxy对象,这是一个代理对象publicObjectgetObject(){returnthisserviceProxy}publicClass<>getObjectType(){returngetServiceInterface()}publicbooleanisSingleton(){returntrue}可以看到,为这个代理对象配置了一个拦截器HttpInvokerClientInterceptor,在这个拦截器中,拦截了对代理对象的方法调用。如以下代码所示:Java代码对代理对象的方法调用入口publicObjectinvoke(MethodInvocationmethodInvocation)throwsThrowable{if(AopUtilsisToStringMethod(methodInvocationgetMethod())){return"HTTPinvokerproxyforserviceURL"getServiceUrl()""}创建RemoteInvocation对象,这个对象封装了对远端的调用,这些远端调用通过序列化的机制完成RemoteInvocationinvocation=createRemoteInvocation(methodInvocation)RemoteInvocationResultresult=try{这里是对远端调用的入口result=executeRequest(invocation,methodInvocation)}catch(Throwableex){throwconvertHttpInvokerAccessException(ex)}try{返回远端调用的结果returnrecreateRemoteInvocationResult(result)}catch(Throwableex){if(resulthasInvocationTargetException()){throwex}else{thrownewRemoteInvocationFailureException("Invocationofmethod"methodInvocationgetMethod()"failedinHTTPinvokerremoteserviceat"getServiceUrl()"",ex)}}}远端调用的具体实现过程,是由executeRequest来完成的,也就是在SimpleHttpInvokerRequestExecutor的实现中,封装了整个HTTP调用器客户端实现的基本过程,如下所示:Java代码这是HTTP调用器实现的基本过程,通过HTTP的request和reponse来完成通信,在通信的过程中传输的数据是序列化的对象protectedRemoteInvocationResultdoExecuteRequest(HttpInvokerClientConfigurationconfig,ByteArrayOutputStreambaos)throwsIOException,ClassNotFoundException{打开一个标准JSEHttpURLConnectionHttpURLConnectioncon=openConnection(config)prepareConnection(con,baossize())远端调用封装成RemoteInvocation对象,这个对象通过序列化被写到对应的HttpURLConnection中去writeRequestBody(config,con,baos)这里取得远端服务返回的结果,然后把结果转换成RemoteInvocationResult返回validateResponse(config,con)InputStreamresponseBody=readResponseBody(config,con)returnreadRemoteInvocationResult(responseBody,configgetCodebaseUrl())}把序列化对象输出到HttpURLConnection去protectedvoidwriteRequestBody(HttpInvokerClientConfigurationconfig,HttpURLConnectioncon,ByteArrayOutputStreambaos)throwsIOException{baoswriteTo(congetOutputStream())}为使用HttpURLConnection完成对象序列化,需要进行一系列的配置比如配置请求方式为post,请求属性等等protectedvoidprepareConnection(HttpURLConnectioncon,intcontentLength)throwsIOException{consetDoOutput(true)consetRequestMethod(HTTPMETHODPOST)consetRequestProperty(HTTPHEADERCONTENTTYPE,getContentType())consetRequestProperty(HTTPHEADERCONTENTLENGTH,IntegertoString(contentLength))LocaleContextlocale=LocaleContextHoldergetLocaleContext()if(locale!=){consetRequestProperty(HTTPHEADERACCEPTLANGUAGE,StringUtilstoLanguageTag(localegetLocale()))}if(isAcceptGzipEncoding()){consetRequestProperty(HTTPHEADERACCEPTENCODING,ENCODINGGZIP)}}获得HTTP响应的IO流protectedInputStreamreadResponseBody(HttpInvokerClientConfigurationconfig,HttpURLConnectioncon)throwsIOException{如果是通过gzip压缩,那么需要先解压if(isGzipResponse(con)){GZIPresponsefoundneedtounzipreturnnewGZIPInputStream(congetInputStream())}else{Plainresponsefound正常的HTTP响应输出returncongetInputStream()}}HTTP调用器服务器端的实现在服务器端使用SpringHTTP远端调用,需要配置HttpInvokerServiceExporter,作为远端服务的服务导出器,来接收HTTP服务请求。在通过HTTP请求,得到客户端传过来的RemoteInvocation对象以后,就可以进行服务方法的调用了。服务调用需要的基本信息,都封装在RemoteInvocation对象中。这个服务调用过程,是由invokeAndCreateResult方法来实现的,如RemoteInvocationSerializingExporter的invoke实现所示:Java代码protectedObjectinvoke(RemoteInvocationinvocation,ObjecttargetObject)throwsNoSuchMethodException,IllegalAccessException,InvocationTargetException{if(loggerisTraceEnabled()){loggertrace("Executing"invocation)}try{调用RemoteInvocationExecutor,这个执行器是DefaultRemoteInvocationExecutorreturngetRemoteInvocationExecutor()invoke(invocation,targetObject)}catch(NoSuchMethodExceptionex){if(loggerisDebugEnabled()){loggerwarn("Couldnotfindtargetmethodfor"invocation,ex)}throwex}catch(IllegalAccessExceptionex){if(loggerisDebugEnabled()){loggerwarn("Couldnotaccesstargetmethodfor"invocation,ex)}throwex}catch(InvocationTargetExceptionex){if(loggerisDebugEnabled()){loggerdebug("Targetmethodfailedfor"invocation,exgetTargetException())}throwex}}看到的invoke方法封装了服务器端调用的主体,这个invoke方法在HttpInvokerServiceExporter的基类RemoteInvocationSerializingExporter中实现,服务对象的方法调用完成之后,会把调用结果,通过HTTP响应和对象序列化,传给HTTP调用器客户端,从而完成整个HTTP调用器的远端调用过程,如以下代码所示:Java代码protectedvoidwriteRemoteInvocationResult(HttpServletRequestrequest,HttpServletResponseresponse,RemoteInvocationResultresult)throwsIOException{设置Response的ContentType属性,设置为applicationxjavaserializedobjectresponsesetContentType(getContentType())writeRemoteInvocationResult(request,response,result,responsegetOutputStream())}输出到HTTP的Response,然后把Response关闭protectedvoidwriteRemoteInvocationResult(HttpServletRequestrequest,HttpServletResponseresponse,RemoteInvocationResultresult,OutputStreamos)throwsIOException{ObjectOutputStreamoos=createObjectOutputStream(decorateOutputStream(request,response,os))try{doWriteRemoteInvocationResult(result,oos)oosflush()}finally{oosclose()}}经过这一系列的处理过程,服务执行结果对象又回到了HTTP的远端调用客户端。在客户端从HTTP响应读取对象之后,它把这个看起来像是在本地实现,其实是由远端服务对象完成的调用结果,交给发起远端调用的客户端调用方法,从而最终完成整个远端调用的过程。这个过程很有特点,它使用了HTTP的请求和响应作为通信通道,在这个通信通道里面,并没有再做进一步的附加的通信协议的封装,而且,在这个处理过程中,使用的都是Java和Spring框架已有的特性,比如,通过IoC的配置,以及代理对象拦截器的封装处理,再加Java的序列化和反序列化,以及在服务器端的SpringMVC框架的使用,通过这些已有的技术实现,让使用者感觉,它的实现风格非常的简洁轻快,整个代码实现,阅读起来,也让人感到非常的赏心悦目。深入解析Spring架构与设计原理(六)SpringACEGISpringACEGI作为Spring丰富生态系统中的一个非常典型的应用,安全框架SpringACEGI的使用是非常普遍的。尽管它不属于Spring平台的范围,但由于它建立在Spring的基础上,因此可以方便地与Spring应用集成,从而方便的为基于Spring的应用提供安全服务。作为一个完整的JavaEE安全应用解决方案,ACEGI能够为基于Spring构建的应用项目,提供全面的安全服务,它可以处理应用需要的各种典型的安全需求例如,用户的身份验证、用户授权,等等。ACEGI因为其优秀的实现,而被Spring开发团队推荐作为Spring应用的通用安全框架,随着Spring的广泛传播而被广泛应用。在各种有关Spring的书籍,文档和应用项目中,都可以看到它活跃的身影。SpringACEGI的基本实现关于ACEGI的基本设置,在这里就不多啰嗦了。我们关心的是ACEGI是怎样实现用户的安全需求的,比如最基本的用户验证,授权的工作原理和实现。在ACEGI配置中,是通过AuthenticationProcessingFilter的过滤功能来启动Web页面的用户验证实现的。AuthenticationProcessingFilter过滤器的基类是AbstractProcessingFilter,在这个AbstractProcessingFilter的实现中,可以看到验证过程的实现模板,在这个实现模板中,可以看到它定义了实现验证的基本过程,如以下代码所示:Java代码publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{检验是不是符合ServletRequestSevletResponse的要求if(!(requestinstanceofHttpServletRequest)){thrownewServletException("CanonlyprocessHttpServletRequest")}if(!(responseinstanceofHttpServletResponse)){thrownewServletException("CanonlyprocessHttpServletResponse")}HttpServletRequesthttpRequest=(HttpServletRequest)requestHttpServletResponsehttpResponse=(HttpServletResponse)responseif(requiresAuthentication(httpRequest,httpResponse)){if(loggerisDebugEnabled()){loggerdebug("Requestistoprocessauthentication")}这里定义ACEGI中的Authentication对象,从而通过这个Authentication对象,来持有用户验证信息AuthenticationauthResulttry{onPreAuthentication(httpRequest,httpResponse)具体验证过程委托给子类完成,比如通过AuthenticationProcessingFilter来完成基于Web页面的用户验证authResult=attemptAuthentication(httpRequest)}catch(AuthenticationExceptionfailed){AuthenticationfailedunsuccessfulAuthentication(httpRequest,httpResponse,failed)return}Authenticationsuccessif(continueChainBeforeSuccessfulAuthentication){chaindoFilter(request,response)}验证工作完成后的后续工作,跳转到相应的页面,跳转的页面路径已经做好了配置successfulAuthentication(httpRequest,httpResponse,authResult)return}chaindoFilter(request,response)}在看到上面的对WEB页面请求的拦截后,处理开始转到ACEGI框架中后台了,我们看到,完成验证工作的主要类在ACEGI中是AuthenticationManager。如以下代码所示:Java代码publicfinalAuthenticationauthenticate(AuthenticationauthRequest)throwsAuthenticationException{try{*doAuthentication是一个抽象方法,由具体的AuthenticationManager实现,从而完成验证工作。传入的参数是一个Authentication对象,在这个对象中已经封装了从HttpServletRequest中得到的用户名和密码,这些信息都是在页面登录时用户输入的*AuthenticationauthResult=doAuthentication(authRequest)copyDetails(authRequest,authResult)returnauthResult}catch(AuthenticationExceptione){esetAuthentication(authRequest)throwe}}***CopiestheauthenticationdetailsfromasourceAuthenticationobjecttoadestinationone,providedthe*latterdoesnotalreadyhaveoneset*privatevoidcopyDetails(Authenticationsource,Authenticationdest){if((destinstanceofAbstractAuthenticationToken)(destgetDetails()==)){AbstractAuthenticationTokentoken=(AbstractAuthenticationToken)desttokensetDetails(sourcegetDetails())}}protectedabstractAuthenticationdoAuthentication(Authenticationauthentication)throwsAuthenticationException而读取用户信息的操作,我们举大家已经很熟悉的DaoAuthenticationProvider作为例子。可以看到,在配置的JdbcDaoImpl中,定义了读取用户数据的操作,如以下代码所示:Java代码publicstaticfinalStringDEFUSERSBYUSERNAMEQUERY="SELECTusername,password,enabledFROMusersWHEREusername="publicstaticfinalStringDEFAUTHORITIESBYUSERNAMEQUERY="SELECTusername,authorityFROMauthoritiesWHEREusername="publicUserDetailsloadUserByUsername(Stringusername)throwsUsernameNotFoundException,DataAccessException{使用SpringJDBCSqlMappingQuery来完成用户信息的查询Listusers=usersByUsernameMappingexecute(username)根据输入的用户名,没有查询到相应的用户信息if(userssize()==){thrownewUsernameNotFoundException("Usernotfound")}如果查询到一个用户列表,使用列表中的第一个作为查询得到的用户UserDetailsuser=(UserDetails)usersget()containsnoGrantedAuthority使用SpringJDBCSqlMappingQuery来完成用户权限信息的查询ListdbAuths=authoritiesByUsernameMappingexecute(usergetUsername())addCustomAuthorities(usergetUsername(),dbAuths)if(dbAuthssize()==){thrownewUsernameNotFoundException("UserhasnoGrantedAuthority")}GrantedAuthorityarrayAuths=(GrantedAuthority)dbAuthstoArray(newGrantedAuthoritydbAuthssize())StringreturnUsername=usergetUsername()if(!usernameBasedPrimaryKey){returnUsername=username}根据查询的用户信息和权限信息,构造User对象返回returnnewUser(returnUsername,usergetPassword(),userisEnabled(),true,true,true,arrayAuths)}ACEGI授权器的实现ACEGI就像一位称职的,负责安全保卫工作的警卫,在它的工作中,不但要对来访人员的身份进行检查(通过口令识别身份),还可以根据识别出来的身份,赋予其不同权限的钥匙,从而可以去打开不同的门禁,得到不同级别的服务。从这点上看,与在这个场景中的“警卫”人员承担的角色一样,ACEGI在Spring应用系统中,起到的也是类似的保卫系统安全的作用,而验证和授权,就分别对应于警卫识别来访者身份和为其赋予权限的过程。为用户授权是由AccessDecisionManager授权器来完成的,授权的过程,在授权器的decide方法中实现,这个decide方法是AccessDecisionManger定义的一个接口方法,通过这个接口方法,可以对应好几个具体的授权器实现,对于授权器完成决策的规则实现,在这里,我们以AffirmativeBased授权器为例,看看在AffirmativeBased授权器中,实现的一票决定授权规则是怎样完成的,这个实现过程,如以下代码所示:Java代码publicvoiddecide(Authenticationauthentication,Objectobject,ConfigAttributeDefinitionconfig)throwsAccessDeniedException{取得配置投票器的迭代器,可以用来遍历所有的投票器Iteratoriter=thisgetDecisionVoters()iterator()intdeny=while(iterhasNext()){取得当前投票器的投票结果AccessDecisionVotervoter=(AccessDecisionVoter)iternext()intresult=votervote(authentication,object,config)对投票结果进行处理,如果是遇到ACCESSGRANT的结果,授权直接通过否则,累计ACCESSDENIED的投票票数switch(result){caseAccessDecisionVoterACCESSGRANTED:returncaseAccessDecisionVoterACCESSDENIED:denybreakdefault:break}}如果有反对票,那么拒绝授权if(deny>){thrownewAccessDeniedException(messagesgetMessage("AbstractAccessDecisionManageraccessDenied","Accessisdenied"))}这里对弃权票进行处理,看看是全是弃权票的决定情况,默认是不通过,这种处理情况,是由allowIfAllAbstainDecisions变量来控制的Togetthisfar,everyAccessDecisionVoterabstainedcheckAllowIfAllAbstainDecisions()}可以看到,在ACEGI的框架实现中,应用的安全需求管理,主要是由过滤器、验证器、用户数据提供器、授权器、投票器,这几个基本模块的协作一起完成的。这几个基本模块的关系,刻画出了ACEGI内部架构的基本情况,也是我们基于ACEGI实现Spring安全应用,需要重点关注的地方

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/97

Spring技术内幕:深入解析Spring架构与设计原理

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利