Spring BlazeDS Integration简介与入门
Adobe与Spring共同宣布将联合开发一个新项目:Spring BlazeDS Integration。其目标是:开发者可以利用Spring开发模型通过Adobe Flex、BlazeDS、Spring以及Java技术创建RIA。这样我们就可以通过BlazeDS公开Spring管理的服务而无需额外的配置文 件。其优势在于将Spring的易用性与Flex、BlazeDS以及Java整合起来以共同创建应用。
以Spring的方式开发RIA
Spring的横空出世完全颠覆了传统Java服务端的开发方式。它鼓励通过依赖注入的方式来装配POJO,这极大地简化了应用的开发与测试。
Spring的核心配置是通过Java bean实现的。借助于bean,任何Java类都能被公开成为服务。比如说,下面的配置片段就将Soda服务声明为一个Spring bean:
<!-- Implementation of soda bean--> <bean id="sodaBean" class="com.gorillalogic.sodaBank.SodaService" init-method="initSodaAccounts"> <property name="numAccounts" value="1000"/> </bean>
为了将这些bean公开成为Flex客户端所用的远程服务,Integration项目采用了Spring Web MVC。Spring Web MVC将DispatcherServlet作为一个中央分发器,用以处理任何类型的HTTP请求或是基于HTTP的远程服务。我们可以通过相同的 JavaBean配置方式来配置该DispatcherServlet以将请求转发给相应的处理器进行后续处理。
之前,BlazeDS项目会通过MessageBrokerServlet将请求路由给相应的BlazeDS Message Broker。现在借助于Spring BlazeDS,Spring Web MVC DispatcherServlet已经替代了MessageBrokerServlet,接下来就需要配置DispatcherServlet以将请求 转发给MessageBrokerHandlerAdapter。 该适配器本身是个Spring工厂bean,它会在Spring Web应用上下文中创建一个局部BlazeDS Message Broker实例,然后将Spring bean公开成为远程服务,之后Flex客户端就能够直接调用该服务了。
这种配置BlazeDS Message Broker的方式可以与Spring项目结合的更加紧密,同时还减少了将Spring bean公开成远程服务所需的配置量。比如说之前,我们需要在messaging.xml中声明一个单独的条目来公开Java服务,但现在可以轻松地在声 明Spring bean的那个配置文件中公开远程bean。
Spring BlazeDS Integration也使用了一些标准的BlazeDS XML配置文件来配置消息基础设施。这包括通道定义等一些内容。
该项目的下一版本将要增加与Spring Security的集成。最初的实现会通过一个pointcut advisor来保护BlazeDS端点。Pointcut advisor是Spring AOP支持的一部分。
建立全新的Spring BlazeDS Integration项目——服务器端
无论是建立全新的项目还是为现有的项目增加支持,步骤都是大同小异的。第一步需要将所需的jar文件增加到程序库目录中。可以通过Spring Source站点(http://www.springsource.org/spring-flex ))下载,也可以使用示例项目中的程序库。
对于这个示例来说,我们打算将一个简单的Soda Service项目修改为Spring BlazeDS项目。首先要修改web.xml文件。将该文件中所有对BlazeDS MessageBrokerServlet的引用都删掉,然后加上对Spring DispatcherServlet的引用:
<servlet> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/config/web-application-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <url-pattern>/gorilla/*</url-pattern> </servlet-mapping>
以上配置通过标准的Servlet映射模式将所有的请求路由给DispatcherServlet,同时还将上下文配置信息指定为web-application-config.xml。
标准的BlazeDS文件位于WEB-INF/flex中,其主文件为services-config.xml,其中定义了通道、日志及其他系统配置。该文件的一个变化就是标准AMF通道的URL变成通过DispatcherServlet来路由请求:
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/ gorilla/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
web-application-config.xml是主配置文件。事实上,一旦配置好了其他文件,那么在大多数情况下只需要修改该文件就行了。 在 web-application-config.xml文件中声明了MessageBrokerHandlerAdapter,这样就会将HTTP消息路 由给Spring管理的Message Broker。
<bean class="org.springframework.flex.messaging.servlet.MessageBrokerHandlerAdapter"/>
文件中声明的框架的另一部分是MessageBrokerFactoryBean。它对我们想处理的URL进行了映射,也就是发往Dispatch Servlet的所有请求:
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <value> /*=mySpringManagedMessageBroker </value> </property> </bean> <bean id="mySpringManagedMessageBroker" class="org.springframework.flex.messaging.MessageBrokerFactoryBean"/>
由于DispatcherServlet监听着/gorilla路径,因此该配置会将发送给/gorilla URL的所有请求都传给Spring管理的MessageBroker。然后由于Message Broker使用了WEB-INF/flex/services-config.xml外的通道配置,这样发送给/gorilla /messagebroker的消息就会被路由给声明为服务的那些bean了。
这样,Message Broker就与bean发生了联系,下一步配置就是声明应用中的Spring bean并将其公开成为远程服务。在该示例中,我们将SodaService声明为sodaBean:
<bean id="sodaBean" class="com.gorillalogic.sodaBank.SodaService" init-method="initSodaAccounts"> <property name="numAccounts" value="1000"/> </bean>
接下来,为BlazeDS remoting公开sodaBean:
<bean id="sodaService" class="org.springframework.flex.messaging.remoting.FlexRemotingServiceExporter"> <property name="messageBroker" ref="mySpringManagedMessageBroker"/> <property name="service" ref="sodaBean"/> </bean>
上面的配置引用了之前的Message Broker bean来公开sodaBean。默认情况下,类中所有方法都会被公开成为远程服务。FlexRemotingServiceExporter拥有众多选 项来对服务进行配置。比如说,我们可以通过includeMethods和excludeMethods来选择公开或是不公开哪些方法。
建立全新的Spring BlazeDS Integration项目——客户端
用于连接服务器的客户端代码使用了标准的Flex RemoteObjects。对于该示例应用,我们声明了如下的RemoteObject:
<mx:RemoteObject id="remoteObject" destination="sodaService" result="resultHandler(event);" fault="faultHandler(event);" channelSet="{sodaChannels}"/> <mx:ChannelSet id="sodaChannels"> <mx:AMFChannel uri="/gorilla/messagebroker/amf"/> </mx:ChannelSet>
凭借该remote对象,Flex客户端可以调用远程的Java服务器。有两种手段能让客户端知道该向哪个通道发起调用。其一是针对服务端配置文件 services-config.xml来编译客户端,通常这不是一个好办法,因为它将客户端与服务器端紧密耦合在了一起。其二是通过一个通道集将通道配 置在客户端上。
对RemoteObject的调用与本地对象调用大同小异,区别在于返回结果的过程是异步的。基于这个原因声明了一个resultHandler以在服务端的结果返回时进行回调。在本示例中,对服务器端的调用形式如下:
remoteObject.getSodaModel();
返回的结果是个ResultEvent,然后将其转换为sodaModel:
sodaModel = event.result as SodaModel;
保护远程服务——服务器端
为了保护与服务器端的通信,Spring BlazeDS Integration项目使用了一个客户化的认证和授权过程。该过程将Spring Security与BlazeDS安全过程集成起来了(注意,在本文撰写之际,该处所使用的代码仅仅存在于SVN上。同时我将示例所用代码的快照放到了 jar文件中)。
在服务器端进行安全配置的第一步就是定义安全上下文。这需要为用户定义用户名、密码以及相关角色等信息。在该简单示例中,我们仅仅将用户信息定义在文件中。真实的企业项目很可能会将这些信息放到数据库中或是使用单点登录。
我们通过一个单独的配置文件(security-context.xml)来声明系统中的用户。需要将该文件加到DispatcherServlet上下文配置中以便服务器启动时就能对其进行加载。如下配置片段展示了如何在web.xml文件中配置该文件:
<servlet> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</ servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/config/security-context.xml /WEB-INF/config/web-application-config.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
接下来,在security-context.xml文件中声明系统用户:
<authentication-provider> <user-service> <user name="ryan" password="monkey" authorities="ROLE_USER, ROLE_ADMIN" /> <user name="alex" password="chimp" authorities="ROLE_USER" /> </user-service> </authentication-provider>
第二步就是通过客户化的安全配置文件来配置Message Broker。为了将额外的服务或是安全配置到Spring管理的Message Broker上,我们需要对MessageBrokerFactoryBean增加一些额外的配置处理器。该处理器实现了两个方 法:processBeforeStartup及processAfterStartup,它们可以在Message Broker的启动前后为其设定相关的配置。
要想配置Message Broker的安全信息,我们需要设定两个处理器。其一是登录命令,它提供了认证与授权;其二是一个安全配置处理器,它保护个别的通道与URL。
LoginCommand是BlazeDS中的一个接口名,它用于定义客户化的认证与授权过程。接下 来,SpringSecurityLoginCommand bean就将Spring Security与BlazeDS security集成起来,将BlazeDS发出的进行认证与授权的调用传递给Spring管理的安全上下文。下面的代码片段声明了该bean的一个实例 并引用了之前定义的安全上下文:
<bean id="loginCommand" class="org.springframework.flex.messaging.security.SpringSecurityLoginCommand"> <constructor-arg ref="_authenticationManager"/> </bean>
第二个过程需要定义一个安全配置处理器以作为pointcut advisor,它定义了保护每个通道及URL的机制。pointcut advisor是Spring AOP的一部分,定义了在某个方法调用之前需要调用的advice。本质上,它会过滤对远程服务的调用然后阻止未授权的调用。
BlazeDS在内部通过AMF Filter来执行消息调用的预处理与后续处理。这些Filter的工作方式类似于Servlet Filter并遵循着标准的pipe-and-filter设计模式。这样,每个Filter都能阻止对消息的进一步处理。该安全过程会通知通道的AMF Filter来增加Spring管理的安全。
为了定义安全处理器,首先需要向WEB-INF/flex/services-config.xml文件中添加两个额外的通道。
<channel-definition id="my-protected-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/ gorilla/protected/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/> </channel-definition> <channel-definition id="my-protected-by-id-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/ gorilla/protected2/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/> </channel-definition>
接下来我们定义一个端点源(endpoint source),它配置了需要保护的端点或通道以及访问它们所需的角色。对于本示例来说,我们只定义一种用户角色,然后配置一个需要保护的URL及端点:
<bean id="configAttribute" class="org.springframework.security.ConfigAttributeDefinition"> <constructor-arg type="java.lang.String" value="ROLE_USER"/> </bean> <bean id="endpointSource" class="org.springframework.flex.messaging.security.EndpointDefinitionSource"> <constructor-arg> <bean class="org.springframework.security.util.AntUrlPathMatcher"/> </constructor-arg> <constructor-arg> <map> <entry> <key> <bean class="org.springframework.security.intercept.web.RequestKey"> <constructor-arg value="**/protected/ messagebroker/**"/> </bean> </key> <ref bean="configAttribute"/> </entry> </map> </constructor-arg> <constructor-arg> <map> <entry> <key> <value>my-protected-by-id-amf</value> </key> <ref bean="configAttribute"/> </entry> </map> </constructor-arg> </bean>
以上配置保护了匹配于**/protected/messagebroker/**路径的URL。在本示例中,这包括了my-protected- amf(该通道监听/gorilla/protected/messagebroker/amf)与my-protected-by-id-amf通道。
接下来,我们定义端点拦截器与异常解析器以将所有配置连接在一起:
<bean id="endpointInterceptor" class="org.springframework.flex.messaging.security.EndpointServiceMessagePointcutAdvisor"> <constructor-arg> <bean class="org.springframework.flex.messaging.security.EndpointInterceptor"> <property name="accessDecisionManager" ref="_accessManager"/> <property name="authenticationManager" ref="_authenticationManager"/> <property name="objectDefinitionSource" ref="endpointSource"/> </bean> </constructor-arg> </bean> <bean id="exceptionTranslator" class="org.springframework.flex.messaging.security.EndpointServiceMessagePointcutAdvisor"> <constructor-arg> <bean class="org.springframework.flex.messaging.security.SecurityExceptionTranslationAdvice"/> </constructor-arg> </bean>
上面的代码配置了端点拦截器以将访问与认证管理器应用到端点源上。
最后,我们修改Spring管理的Message Broker的定义来应用这些配置处理器:
<bean id="mySpringManagedMessageBroker" class="org.springframework.flex.messaging.MessageBrokerFactoryBean"> <property name="configProcessors"> <set> <ref bean="loginCommand"/> <ref bean="securityConfigProcessor"/> </set> </property> </bean>
这样,Message Broker就会通过配置在security-context.xml文件中的安全上下文来保护定义在端点拦截器中的通道。现在如果要定义服务,那还需要定义服务通信所需的通道。
<bean id="sodaService" class="org.springframework.flex.messaging.remoting.FlexRemotingServiceExporter"> <property name="messageBroker" ref="mySpringManagedMessageBroker"/> <property name="service" ref="sodaBean"/> <property name="channelIds" value="my-protected-amf, my-protected-by-id-amf"/> </bean>
对于该soda服务来说,我们已经定义好了其只能在安全的通道上进行通信。这会阻止未认证的用户(并非来自于正确的角色)对该服务的访问。
保护远程服务——客户端
安全的远程服务的客户端配置相当简单。所有的重头戏都在服务端完成了。在客户端,我们只需修改remote object定义使之包含一个安全的通道即可:
<mx:RemoteObject id="remoteObject" destination="sodaService" result="resultHandler(event);" fault="faultHandler(event);" channelSet="{sodaChannels}"/> <mx:ChannelSet id="sodaChannels"> <mx:AMFChannel uri="/gorilla/protected/messagebroker/amf"/> </mx:ChannelSet>
现在,remote object必须要经过认证方能对soda服务进行调用。比如说,如果我们没有认证,同时又调用了soda服务来获取Soda模型,那么客户端就会收到如下的错误消息:
Received fault: [RPC Fault faultString="An Authentication object was not found in theB SecurityContext" faultCode="Client.Authentication" faultDetail="null"]
我们只需将登陆信息传递给管道集就能实现对客户端的认证。如下是个超级简单的示例:
var token:AsyncToken = sodaChannels.login(username.text, password.text); token.addResponder( new AsyncResponder( function(result:ResultEvent, token:Object = null):void{ remoteObject.getSodaModel(numAccounts.text); }, function(result:FaultEvent, token:Object = null):void{ ta.text += "Received fault: " + result.fault + "\n"; } ) );
以上代码会从用户名与密码框中获取登陆信息并对用户进行认证。如果认证成功,那么就会调用远程服务并返回Soda模型。
小结
Spring BlazeDS Integration项目通过使用Spring开发模型简化了Java RIA的开发。通过与Spring Bean及Spring Security的集成,它可以轻松实现将远程服务直接公开给Flex客户端的过程。总体上来说,该项目主要面向使用Flex、BlazeDS及Java 的企业级应用开发。
Integration项目的未来版本会进一步增强与Spring的集成。计划的特性包括与Spring Security及JMS的进一步集成。同时还有一个用于定义服务端上远程服务的客户化模式定义。这将极大地简化配置文件的编写。
注意:使用BDS怎么处理用户的登录信息? 用flex端支持的 shareobject加密后保存,需要的时候再读取