示例用的是spring+CXF来配置webservice
首先是web.xml配置,将spring,CXF的配置文件加载进去,由于我客户端跟服务端在同一个工程里面,所以配置文件在一块。
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/classes/spring-cxf.xml,<!--服务端用到的配置--> /WEB-INF/classes/spring-cxf-client.xml<!--客户端用到的配置文件--> </param-value> </context-param>
<servlet> <servlet-name>cxf-jeeek</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>cxf-jeeek</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping>
一、服务端
<1>CXF的配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:/META-INF/cxf/cxf.xml" /> <import resource="classpath:/META-INF/cxf/cxf-servlet.xml" /> <import resource="classpath:/META-INF/cxf/cxf-extension-soap.xml" /> <!--配置拦截器,用来验证Token信息--> <bean id="exampleServiceInterceptor" class="com.ekservice.interceptor.ExampleServiceInterceptor"> </bean> <bean id="exampleServiceImpl" class="com.ekservice.impl.ExampleServiceImpl" /> <jaxws:endpoint implementor="#exampleServiceImpl" address="/ExampleService"> <!-- --> <jaxws:inInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" /> <!-- 这个地方不知道为什么,官方文档说CXF2.0.x需要加这个过滤器,但是我用的是CXF2.7.7不加SAAJInInterceptor过滤器报错 --> <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" /> <bean id="WSS4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken"></entry> <entry key="passwordType" value="PasswordText"></entry> <!-- 设置密码类型为加密 <entry key="passwordType" value="PasswordDigest" />--> <entry key="passwordCallbackClass" value="com.ekservice.interceptor.ExampleServiceInterceptor"></entry> </map> </constructor-arg> </bean> </jaxws:inInterceptors> <jaxws:outInterceptors> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> </jaxws:outInterceptors> </jaxws:endpoint> </beans>
<2>拦截器ExampleServiceInterceptor.java的代码,有详细注释
package com.ekservice.interceptor; import org.apache.log4j.Logger; import org.apache.ws.security.WSPasswordCallback; import org.apache.ws.security.WSSecurityException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @author EK * @version 创建时间:2015-5-14 下午04:13:47 * 类说明 */ public class ExampleServiceInterceptor implements CallbackHandler{ Logger logger = Logger.getLogger(ExampleServiceInterceptor.class); private Map<String, String> passwords = new HashMap<String, String>(); public ExampleServiceInterceptor() { passwords.put("admin", "password"); } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback pc = (WSPasswordCallback) callbacks[i]; String identifier = pc.getIdentifier(); int usage = pc.getUsage(); logger.info("Client Token [username: " + identifier+", password: "+ passwords.get(identifier) + "] Exist user: "+passwords.containsKey(identifier)); if (usage == WSPasswordCallback.USERNAME_TOKEN) {// 密钥方式USERNAME_TOKEN if (!passwords.containsKey(identifier)) { try { throw new WSSecurityException("User not match - "+identifier); } catch (WSSecurityException e) { e.printStackTrace(); } } // username token pwd... // ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同 // 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime // Changes片段 pc.setPassword(passwords.get(identifier));// ▲【这里非常重要】▲ // ▲PS 如果和客户端不同将抛出org.apache.ws.security.WSSecurityException: // The // security token could not be authenticated or // authorized异常,服务端会认为客户端为非法调用 }else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE if (!passwords.containsKey(identifier)) { try { throw new WSSecurityException("User not match - "+identifier); } catch (WSSecurityException e) { e.printStackTrace(); } } // set the password for client's keystore.keyPassword // ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同; // 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime // Changes片段 pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲ // ▲PS:如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:The // security token could not be authenticated or // authorized异常,服务端会认为客户端为非法调用 } } } }
<3>接口及其实现类的代码,比较简单,只是做个示例
package com.ekservice.service; import com.ek.entry.user.User; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; /** * @author EK * @version 创建时间:2015-5-14 下午02:17:59 * 类说明 */ @WebService(targetNamespace="http://jeeek-dp/", name="ExampleService") public interface ExampleService { @WebResult(name="password") @WebMethod(action="http://jeeek-dp/ExampleService/getStr") public User getStr(@WebParam(name="name")String userName); @WebMethod(action="http://jeeek-dp/ExampleService/receive") public void receive(); @WebResult(name="tokenId") @WebMethod(action="http://jeeek-dp/ExampleService/getTokenId") public String getTokenId(); @WebResult(name="sendUnitInfo") @WebMethod(action="http://jeeek-dp/ExampleService/getSendUnitInfo") public int getSendUnitInfo(@WebParam(name="orgAccountId")long orgAccountId, @WebParam(name="recOrgType")int recOrgType, @WebParam(name="recOrgId")long recOrgId); }
package com.ekservice.impl; import com.ek.entry.user.User; import com.ekservice.service.ExampleService; import org.apache.log4j.Logger; import javax.jws.WebService; /** * @author EK * @version 创建时间:2015-5-14 下午02:30:35 * 类说明 */ @WebService(targetNamespace="http://jeeek-dp/", serviceName="ExampleService", name="ExampleService") public class ExampleServiceImpl implements ExampleService { Logger logger = Logger.getLogger(ExampleServiceImpl.class); public User getStr(String userName) { User user = new User(); user.setAddress("北京市海淀区222号"); user.setMobilePhone("010-1101111"); System.out.println(userName+" "+user.getAddress()); return user; } @Override public void receive() { this.logger.info("Call reveive() method...."); } @Override public String getTokenId() { this.logger.info("Call getTokenId() method...."); return "success"; } @Override public int getSendUnitInfo(long orgAccountId, int recOrgType, long recOrgId) { this.logger.info("Call getSendUnitInfo() method...."+orgAccountId+", "+recOrgType+", "+recOrgId); return 0; } }
二、客户端
<1>CXF配置文件,基本类似于服务端配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <bean id="exampleServiceClientInterceptor" class="com.ek.client.interceptor.ExampleServiceClientInterceptor"></bean> <bean id="WSS4JOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordText" /> <entry key="user" value="admin" /> <!-- <entry key="mustUnderstand" value="false"></entry> --> <entry key="passwordCallbackRef"> <ref bean="exampleServiceClientInterceptor" /> </entry> </map> </constructor-arg> </bean> <jaxws:client id="exampleService" address="http://localhost:8080/jeeek-dp/services/ExampleService" serviceClass="com.ek.client.services.example.ExampleService"> <jaxws:outInterceptors> <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor"></bean> <ref bean="WSS4JOutInterceptor"/> </jaxws:outInterceptors> </jaxws:client> <bean id="eperpServiceClientInterceptor" class="com.ek.client.interceptor.CPERPServiceClientInterceptor"></bean> <bean id="cperp" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordText" /> <entry key="user" value="cpweb" /> <entry key="passwordCallbackRef"> <ref bean="eperpServiceClientInterceptor" /> </entry> </map> </constructor-arg> </bean> </beans>
<2>客户端拦截器代码
package com.ek.client.interceptor; import org.apache.log4j.Logger; import org.apache.ws.security.WSPasswordCallback; import org.apache.ws.security.WSSecurityException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @author EK * @version 创建时间:2015年5月15日 上午11:36:20 * 类说明 */ public class ExampleServiceClientInterceptor implements CallbackHandler{ Logger logger = Logger.getLogger(ExampleServiceClientInterceptor.class); private Map<String, String> passwords = new HashMap<String, String>(); public ExampleServiceClientInterceptor() { passwords.put("admin", "password"); } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback pc = (WSPasswordCallback) callbacks[i]; String identifier = pc.getIdentifier(); int usage = pc.getUsage(); if (usage == WSPasswordCallback.USERNAME_TOKEN) {// 密钥方式USERNAME_TOKEN logger.info("Client Token [username: " + identifier+", password: "+ passwords.get(identifier) + "] Exist user= "+passwords.containsKey(identifier)); if (!passwords.containsKey(identifier)) { try { throw new WSSecurityException("User not match - "+identifier); } catch (WSSecurityException e) { e.printStackTrace(); } } pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲ }else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE if (!passwords.containsKey(identifier)) { try { throw new WSSecurityException("User not match - "+identifier); } catch (WSSecurityException e) { e.printStackTrace(); } } pc.setPassword(passwords.get(identifier));// //▲【这里非常重要】▲ } } } }
<3>客户端接口代码
package com.ek.client.services.example; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.ws.RequestWrapper; import javax.xml.ws.ResponseWrapper; /** * */ @WebService(targetNamespace = "http://jeeek-dp/", name = "ExampleService") public interface ExampleService { @WebResult(name = "password", targetNamespace = "") @RequestWrapper(localName = "getStr", targetNamespace = "http://jeeek-dp/") @WebMethod(action = "http://jeeek-dp/ExampleService/getStr") @ResponseWrapper(localName = "getStrResponse", targetNamespace = "http://jeeek-dp/") public User getStr( @WebParam(name = "name", targetNamespace = "") java.lang.String name ); @RequestWrapper(localName = "receive", targetNamespace = "http://jeeek-dp/") @WebMethod(action = "http://jeeek-dp/ExampleService/receive") public void receive(); @WebResult(name = "tokenId", targetNamespace = "") @RequestWrapper(localName = "getTokenId", targetNamespace = "http://jeeek-dp/") @WebMethod(action = "http://jeeek-dp/ExampleService/getTokenId") @ResponseWrapper(localName = "getTokenIdResponse", targetNamespace = "http://jeeek-dp/") public java.lang.String getTokenId(); @WebResult(name = "sendUnitInfo", targetNamespace = "") @RequestWrapper(localName = "getSendUnitInfo", targetNamespace = "http://jeeek-dp/") @WebMethod(action = "http://jeeek-dp/ExampleService/getSendUnitInfo") @ResponseWrapper(localName = "getSendUnitInfoResponse", targetNamespace = "http://jeeek-dp/") public java.lang.Integer getSendUnitInfo( @WebParam(name = "orgAccountId", targetNamespace = "") java.lang.Long orgAccountId, @WebParam(name = "recOrgType", targetNamespace = "") java.lang.Integer recOrgType, @WebParam(name = "recOrgId", targetNamespace = "") java.lang.Long recOrgId ); }
<4>客户端调用类
package com.ek.client.call; import com.ek.client.interceptor.ExampleServiceClientInterceptor; import com.ek.client.services.example.ExampleService; import com.ek.client.services.example.User; import org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor; import org.apache.ws.security.WSConstants; import org.apache.ws.security.handler.WSHandlerConstants; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author EK * @version 创建时间:2015年5月15日 上午11:05:06 * 类说明 */ public class ExampleServiceClient { public static void main(String[] args) { // ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cxf-client.xml"); // ExampleService es = (ExampleService) ctx.getBean("exampleService"); Map<String, Object> map = new HashMap<String, Object>(); map.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN); map.put(WSHandlerConstants.USER, "admin"); map.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT); map.put(WSHandlerConstants.PW_CALLBACK_CLASS, ExampleServiceClientInterceptor.class.getName()); List list = new ArrayList(); list.add(new SAAJOutInterceptor()); list.add(new WSS4JOutInterceptor(map)); JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(ExampleService.class); factory.setAddress("http://localhost:8080/jeeek-dp/services/ExampleService"); factory.getOutInterceptors().addAll(list); ExampleService es = (ExampleService) factory.create(); User u = es.getStr("ssss"); //Integer sui = es.getSendUnitInfo(1L, 2, 3L); String tokenId = es.getTokenId(); //es.receive(); System.out.println("的, " + tokenId); //System.out.println("的" + sui); } }