zoukankan      html  css  js  c++  java
  • CXF 入门:创建一个基于WS-Security标准的安全验证(CXF回调函数使用,)

     http://jyao.iteye.com/blog/1346547

    注意:以下客户端调用代码中获取服务端ws实例,都是通过CXF 入门: 远程接口调用方式实现

    直入正题!

    以下是服务端配置

    ========================================================

    一,web.xml配置,具体不在详述

    Xml代码  收藏代码
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <!DOCTYPE web-app  
    3.     PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"  
    4.     "http://java.sun.com/dtd/web-app_2_3.dtd">  
    5. <web-app>  
    6.     <context-param>  
    7.         <param-name>contextConfigLocation</param-name>  
    8.         <!--ws-context.xml(必须)是cxf配置文件, wssec.xml可选,作用可以打印出加密信息类容 -->  
    9.         <param-value>WEB-INF/ws-context.xml,WEB-INF/wssec.xml</param-value>  
    10.     </context-param>  
    11.   
    12.     <listener>  
    13.         <listener-class>  
    14.             org.springframework.web.context.ContextLoaderListener  
    15.         </listener-class>  
    16.     </listener>  
    17.   
    18.     <servlet>  
    19.         <servlet-name>CXFServlet</servlet-name>  
    20.         <display-name>CXF Servlet</display-name>  
    21.         <servlet-class>  
    22.             org.apache.cxf.transport.servlet.CXFServlet  
    23.         </servlet-class>  
    24.         <load-on-startup>0</load-on-startup>  
    25.     </servlet>  
    26.   
    27.     <servlet-mapping>  
    28.         <servlet-name>CXFServlet</servlet-name>  
    29.         <url-pattern>/services/*</url-pattern>  
    30.     </servlet-mapping>  
    31. </web-app>  

     二,ws具体代码
    简单的接口

    Java代码  收藏代码
    1. import javax.jws.WebService;  
    2.   
    3. @WebService()  
    4. public interface WebServiceSample {  
    5.     String say(String name);  
    6.   
    7. }  

    接口具体实现类

    Java代码  收藏代码
    1. public class WebServiceSampleImpl implements WebServiceSample {  
    2.   
    3.     public String say(String name) {  
    4.         return "你好," + name;  
    5.     }  
    6. }  
     

    三,ws回调函数,必须实现javax.security.auth.callback.CallbackHandler

    从cxf2.4.x后校验又cxf内部实现校验,所以不必自己校验password是否相同,但客户端必须设置,详情请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime  Changes片段

    回调函数WsAuthHandler代码,校验客户端请求是否合法 ,合法就放行,否则拒绝执行任何操作

    Java代码  收藏代码
    1. import java.io.IOException;  
    2. import javax.security.auth.callback.Callback;  
    3. import javax.security.auth.callback.CallbackHandler;  
    4. import javax.security.auth.callback.UnsupportedCallbackException;  
    5. import org.apache.cxf.interceptor.Fault;  
    6. import org.apache.ws.security.WSPasswordCallback;  
    7. import org.apache.xmlbeans.impl.soap.SOAPException;  
    8.   
    9. public class WsAuthHandler implements CallbackHandler {  
    10.   
    11.     public void handle(Callback[] callbacks) throws IOException,  
    12.             UnsupportedCallbackException {  
    13.         for (int i = 0; i < callbacks.length; i++) {  
    14.             WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];  
    15.             String identifier = pc.getIdentifier();  
    16.             int usage = pc.getUsage();  
    17.             if (usage == WSPasswordCallback.USERNAME_TOKEN) {// 密钥方式USERNAME_TOKEN  
    18.                 // username token pwd...  
    19.                 // ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同  
    20.                 // 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime  
    21.                 // Changes片段  
    22.                 pc.setPassword("testPassword");// ▲【这里非常重要】▲  
    23.                 // ▲PS 如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:  
    24.                 // The  
    25.                 // security token could not be authenticated or  
    26.                 // authorized异常,服务端会认为客户端为非法调用  
    27.             } else if (usage == WSPasswordCallback.SIGNATURE) {// 密钥方式SIGNATURE  
    28.                 // set the password for client's keystore.keyPassword  
    29.                 // ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同;  
    30.                 // 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime  
    31.                 // Changes片段  
    32.                 pc.setPassword("testPassword");// //▲【这里非常重要】▲  
    33.                 // ▲PS:如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:The  
    34.                 // security token could not be authenticated or  
    35.                 // authorized异常,服务端会认为客户端为非法调用  
    36.             }  
    37.             //不用做其他操作  
    38.         }  
    39.     }  
    40. }  
     

    四,CXF配置ws-context.xml:

    Xml代码  收藏代码
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"  
    4.     xsi:schemaLocation="  
    5. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
    6. http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">  
    7.   
    8.     <import resource="classpath:META-INF/cxf/cxf.xml" />  
    9.     <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />  
    10.     <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />  
    11.     <!-- 以上未基本配置,必须,位置在cxf jar中 -->  
    12.     <jaxws:endpoint id="webServiceSample" address="/WebServiceSample"  
    13.         implementor="com.service.impl.WebServiceSampleImpl">  
    14.         <!--inInterceptors表示被外部调用时,调用此拦截器 -->  
    15.         <jaxws:inInterceptors>  
    16.             <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />  
    17.             <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">  
    18.                 <constructor-arg>  
    19.                     <map>  
    20.                         <!-- 设置加密类型 -->  
    21.                         <entry key="action" value="UsernameToken" />  
    22.                         <!-- 设置密码类型为明文 -->  
    23.                         <entry key="passwordType" value="PasswordText" />  
    24.                         <!--<entry key="action" value="UsernameToken Timestamp" /> 设置密码类型为加密<entry   
    25.                             key="passwordType" value="PasswordDigest" /> -->  
    26.                         <entry key="passwordCallbackClass" value="com.service.handler.WsAuthHandler" />  
    27.                     </map>  
    28.                 </constructor-arg>  
    29.             </bean>  
    30.         </jaxws:inInterceptors>  
    31.     </jaxws:endpoint>  
    32. </beans>  

    CXF配置wssec.xml(可选),用于配置输出校验的具体信息

    Xml代码  收藏代码
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://cxf.apache.org/core"  
    4.     xmlns:wsa="http://cxf.apache.org/ws/addressing" xmlns:http="http://cxf.apache.org/transports/http/configuration"  
    5.     xmlns:wsrm-policy="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"  
    6.     xmlns:wsrm-mgr="http://cxf.apache.org/ws/rm/manager"  
    7.     xsi:schemaLocation="  
    8.        http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd  
    9.        http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd  
    10.        http://schemas.xmlsoap.org/ws/2005/02/rm/policy http://schemas.xmlsoap.org/ws/2005/02/rm/wsrm-policy.xsd  
    11.        http://cxf.apache.org/ws/rm/manager http://cxf.apache.org/schemas/configuration/wsrm-manager.xsd  
    12.        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
    13.     <cxf:bus>  
    14.         <cxf:features>  
    15.             <cxf:logging />  
    16.             <wsa:addressing />  
    17.         </cxf:features>  
    18.     </cxf:bus>  
    19. </beans>  

    服务端代码及配置到此结束!!!

    =========================================================

    =========================================================

    以下是客户端配置,主要是回调函数,在客户端调用服务端前被调用,负责安全信息的设置

    ----------------------------------------------------------------------------------------

    一,先实现回调函数WsClinetAuthHandler,同样必须实现javax.security.auth.callback.CallbackHandler

    Java代码  收藏代码
    1. import java.io.IOException;  
    2. import javax.security.auth.callback.Callback;  
    3. import javax.security.auth.callback.CallbackHandler;  
    4. import javax.security.auth.callback.UnsupportedCallbackException;  
    5. import org.apache.ws.security.WSPasswordCallback;  
    6.   
    7. public class WsClinetAuthHandler implements CallbackHandler {  
    8.   
    9.     public void handle(Callback[] callbacks) throws IOException,  
    10.             UnsupportedCallbackException {  
    11.         for (int i = 0; i < callbacks.length; i++) {  
    12.             WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];  
    13.             System.out.println("identifier: " + pc.getIdentifier());  
    14.             // 这里必须设置密码,否则会抛出:java.lang.IllegalArgumentException: pwd == null  
    15.             // but a password is needed  
    16.             pc.setPassword("testPassword");// ▲【这里必须设置密码】▲  
    17.         }  
    18.     }  
    19. }  
     

    二,客户端调用代码:

    Java代码  收藏代码
    1. import java.util.ArrayList;  
    2. import java.util.HashMap;  
    3. import java.util.Map;  
    4.   
    5. import org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor;  
    6. import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;  
    7. import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;  
    8. import org.apache.ws.security.WSConstants;  
    9. import org.apache.ws.security.handler.WSHandlerConstants;  
    10.   
    11. import test.saa.client.WebServiceSample;  
    12. import test.saa.handler.WsClinetAuthHandler;  
    13.   
    14. public class TestClient {  
    15.   
    16.     public static void main(String[] args) {  
    17.         // 以下和服务端配置类似,不对,应该说服务端和这里的安全验证配置一致  
    18.         Map<String, Object> outProps = new HashMap<String, Object>();  
    19.         outProps.put(WSHandlerConstants.ACTION,  
    20.                 WSHandlerConstants.USERNAME_TOKEN);  
    21.         outProps.put(WSHandlerConstants.USER, "admin");  
    22.         outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);  
    23.         // 指定在调用远程ws之前触发的回调函数WsClinetAuthHandler,其实类似于一个拦截器  
    24.         outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,  
    25.                 WsClinetAuthHandler.class.getName());  
    26.         ArrayList list = new ArrayList();  
    27.         // 添加cxf安全验证拦截器,必须  
    28.         list.add(new SAAJOutInterceptor());  
    29.         list.add(new WSS4JOutInterceptor(outProps));  
    30.   
    31.         JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();  
    32.         // WebServiceSample服务端接口实现类,这里并不是直接把服务端的类copy过来,具体请参考http://learning.iteye.com/blog/1333223  
    33.         factory.setServiceClass(WebServiceSample.class);  
    34.         // 设置ws访问地址  
    35.         factory.setAddress("http://localhost:8080/cxf-wssec/services/WebServiceSample");  
    36.         //注入拦截器,用于加密安全验证信息  
    37.         factory.getOutInterceptors().addAll(list);  
    38.         WebServiceSample service = (WebServiceSample) factory.create();  
    39.         String response = service.say("2012");  
    40.         System.out.println(response);  
    41.     }  
    42. }  

     客户端到此结束!!!!

    ========================================================================

    #######################################################################

    PS:客户端的另一种调用方式,主要通过配置文件,不过需要spring bean的配置文件(第一种就不用牵扯到spring的配置,比较通用吧!)

    一,回调函数WsClinetAuthHandler不变,和上面一样
    二,client-beans.xml安全验证配置文件,具体信息看注释,如下:

    Xml代码  收藏代码
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"  
    4.     xsi:schemaLocation="  
    5. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
    6. http://cxf.apache.org/jaxws http://cxf.apache.org/schema/jaxws.xsd">  
    7. <!--这里无非是通过配置来替代JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean()创建代理并实例化一个ws-->  
    8.     <bean id="client" class="test.saa.client.WebServiceSample"  
    9.         factory-bean="clientFactory" factory-method="create" />  
    10.     <!-- 通过代理创建ws实例 -->  
    11.     <bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">  
    12.         <property name="serviceClass" value="test.saa.client.WebServiceSample" />  
    13.         <!-- ws地址,也可以是完整的wsdl地址 -->  
    14.         <property name="address"  
    15.             value="http://localhost:8080/cxf-wssec/services/WebServiceSample" />  
    16.         <!--outInterceptors表示调用外部指定ws时,调用此拦截器 -->  
    17.         <property name="outInterceptors">  
    18.             <list>  
    19.                 <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />  
    20.                 <ref bean="wss4jOutConfiguration" />  
    21.             </list>  
    22.         </property>  
    23.     </bean>  
    24.   
    25.     <bean id="wss4jOutConfiguration" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">  
    26.         <property name="properties">  
    27.             <map>  
    28.                 <!-- 设置加密类型 ,服务端需要和这里的设置保持一致-->  
    29.                 <entry key="action" value="UsernameToken" />  
    30.                 <entry key="user" value="admin" />  
    31.                 <!-- 设置密码为明文 ,服务端需要和这里的设置保持一致-->  
    32.                 <entry key="passwordType" value="PasswordText" />  
    33.                 <!-- <entry key="action" value="UsernameToken Timestamp" /> <entry key="user"   
    34.                     value="adminTest" /> 设置密码类型为加密方式,服务端需要和这里的设置保持一致<entry key="passwordType" value="PasswordDigest"   
    35.                     /> -->  
    36.                 <entry key="passwordCallbackRef">  
    37.                     <ref bean="passwordCallback" />  
    38.                 </entry>  
    39.             </map>  
    40.         </property>  
    41.     </bean>  
    42.     <bean id="passwordCallback" class="test.saa.handler.WsClinetAuthHandler" />  
    43. </beans>  

     
    三,具体调用服务端代码:

    Java代码  收藏代码
    1. import org.springframework.context.ApplicationContext;  
    2. import org.springframework.context.support.ClassPathXmlApplicationContext;  
    3.   
    4. import test.saa.client.WebServiceSample;  
    5.   
    6. public final class Client {  
    7.   
    8.     public static void main(String args[]) throws Exception {  
    9.     //加载配置  
    10.         ApplicationContext context = new ClassPathXmlApplicationContext(  
    11.                 new String[] { "test/saa/client-beans.xml" });  
    12.         //获取ws实例  
    13.         WebServiceSample client = (WebServiceSample) context.getBean("client");  
    14.         String response = client.say("2012");  
    15.         System.out.println("Response: " + response);  
    16.     }  
    17. }  

     
    到此客户端第二种实现方式结束
    GOOD LUCKY!!!
    如有不明,请指教!!!

     

    1 
    0 
    分享到:  
    参考知识库
    Git知识库286  关注 | 304  收录
    jQuery知识库445  关注 | 232  收录
    大型网站架构知识库1233  关注 | 532  收录
    AngularJS知识库594  关注 | 262  收录
    评论
    6 楼 wentao_tang 2013-12-02   引用
    客户端调用,这行代码是必须的吗,有什么作用吗?
    outProps.put(WSHandlerConstants.USER, "admin"); 
    5 楼 xiangximingong 2013-09-18   引用
    我想问下,就是客户端传入错误的密码,也会抛一个异常,但这个异常我不知道在哪捕获,也不知道怎么通知客户端密码错误!能否指教下……
    4 楼 zhenglongfei 2013-07-25   引用
    想不通,为什么要在服务端要在set一下呢??
    3 楼 Sev7en_jun 2012-12-06   引用
    ice.kane 写道
    zl759869747 写道
     我想问下,为什么在服务端的回调函数里面当还要setPassword呢?我很郁闷的是,我在客户端明明已经设定了密码,可是服务端却拿不到,而且如果有服务端不设定密码就抛异常,我想问下到底是怎么样校验的呢?

    我也是遇到这个问题。。 

    在服务端setPassword,那密码校验还有什么意义

    1,为什么不设置密码会抛异常: 
       --既然要把服务加密,就要设置密码,要不然怎么加密,怎么知道哪些客户端是有权限访问的,就像买了一把锁,连自己(服务端)都不知道钥匙是怎么,那别人(客户端)怎么开这把锁 
    2,服务端设置密码是为了给自己上一把锁,如果要访问它必须有开这把锁的钥匙,即密码,有钥匙还要保证这个钥匙可以开这把锁,即客户端拿的密码是否和服务端锁的密码一致
    2 楼 ice.kane 2012-11-27   引用
    zl759869747 写道
     我想问下,为什么在服务端的回调函数里面当还要setPassword呢?我很郁闷的是,我在客户端明明已经设定了密码,可是服务端却拿不到,而且如果有服务端不设定密码就抛异常,我想问下到底是怎么样校验的呢?

    我也是遇到这个问题。。

    在服务端setPassword,那密码校验还有什么意义
    1 楼 zl759869747 2012-10-23   引用
     我想问下,为什么在服务端的回调函数里面当还要setPassword呢?我很郁闷的是,我在客户端明明已经设定了密码,可是服务端却拿不到,而且如果有服务端不设定密码就抛异常,我想问下到底是怎么样校验的呢?
  • 相关阅读:
    广义mandelbrot集,使用python的matplotlib绘制,支持放大缩小
    cs229 斯坦福机器学习笔记(一)-- 入门与LR模型
    Scrapy研究探索(三)——Scrapy核心架构与代码执行分析
    matlab各类数据l图像之间的转化
    开源重磅,java内容管理系统CMS,点击就可以编辑,保存,轻松构建自己的站点
    Android4.4 ContentResolver查询图片无效 及 图库删除 添加图片后,ContentResolver不更新的问题解决
    03002_MySQL数据库的安装和配置
    [.Net Core] 简单使用 Mvc 内置的 Ioc
    Asp.Net MVC中Action跳转
    EF的增删改查
  • 原文地址:https://www.cnblogs.com/exe19/p/5403083.html
Copyright © 2011-2022 走看看