加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

WebService学习——回顾之前应用

发布时间:2020-12-16 23:07:04 所属栏目:安全 来源:网络整理
导读:回顾之前项目的应用——Cxf在spring框架中的简单运用 之前项目需要用到WebService,但是由于项目开发的快节奏,所以虽然把他搭起来,但是没有了解他的原理。 1 准备工作 cxf核心包(cxf-2.4.1.jar)、wss4j( 安全模块 )、spring包。 其他包如 commons-logg

回顾之前项目的应用——Cxf在spring框架中的简单运用

之前项目需要用到WebService,但是由于项目开发的快节奏,所以虽然把他搭起来,但是没有了解他的原理。

1 准备工作


cxf核心包(cxf-2.4.1.jar)、wss4j( 安全模块)、spring包。
其他包如
commons-logging-1.1.1.jar
geronimo-activation_1.1_spec-1.0.2.jar (or Sun's Activation jar)
geronimo-annotation_1.0_spec-1.1.1.jar (JSR 250)
geronimo-javamail_1.4_spec-1.6.jar (or Sun's JavaMail jar)
geronimo-servlet_2.5_spec-1.2.jar (or Sun's Servlet jar)
geronimo-ws-metadata_2.0_spec-1.1.2.jar (JSR 181)
geronimo-jaxws_2.1_spec-1.0.jar (or Sun's jaxws-api-2.1.jar)
geronimo-stax-api_1.0_spec-1.0.1.jar (or other stax-api jar)
jaxb-api-2.1.jar
jaxb-impl-2.1.12.jar
jetty-6.1.21.jar
jetty-util-6.1.21.jar
neethi-2.0.4.jar
saaj-api-1.3.jar
saaj-impl-1.3.2.jar
wsdl4j-1.6.2.jar
wstx-asl-3.2.8.jar
XmlSchema-1.4.5.jar
xml-resolver-1.2.jar


如果使用的Maven,需要注意了,Maven仓库中下不到cxf的包。

2 配置web.xml

  <!-- 配置CXFServlet -->
  <servlet>
    <servlet-name>CXFServlet</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/service/*</url-pattern>
  </servlet-mapping>

3 spring整合cxf

在spring配置文件之一applicationContext.xml中,加入:
 <!-- 引入CXF Bean定义如下 -->
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

如果你需要自定义返回的信息,为了目录清除编写一个wssec.xml,然后import进你的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:cxf="http://cxf.apache.org/core"
    xmlns:wsa="http://cxf.apache.org/ws/addressing" xmlns:http="http://cxf.apache.org/transports/http/configuration"
    xmlns:wsrm-policy="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"
    xmlns:wsrm-mgr="http://cxf.apache.org/ws/rm/manager"
    xsi:schemaLocation="
       http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
       http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
       http://schemas.xmlsoap.org/ws/2005/02/rm/policy http://schemas.xmlsoap.org/ws/2005/02/rm/wsrm-policy.xsd
       http://cxf.apache.org/ws/rm/manager http://cxf.apache.org/schemas/configuration/wsrm-manager.xsd
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <cxf:bus>
        <cxf:features>
            <cxf:logging />
            <wsa:addressing />
        </cxf:features>
    </cxf:bus>
</beans>

spring配置文件 beans.xml
<?xml version="1.0" encoding="UTF-8"?>

<!-- Spring配置文件 -->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-lazy-init="false" default-dependency-check="none"
	default-autowire="no">
	<import resource="applicationContext.xml" />
	<import resource="applicationContext-aop.xml" />
	<import resource="applicationContext-webService.xml" />
	<import resource="applicationContext-Quartz.xml" /> 
	<import resource="wssec.xml"></import>

</beans>

4 编写客户端

WsClinetAuthHandler.java
package com.interfaces.utils;

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;

/**
 * 用一句话描述功能
 * @author lzy
 * @date(开发日期) 2014-9-9下午10:00:51
 */

public class WsClinetAuthHandler implements CallbackHandler {

    public static String userAcc;
    public static String password;
    
    public void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
            System.out.println("identifier: " + pc.getIdentifier());
            // 这里必须设置密码,否则会抛出:java.lang.IllegalArgumentException: pwd == null
            // but a password is needed
            pc.setPassword(password);// ▲【这里必须设置密码】▲
        }
    }

    public static String getUserAcc()
    {
        return userAcc;
    }

    public static void setUserAcc(String userAcc)
    {
        WsClinetAuthHandler.userAcc = userAcc;
    }

    public static String getPassword()
    {
        return password;
    }

    public static void setPassword(String password)
    {
        WsClinetAuthHandler.password = password;
    }
}

JaxBeansFactory.java
package com.interfaces.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.cxf.binding.soap.interceptor.SoapInterceptor;
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 com.saleSystem.util.Constants;


/**
 * 通过传入相应的接口类,以及地址,进行数据通信
 * @author lzy
 * @version Ver1.0 2014-8-12
 * @date(开发日期) 2014-8-12上午11:39:40
 */
public class JaxBeansFactory {

    public static String userName;
    
    public static void setUserAccAndPaswword(String userAcc,String password){
        WsClinetAuthHandler.setPassword(password);
        setUserName(userAcc);
    }
    
    public static Object createService(Class T,String Address){
        Object obj = null;
        
        Map<String,Object> outProps = new HashMap<String,Object>();
        outProps.put(WSHandlerConstants.ACTION,WSHandlerConstants.USERNAME_TOKEN);
        outProps.put(WSHandlerConstants.USER,userName);
        outProps.put(WSHandlerConstants.PASSWORD_TYPE,WSConstants.PW_TEXT);
        // 指定在调用远程ws之前触发的回调函数WsClinetAuthHandler,其实类似于一个拦截器
        outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,WsClinetAuthHandler.class.getName());
        ArrayList<SoapInterceptor> list = new ArrayList<SoapInterceptor>();
        // 添加cxf安全验证拦截器,必须
        list.add(new SAAJOutInterceptor());
        list.add(new WSS4JOutInterceptor(outProps));
        
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

        factory.setServiceClass(T);

        factory.setAddress(Address);
        factory.getOutInterceptors().addAll(list);
        obj = factory.create();
        
        return obj;
    }

    public static String getUserName()
    {
        return userName;
    }

    public static void setUserName(String userName)
    {
        JaxBeansFactory.userName = userName;
    }

    
}

ISaleTerReceive.java
package com.interfaces.saleSystem.services;

import java.util.List;

import javax.jws.WebService;

import com.interfaces.saleSystem.models.DownloadDatas;

/**
 * 用一句话描述功能
 * @author lzy
 * @version Ver1.0 2014-8-8
 * @date(开发日期) 2014-8-8上午10:05:13
 */
@WebService
public interface ISaleTerReceive {

    public DownloadDatas receivePorkResults(String[] porkIds);
    
    public DownloadDatas receivePorkPdtResults(String[] porkPdtIds);
    
}

5 编写服务器端

WsAuthHandler.java
package com.interfaces.utils;

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;

import com.centrySystem.util.JDBCCon;

/**
 * 用一句话描述功能
 * 
 * @author lzy
 * @date(开发日期) 2014-9-9下午10:16:38
 */

public class WsAuthHandler implements CallbackHandler {

    
    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();
            String password = this.getPasswordByUserAcc(identifier);
            if (usage == WSPasswordCallback.USERNAME_TOKEN)
            {
                // username token pwd...
                // ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同
                // 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime
                // Changes片段
                    pc.setPassword(password);// ▲【这里非常重要】▲
                // ▲PS 如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:
                // The
                // security token could not be authenticated or
                // authorized异常,服务端会认为客户端为非法调用
            }
            else if (usage == WSPasswordCallback.SIGNATURE)
            {// 密钥方式SIGNATURE
                // set the password for client's keystore.keyPassword
                // ▲这里的值必须和客户端设的值相同,从cxf2.4.x后校验方式改为cxf内部实现校验,不必自己比较password是否相同;
                // 请参考:http://cxf.apache.org/docs/24-migration-guide.html的Runtime
                // Changes片段
//                System.out.println(password);
                    pc.setPassword(password);// ▲【这里非常重要】▲
                // ▲PS:如果和客户端不同将抛出org.apache.ws.security.WSSecurityException:The
                // security token could not be authenticated or
                // authorized异常,服务端会认为客户端为非法调用
            }
            // 不用做其他操作
        }
    }

    private String getPasswordByUserAcc(String userAcc)
    {
        JDBCCon con = new JDBCCon();
        String password = con.getPasswordByUserName(userAcc);
        return password;
    }
}


private String getPasswordByUserAcc(String userAcc)
    {
        JDBCCon con = new JDBCCon();
        String password = con.getPasswordByUserName(userAcc);
        return password;
    }

这里,我写了一个JDBC的连接,来验证客服端传过来的用户名密码是否正确,即验证他是否是我服务端的合法用户,如果是合法用户,我再提供给他服务。

SaleTerReceiveImpl.java

/**
 * ISaleTerSendImpl.java
 * 类名: ISaleTerSendImpl
 *
 *   ver     变更日                  变更人            变更内容
 * ──────────────────────────────────
 *  V1.0    2014-8-8    lzy     初始生成
 *
 * Copyright 2014 
 */
package com.interfaces.saleSystem.services.impl;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;
import javax.jws.WebService;

import com.centrySystem.services.government.warning.IPorkPdtWarningService;
import com.centrySystem.services.government.warning.IPorkWarningService;
import com.centrySystem.util.TypeInCenterUtil;
import com.interfaces.saleSystem.models.DownloadDatas;
import com.interfaces.saleSystem.models.PorkPdtWarningResult;
import com.interfaces.saleSystem.models.PorkWarningResult;
import com.interfaces.saleSystem.services.ISaleTerReceive;

/**
 * 用一句话描述功能
 * 
 * @author lzy
 * @version Ver1.0 2014-8-8
 * @date(开发日期) 2014-8-8上午10:05:38
 */
@WebService
public class SaleTerReceiveImpl implements ISaleTerReceive {
	@Resource
	private IPorkWarningService porkWarningSer;
	@Resource
	private IPorkPdtWarningService porkPdtWarningSer;

	@Override
	public DownloadDatas receivePorkResults(String[] porkIds) {
		List<PorkWarningResult> list = new ArrayList<PorkWarningResult>();
		for (String porkId : porkIds) {
			PorkWarningResult pork = new PorkWarningResult();
			pork.setPorkId(porkId);
			pork = this.porkStartInStorWarning(porkId,pork);
			list.add(pork);
		}
		DownloadDatas datas = new DownloadDatas();
		datas.setPorkWarningResults(list);
		return datas;
	}

	private PorkWarningResult porkStartInStorWarning(String porkId,PorkWarningResult pork) {
		String result = porkWarningSer.getPorkWarningResult(porkId);
        if(result==null||result.equals("")){
        	pork.setIsRejection(TypeInCenterUtil.TYPE_OF_WARNING_ACCESS);
        }
        else{
        	pork.setIsRejection(TypeInCenterUtil.TYPE_OF_WARNING_REJECT);
        	pork.setResultReason(result);
        }
        return pork;
	}

	private PorkPdtWarningResult porkPdtStartInStorWarning(String porkPdtId,PorkPdtWarningResult porkPdt) {
		String result = porkPdtWarningSer.getPorkPdtWarningResult(porkPdtId);
        if(result==null||result.equals("")){
        	porkPdt.setIsRejection(TypeInCenterUtil.TYPE_OF_WARNING_ACCESS);
        }
        else{
        	porkPdt.setIsRejection(TypeInCenterUtil.TYPE_OF_WARNING_REJECT);
        	porkPdt.setResultReason(result);
        }
        return porkPdt;
	}

	@Override
	public DownloadDatas receivePorkPdtResults(String[] porkPdtIds) {
		List<PorkPdtWarningResult> list = new ArrayList<PorkPdtWarningResult>();
		for (String porkPdtId : porkPdtIds) {
			PorkPdtWarningResult porkPdt = new PorkPdtWarningResult();
			porkPdt.setPorkPdtId(porkPdtId);
			porkPdt = this.porkPdtStartInStorWarning(porkPdtId,porkPdt);
			list.add(porkPdt);
		}
		DownloadDatas datas = new DownloadDatas();
		datas.setPorkPdtWarningResults(list);
		return datas;
	}

}

服务器端,除了有客户端相同ISaleTerReceive接口外,还实现了这个接口。

服务端发布服务?applicationContext-webService.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:jaxws="http://cxf.apache.org/jaxws" 
    xmlns:jaxrs="http://cxf.apache.org/jaxrs" 
    xmlns:cxf="http://cxf.apache.org/core"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
         http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
         http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    
    
    <!-- 引入CXF Bean定义如下 -->
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
    
    
    <!-- 发布webservice -->
    <bean id="saleTerReceiveImpl" class="com.interfaces.saleSystem.services.impl.SaleTerReceiveImpl" ></bean>
    <jaxws:endpoint id="saleTerReceive" implementor="#saleTerReceiveImpl" address="/saleTerReceive">
         <jaxws:inInterceptors>
            <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
            <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
                <constructor-arg>
                    <map>
                        <!-- 设置加密类型 -->
                        <entry key="action" value="UsernameToken" />
                        <!-- 设置密码类型为明文 -->
                        <entry key="passwordType" value="PasswordText" />
                        <!--<entry key="action" value="UsernameToken Timestamp" /> 设置密码类型为加密<entry 
                            key="passwordType" value="PasswordDigest" /> -->
                        <entry key="passwordCallbackClass" value="com.interfaces.utils.WsAuthHandler" />
                    </map>
                </constructor-arg>
            </bean>
        </jaxws:inInterceptors>
    </jaxws:endpoint>
        
</beans>


6.客户端调用

InStorWarningAction.java
package com.interfaces.action;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import com.interfaces.saleSystem.models.DownloadDatas;
import com.interfaces.saleSystem.services.ISaleTerReceive;
import com.interfaces.utils.JaxBeansFactory;
import com.saleSystem.dao.systemManag.ISysConfigDao;
import com.saleSystem.models.TSysConfig;
import com.saleSystem.services.upload.IUploadService;
import com.saleSystem.util.md5.CipherUtil;

@Component("instorWarningAction")
public class InStorWarningAction {

	@Resource
	private ISysConfigDao sysConfigDao;
	@Resource
	private IUploadService uploadSer;

	public DownloadDatas getDownloadDatasByPorkIdList(String[] porkIdStr) throws Exception {
		DownloadDatas datas = null;
		TSysConfig sys = sysConfigDao.get("from TSysConfig");
		String userAcc = sys.getUserAcc();
		String password = sys.getUserPassword();
		String ip = sys.getServerIp();
		String serviceAdd = "http://" + ip + ":8080/centrySystem/service/saleTerReceive?wsdl";
		String newPassword = CipherUtil.generatePassword(password);
		JaxBeansFactory.setUserAccAndPaswword(userAcc,newPassword);
		ISaleTerReceive service = (ISaleTerReceive) JaxBeansFactory.createService(ISaleTerReceive.class,serviceAdd);
		datas = service.receivePorkResults(porkIdStr);
		return datas;
	}

	public DownloadDatas getDownloadDatasByPorkPdtIdList(String[] porkPdtIdStr) throws Exception {
		DownloadDatas datas = null;
		TSysConfig sys = sysConfigDao.get("from TSysConfig");
		String userAcc = sys.getUserAcc();
		String password = sys.getUserPassword();
		String ip = sys.getServerIp();
		String serviceAdd = "http://" + ip + ":8080/centrySystem/service/saleTerReceive?wsdl";
		String newPassword = CipherUtil.generatePassword(password);
		JaxBeansFactory.setUserAccAndPaswword(userAcc,serviceAdd);
		datas = service.receivePorkPdtResults(porkPdtIdStr);
		return datas;
	}

}

这是项目中一个调用的地方。
其中CipherUtil.generatePassword(password)是MD5加密,这第一是为了防止密码被窃取,第二是由于在服务端数据库中密码也是加密的。
JaxBeansFactory.setUserAccAndPassword(userAcc,newPassword)这是将账号密码填入账号密码。
datas是返回的数据,是自己定义的类型。

很重要的一点,客户端和服务器端在通信的时候,使用的model,需要一样。即你返回的DownloadDatas在你客户端与服务器端的定义是相同的!
请忽视我将hql写在action里面.....

上面使用的例子中,还有很多是我之前囫囵吞枣就使用进去的,我希望通过几天学习能把webSevice真正的用起来!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读