这里演示的是XFire通过SOAP Header进行身份验证(handler方式)
首先是web.xml文件
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <servlet-name>XFireServlet</servlet-name> <servlet-class>org.codehaus.xfire.transport.http.XFireConfigurableServlet</servlet-class> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>XFireServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> </web-app>
然后是services.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xfire.codehaus.org/config/1.0"> <service> <name>XFireWSSecurityDemo</name> <serviceClass>com.jadyer.server.HelloService</serviceClass> <implementationClass>com.jadyer.server.HelloServiceImpl</implementationClass> <inHandlers> <handler handlerClass="com.jadyer.server.AuthenticationHandler"/> </inHandlers> </service> </beans>
接下来是核心的服务验证类
package com.jadyer.server; import org.codehaus.xfire.MessageContext; import org.codehaus.xfire.fault.XFireFault; import org.codehaus.xfire.handler.AbstractHandler; import org.jdom.Element; /** * 服务端验证类 * @see 在XFire里通过继承AbstractHandler可以向SOAP头里写东西,也可以读头的信息 * @see 其实这个AbstractHandler就像拦截器一样,当调用服务端方法时,它会做一个验证 */ public class AuthenticationHandler extends AbstractHandler { public void invoke(MessageContext context) throws Exception { // 获得HttpServletRequest对象 //HttpServletRequest request = XFireServletController.getRequest(); Element element = context.getInMessage().getHeader(); Element token = context.getInMessage().getHeader().getChild("AuthenticationToken"); if (null == element) { throw new XFireFault("您的请求必须包含验证信息", XFireFault.SENDER); } if (null == token) { throw new XFireFault("您的请求必须包含身份验证信息", XFireFault.SENDER); } String username = token.getChild("UserName").getValue(); String password = token.getChild("PassWord").getValue(); try { // 身份验证:只有admin@jadyer的用户为授权用户 if ("admin".equals(username) && "jadyer".equals(password)){ System.out.println("身份验证通过"); //该语句不会输出在客户端的控制台上 }else{ throw new Exception(); } }catch(Exception e) { throw new XFireFault("非法的用户名和密码", XFireFault.SENDER); } } }
然后是Web服务的接口和实现类
package com.jadyer.server; import java.util.List; import com.jadyer.model.User; public interface HelloService { public List<User> getUserList(String name); } /** * 暴露成Web服务的接口和实现类 */ package com.jadyer.server; import java.util.ArrayList; import java.util.List; import com.jadyer.model.User; public class HelloServiceImpl implements HelloService { public List<User> getUserList(String name) { User uu = new User(); uu.setName(name); List<User> list = new ArrayList<User>(); list.add(uu); return list; } } /** * 下面的是接口中用到的VO类 */ package com.jadyer.model; public class User { private String name; /*--getter和setter略--*/ }
最后是用于定义友好的接口参数的HelloService.aegis.xml文件
<?xml version="1.0" encoding="UTF-8"?> <mappings> <mapping> <!-- name为接口中的方法名称 --> <method name="getUserList"> <!-- index为方法中的第一个参数 --> <!-- mappedName为替换后的名称 --> <parameter index="0" mappedName="username"/> <return-type componentType="com.jadyer.model.User"/> </method> </mapping> </mappings> <!-- XFire默认的生成的WSDL文档中的接口的参数名,极不友好 <xsd:element max0ccurs="1" min0ccurs="1" name="in0" nillable="true" type="xsd:string"/> <xsd:element max0ccurs="1" min0ccurs="1" name="in1" nillable="true" type="xsd:string"/> <xsd:element max0ccurs="1" min0ccurs="1" name="in2" nillable="true" type="xsd:string"/> 这样在调用时,接口方法的参数名,就变成了in0、in1、in2等等,尽管它仍是String型,但参数名很不友好 我们的目的,就是想办法让它自动的将in0、in1替换成具体的参数名 这时可以在接口的同一包下建立一个名为<接口名>.aegis.xml文件 该文件用于定义友好的接口参数 -->
上面展示的是服务端的代码
下面展示的是客户端的代码
客户端这里采用的是Ant方式生成的,相应的Ant内容如下
<?xml version="1.0" encoding="UTF-8"?> <project name="xfireAnt" basedir="." default="createClientCode"> <property name="xfirelib" value="${basedir}/WebRoot/WEB-INF/lib"/> <property name="sources" value="${basedir}/src"/> <path id="classpath"> <fileset dir="${xfirelib}"> <include name="*.jar"/> </fileset> </path> <target name="createClientCode"> <taskdef name="wsgen" classname="org.codehaus.xfire.gen.WsGenTask" classpathref="classpath"/> <wsgen outputDirectory="${sources}" wsdl="http://127.0.0.1:8080/XFire_Security/services/XFireWSSecurityDemo?wsdl" package="com.jadyer.client" overwrite="true"/> </target> </project>
执行该build.xml文件,就会生成一系列的客户端代码
接下来,我们再自定义两个文件
一个用来为SOAP Header构造验证信息
一个用来测试
package com.jadyer.client22; import org.codehaus.xfire.MessageContext; import org.codehaus.xfire.handler.AbstractHandler; import org.jdom.Element; public class ClientAuthenticationHandler extends AbstractHandler { private String username = null; private String password = null; public ClientAuthenticationHandler() {} public ClientAuthenticationHandler(String username, String password) { this.username = username; this.password = password; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } /** * 为SOAP Header构造验证信息 * @see ------------------------------------------------- * @see 为了便于理解,我们可以把它理解成,最后会构造出一个虚拟的XML * @see <header> * @see <AuthenticationToken> * @see <UserName>SomeInfo</UserName> * @see <PassWord>SomeInfo</PassWord> * @see </AuthenticationToken> * @see </header> * @see 注意,这是个虚拟XML,是我猜测出来的,为了理解方便,未加验证 * @see ------------------------------------------------- * @see 客户端会把该虚拟XML提交到服务端,即AuthenticationHandler * @see 然后服务端解析读取XML信息,并加以判断 */ public void invoke(MessageContext context) throws Exception { Element element = new Element("header"); context.getOutMessage().setHeader(element); Element auth = new Element("AuthenticationToken"); Element username_el = new Element("UserName"); username_el.addContent(username); Element password_el = new Element("PassWord"); password_el.addContent(password); auth.addContent(username_el); auth.addContent(password_el); element.addContent(auth); } }
然后是测试的代码
package com.jadyer.client22; import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.util.List; import org.codehaus.xfire.client.Client; import org.codehaus.xfire.client.XFireProxy; import com.jadyer.client.XFireWSSecurityDemoClient; import com.jadyer.client.XFireWSSecurityDemoPortType; import com.jadyer.model.User; public class ClientUseAntFromWSDL { public static void main(String[] args) throws MalformedURLException, Exception { XFireWSSecurityDemoClient wsClient = new XFireWSSecurityDemoClient(); XFireWSSecurityDemoPortType service = wsClient.getXFireWSSecurityDemoHttpPort(); XFireProxy proxy = (XFireProxy) Proxy.getInvocationHandler(service); Client client = proxy.getClient(); client.addOutHandler(new ClientAuthenticationHandler("admin","jadyer")); List<User> list = service.getUserList("玄玉").getUser(); for(User u: list){ System.out.println(u.getName().getValue()); } } }
至此,XFire基于SOPA Header的安全验证的服务端和客户端的代码,就全部展示完毕
所以,我们来测试一下
如果客户端测试时,使用的用户名和密码是admin@jadyer,那么控制台会按照预定的,输出:玄玉
如果客户端测试时,使用的用户名和密码是jadees@jadyer,那么控制台会输出如下信息
2010-12-22 17:33:56 org.apache.commons.httpclient.HttpMethodBase writeRequest 信息: 100 (continue) read timeout. Resume sending the request 2010-12-22 17:33:56 org.apache.commons.httpclient.HttpMethodBase readResponse 信息: Discarding unexpected response: HTTP/1.1 100 Continue Exception in thread "main" org.codehaus.xfire.XFireRuntimeException: Could not invoke service.. Nested exception is org.codehaus.xfire.fault.XFireFault: 非法的用户名和密码 org.codehaus.xfire.fault.XFireFault: 非法的用户名和密码 at org.codehaus.xfire.fault.Soap11FaultSerializer.readMessage(Soap11FaultSerializer.java:31) at org.codehaus.xfire.fault.SoapFaultSerializer.readMessage(SoapFaultSerializer.java:28) at org.codehaus.xfire.soap.handler.ReadHeadersHandler.checkForFault(ReadHeadersHandler.java:111) at org.codehaus.xfire.soap.handler.ReadHeadersHandler.invoke(ReadHeadersHandler.java:67) at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131) at org.codehaus.xfire.client.Client.onReceive(Client.java:406) at org.codehaus.xfire.transport.http.HttpChannel.sendViaClient(HttpChannel.java:139) at org.codehaus.xfire.transport.http.HttpChannel.send(HttpChannel.java:48) at org.codehaus.xfire.handler.OutMessageSender.invoke(OutMessageSender.java:26) at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131) at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:79) at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:114) at org.codehaus.xfire.client.Client.invoke(Client.java:336) at org.codehaus.xfire.client.XFireProxy.handleRequest(XFireProxy.java:77) at org.codehaus.xfire.client.XFireProxy.invoke(XFireProxy.java:57) at $Proxy12.getUserList(Unknown Source) at com.jadyer.client22.ClientFromWSDL.main(ClientFromWSDL.java:23)
补充:在与Spring集成后,由于不再需要service.xml,所以还要在Spring的<bean>中添加inHandlers属性
<bean name="helloService" ......> <property name="serviceBean" ref="helloServiceBean" /> <property name="serviceClass" value="com.jadyer.server.HelloService" /> <property name="inHandlers" ref="UserAuthenticationHandler"/> </bean> <bean id="UserAuthenticationHandler" class="com.jadyer.server.AuthenticationHandler"/>