参照上一篇博文 基于HTTPS的webservice 的环境搭建step by step 完成所需环境之后,就可以开始接下来的著名项目HelloWorld了。
1. 建立一个helloworld的web项目
2. 创建SEI
package com.harvey.services; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; @WebService(targetNamespace="http://webservice.harvey.com") public interface HelloWorldInf { public String sayHello(@WebParam(name="name")String name); }
通过@WebService注解将该接口声明为webservice
3. 创建SIB
package com.harvey.services; import javax.jws.WebService; @WebService(endpointInterface="com.harvey.services.HelloWorldInf", targetNamespace="http://webservice.harvey.com") public class HelloWorldImpl implements HelloWorldInf { public String sayHello(String name) { return "hello,"+name; } }
这个类功能虽然很强大,但是应该比较容易看懂,就是向调用者打招呼。
4. 建立鉴权的handler
package com.harvey.services; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; public class ServerPasswordCallback implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; String pw = pc.getPassword(); String idf = pc.getIdentifier(); if (!"harvey".equals(idf) || !"123456".equals(pw)) { throw new SecurityException("鉴权失败!"); } } }
5. 通过spring整合
通过spring发布helloworld服务并为服务添加鉴权的handler.
在web.xml的配置如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/applicationContext.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
为了方便直接在src目录下建立applicationContext.xml,内容如下:
<?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:aop="http://www.springframework.org/schema/aop" xmlns:cxf="http://cxf.apache.org/core" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <bean id="hello" class="com.harvey.services.HelloWorldImpl" /> <jaxws:endpoint id="helloworldservice" endpointName="e:helloWorldPointName" serviceName="s:helloworldservice" implementor="#hello" address="/helloworld" xmlns:e="http://webservice.harvey.com/endponit" xmlns:s="http://webservice.harvey.com/service"> <jaxws:inInterceptors> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordText" /> <entry key="user" value="cxfServer" /> <entry key="passwordCallbackRef"> <ref bean="serverPasswordCallback" /> </entry> </map> </constructor-arg> </bean> </jaxws:inInterceptors> </jaxws:endpoint> <bean id="serverPasswordCallback" class="com.harvey.services.ServerPasswordCallback" /> </beans>
Server端的工作就这些了,结合我们在上一篇文章中提到的数字证书的环境,一个支持数字证书和鉴权的server端搭建完成了。
接下来我们在转到client端,看一下如何在client加入对数字证书以及鉴权的支持。
6. 创建client端的密码的handler
为了方便起见,我们不再为client独立创建功能,与service在同一项目下建立client端的回调类,为webservice的请求添加鉴权的头信息
import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; public class ClientPasswordCallback implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; pc.setPassword("123456"); pc.setIdentifier("Harvey"); } }
7. 创建client的spring配置文件
<?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:aop="http://www.springframework.org/schema/aop" xmlns:cxf="http://cxf.apache.org/core" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:http="http://cxf.apache.org/transports/http/configuration" xmlns:sec="http://cxf.apache.org/configuration/security" xsi:schemaLocation=" http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="client" class="com.harvey.services.HelloWorldInf" factory-bean="clientFactory" factory-method="create" /> <bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"> <property name="serviceClass" value="com.harvey.services.HelloWorldInf" /> <property name="address" value="https://localhost:8443/WebServiceDemo/services/helloworld" /> <property name="outInterceptors"> <list> <ref bean="wss4jOut" /> </list> </property> </bean> <bean id="wss4jOut" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="user" value="cxfClient" /> <entry key="passwordType" value="PasswordText" /> <entry key="passwordCallbackClass" value="com.harvey.services.ClientPasswordCallback" /> </map> </constructor-arg> </bean> </beans>
8. 创建client端的测试类,并将上一篇 博文中生成的client.jks和truststore.jks添加到类目录下
package com.harvey.test; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.transport.http.HTTPConduit; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.harvey.services.HelloWorldInf; public class HelloWorldClient { private final static String trustStore = "truststore.jks"; private final static String keyStore = "client.jks"; private final static String trustStorePass = "123456"; private final static String keyStorePass = "999999"; /** * @param args */ public static void main(String[] args) throws Exception{ ApplicationContext context=new ClassPathXmlApplicationContext("client-bean.xml"); HelloWorldInf client = (HelloWorldInf)context.getBean("client"); Client proxy = ClientProxy.getClient(client); HTTPConduit conduit = (HTTPConduit) proxy.getConduit(); TLSClientParameters tlsParams = conduit.getTlsClientParameters(); if (tlsParams == null) { tlsParams = new TLSClientParameters(); } tlsParams.setSecureSocketProtocol("SSL"); tlsParams.setKeyManagers(getKeyManagers()); tlsParams.setTrustManagers(getTrustManagers()); conduit.setTlsClientParameters(tlsParams); String response = client.sayHello("han"); System.out.println(response); } private static TrustManager[] getTrustManagers() throws IOException { try { String alg = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory factory = TrustManagerFactory.getInstance(alg); InputStream fp = HelloWorldClient.class.getResourceAsStream(trustStore); KeyStore ks = KeyStore.getInstance("JKS"); ks.load(fp, trustStorePass.toCharArray()); fp.close(); factory.init(ks); TrustManager[] tms = factory.getTrustManagers(); return tms; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } return null; } private static KeyManager[] getKeyManagers() throws IOException { try { String alg = KeyManagerFactory.getDefaultAlgorithm(); KeyManagerFactory factory = KeyManagerFactory.getInstance(alg); InputStream fp = HelloWorldClient.class.getResourceAsStream(keyStore); KeyStore ks = KeyStore.getInstance("JKS"); ks.load(fp, keyStorePass.toCharArray()); fp.close(); factory.init(ks, keyStorePass.toCharArray()); KeyManager[] keyms = factory.getKeyManagers(); return keyms; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (UnrecoverableKeyException e) { e.printStackTrace(); } return null; } }
9.现在就可以运行client端测试鉴权功能了。
本来想着认真解释一下代码,不过环境太吵,实在没法安静下来写,就算是抛砖引玉吧。