SpringBoot+CXF 实现简单的webservice,并支持Basic验证

SpringBoot+CXF 实现简单的webservice,并支持Basic验证

pom.xml 添加的依赖

<!-- CXF -->
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
    <version>3.1.15</version>
</dependency>

一个简单的 interface 标记为webservice接口

package com.wzh.demo.webservice.service;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

/**
 * @desc: 测试发布的websercice服务
 * @author: wzh
 * @date: 2019年3月31日
 */
@WebService
public interface IhelloService {
    
    /**
     * @desc: 测试发布的websercice接口
     * @author: wzh
     * @date: 2019年3月31日
     * @param message
     * @return
     */
    @WebMethod
    public String helloWebService(@WebParam(name="msg")String message);

}

basic鉴权拦截器

CXF内置了很多拦截器,大部分默认添加到拦截器链中,有些拦截器也可以手动添加,如CXF的日志拦截器。如果需要自定义拦截器,只要继承AbstractPhaseInterceptor或者AbstractPhaseInterceptor的子类(如AbstractSoapInterceptor)

package com.wzh.demo.webservice.interceptor;


import javax.servlet.http.HttpServletRequest;
import javax.xml.soap.SOAPException;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.util.Base64;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.http.AbstractHTTPDestination;

/**
 * @desc: 测试用baisc鉴权拦截器
 * @author: wzh
 * @date: 2019年3月31日
 */
public class HelloAuthInterceptor extends AbstractSoapInterceptor{

    private static final String BASIC_PREFIX = "Basic ";
    private static final String USERNAME = "admin";
    private static final String PASSWORD = "123456";
    
    /**
     * 指定加入拦截器到某个阶段 
     */
    public HelloAuthInterceptor() {
        super(Phase.PRE_INVOKE);
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {

        // 获取HttpServletRequest
        HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
        
        String auth = request.getHeader("Authorization");
        
        if (auth == null) {
            SOAPException exception = new SOAPException("Authorization 授权信息为空!");
            throw new Fault(exception);
        }
        if (!auth.startsWith(BASIC_PREFIX)) {
            SOAPException exception = new SOAPException("Authorization 非baisc验证");
            throw new Fault(exception);
        }
        // 合法的baisc格式为username:password  例如:admin:123456
        String plaintext = new String(Base64.decodeBase64(auth.substring(BASIC_PREFIX.length())));
        if (StringUtils.isEmpty(plaintext) || !plaintext.contains(":")) {
            SOAPException exception = new SOAPException("Authorization 非baisc验证");
            throw new Fault(exception);
        }
        
        String[] usernameAndPass = plaintext.split(":");
        String username = usernameAndPass[0];
        String password = usernameAndPass[1];
        if (!USERNAME.equals(username) || !PASSWORD.equals(password)) {
            SOAPException exception = new SOAPException("用户名或密码不正确!");
            throw new Fault(exception);
        }

    }

}

webservice 服务接口实现类

package com.wzh.demo.webservice.service.impl;

import java.util.Date;

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

import org.apache.cxf.interceptor.InInterceptors;
import org.springframework.stereotype.Component;

import com.wzh.demo.webservice.service.IhelloService;

@InInterceptors(interceptors={"com.wzh.demo.webservice.interceptor.HelloAuthInterceptor"})// 添加拦截器
@WebService
@SOAPBinding(style=Style.RPC)
@Component
public class HelloServiceImpl implements IhelloService {

    @Override
    public String helloWebService(String message) {
        System.out.println(message);
        return "响应:" + new Date();
    }

}

配置发布webservice服务

package com.wzh.demo.webservice.Config;

import javax.xml.ws.Endpoint;

import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.cxf.transport.servlet.CXFServlet;

import com.wzh.demo.webservice.service.IhelloService;

/**
 * @desc: CXF SOAP 接口配置发布
 * @author: wzh
 * @date: 2019年3月31日
 */
@Configuration
public class SoapConfig {

    @Autowired
    @Qualifier(Bus.DEFAULT_BUS_ID)
    private SpringBus bus;

    @Autowired
    private IhelloService ihelloService;
    
    /**
     * 改变项目中服务名的前缀名
     * 方法被注释后:wsdl访问地址为http://localhost:8080/SpringBootDemo_eclipse/services/ihelloService?wsdl
     * 去掉注释后:wsdl访问地址为http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl
     * @return
     */
    @Bean
    public ServletRegistrationBean dispatcherServlet() {
        return new ServletRegistrationBean(new CXFServlet(), "/soap/*");
    }

    // 发布多个接口 添加多个@Bean endpoint.publish 这里不能一样
    @Bean
    public Endpoint helloEndpoint() {
        EndpointImpl endpoint = new EndpointImpl(bus, ihelloService);
        endpoint.publish("/ihelloService");
        return endpoint;
    }
}

如果是传统的spring+cxf 项目,拦截器的写法是一样的,只是发布和配置的方法在cxf的配置文件的xml中进行

    <!--id:名称(随意配),implementor:指定接口具体实现类,address:随意配-->  
    <jaxws:endpoint id="helloWorld"  implementor="#HelloWorldImpl" address="/HelloWorld"  >  
        <!-- 输入日志拦截器 -->  
       <jaxws:inInterceptors>  
          <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
       </jaxws:inInterceptors>  
       <!-- 输出日志拦截器 -->  
       <jaxws:outInterceptors>  
          <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
       </jaxws:outInterceptors>  
       <jaxws:properties>  
          <entry key="mtom_enabled" value="true"></entry>  
       </jaxws:properties>   
          
    </jaxws:endpoint>  

测试

WSDL 地址:http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl

代码客户端测试

CXF 自动编译解析的客户端

package com.wzh.demo.webservice.client;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

/**
 * This class was generated by Apache CXF 2.6.2
 * 2019-03-31T19:54:58.255+08:00
 * Generated source version: 2.6.2
 * 
 */
@WebService(targetNamespace = "http://service.webservice.demo.wzh.com/", name = "IhelloService")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface IhelloService {

    @WebResult(name = "return", targetNamespace = "http://service.webservice.demo.wzh.com/", partName = "return")
    @WebMethod
    public java.lang.String helloWebService(
        @WebParam(partName = "msg", name = "msg")
        java.lang.String msg
    );
}

package com.wzh.demo.webservice.client;

import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.Service;

/**
 * This class was generated by Apache CXF 2.6.2
 * 2019-03-31T19:54:58.261+08:00
 * Generated source version: 2.6.2
 * 
 */
@WebServiceClient(name = "HelloServiceImplService", 
                  wsdlLocation = "http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl",
                  targetNamespace = "http://impl.service.webservice.demo.wzh.com/") 
public class HelloServiceImplService extends Service {

    public final static URL WSDL_LOCATION;

    public final static QName SERVICE = new QName("http://impl.service.webservice.demo.wzh.com/", "HelloServiceImplService");
    public final static QName HelloServiceImplPort = new QName("http://impl.service.webservice.demo.wzh.com/", "HelloServiceImplPort");
    static {
        URL url = null;
        try {
            url = new URL("http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl");
        } catch (MalformedURLException e) {
            java.util.logging.Logger.getLogger(HelloServiceImplService.class.getName())
                .log(java.util.logging.Level.INFO, 
                     "Can not initialize the default wsdl from {0}", "http://localhost:8080/SpringBootDemo_eclipse/soap/ihelloService?wsdl");
        }
        WSDL_LOCATION = url;
    }

    public HelloServiceImplService(URL wsdlLocation) {
        super(wsdlLocation, SERVICE);
    }

    public HelloServiceImplService(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public HelloServiceImplService() {
        super(WSDL_LOCATION, SERVICE);
    }
    
    //This constructor requires JAX-WS API 2.2. You will need to endorse the 2.2
    //API jar or re-run wsdl2java with "-frontend jaxws21" to generate JAX-WS 2.1
    //compliant code instead.
    public HelloServiceImplService(WebServiceFeature ... features) {
        super(WSDL_LOCATION, SERVICE, features);
    }

    //This constructor requires JAX-WS API 2.2. You will need to endorse the 2.2
    //API jar or re-run wsdl2java with "-frontend jaxws21" to generate JAX-WS 2.1
    //compliant code instead.
    public HelloServiceImplService(URL wsdlLocation, WebServiceFeature ... features) {
        super(wsdlLocation, SERVICE, features);
    }

    //This constructor requires JAX-WS API 2.2. You will need to endorse the 2.2
    //API jar or re-run wsdl2java with "-frontend jaxws21" to generate JAX-WS 2.1
    //compliant code instead.
    public HelloServiceImplService(URL wsdlLocation, QName serviceName, WebServiceFeature ... features) {
        super(wsdlLocation, serviceName, features);
    }

    /**
     *
     * @return
     *     returns IhelloService
     */
    @WebEndpoint(name = "HelloServiceImplPort")
    public IhelloService getHelloServiceImplPort() {
        return super.getPort(HelloServiceImplPort, IhelloService.class);
    }

    /**
     * 
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns IhelloService
     */
    @WebEndpoint(name = "HelloServiceImplPort")
    public IhelloService getHelloServiceImplPort(WebServiceFeature... features) {
        return super.getPort(HelloServiceImplPort, IhelloService.class, features);
    }

}

客户端basic 认证调用

package com.wzh.demo.webservice.client;

import javax.xml.ws.BindingProvider;

/**
 * @desc: 测试连接用客户端
 * @author: wzh
 * @date: 2019年3月31日
 */
public class testClient {
    
    public static void main(String[] args) {
        IhelloService service = new HelloServiceImplService().getHelloServiceImplPort();
        
        // 添加basc 认证
        BindingProvider bp = (BindingProvider) service;
        bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "admin");
        bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "1234562");
        
        System.out.println(service.helloWebService("测试一下"));
    }
}

SOAPUI 测试
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容