springboot 整合xcf 发布 webservice

    技术2022-07-11  126

    Spring Boot集成webService

    在pom添加依赖

    <!--WerbService CXF依赖 start--> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> </dependency> <!--WerbService CXF依赖 end-->

    提供webservice接口

    @WebService(name = "MesService", // 暴露服务名称 targetNamespace = "http://mes.jingchuang.com/"// 命名空间,一般是接口的包名倒序 ) public interface T100Service { /** * 1.【sendMaterialInfo】接收T100料件信息 * * @param requestMaterialInfoData * @return Result */ @WebMethod Result sendMaterialInfo(RequestMaterialInfoData requestMaterialInfoData); }

    实现webservice的方法

    @Service @Transactional @WebService( // 对外发布的服务名,指定Web Service的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service serviceName = "MesService", //portName:wsdl:portName的值。缺省值为WebService.name+Port portName = "UserServiceImpl", // 命名空间(默认的值为 “http://包名/” (也可为域名) ,可以通过此变量指定一个自定义的targetNamespace值) targetNamespace = "http://mes.jingchuang.com/", // 对外提供的接口 endpointInterface = "com.jingchuang.mes.t100service.service.T100Service") @Component @Slf4j public class T100ServiceImpl implements T100Service { /** * 1.【sendMaterialInfo】接收****料件信息 * * @param requestMaterialInfoData * @return */ //region 接收料件信息 @Override @Transactional public Result sendMaterialInfo(RequestMaterialInfoData requestMaterialInfoData) { return ResultGenerator.fail("请输入正确的操作码"); } //endregion }

    配置并发布

    @Configuration public class WebServiceConfig { // 注意更改此方法名:public ServletRegistrationBean dispatcherServlet() // 把默认映射覆盖掉了,把这个名字改掉,控制类方法就能访问了,可以正常其他请求url,webservice服务也正常。 @Bean public ServletRegistrationBean disServlet(){ return new ServletRegistrationBean(new CXFServlet(),"/service/*");//发布服务名称 } @Bean(name = Bus.DEFAULT_BUS_ID) public SpringBus springBus() { return new SpringBus(); } @Autowired public T100Service t100Service; @Bean public Endpoint endpoint() { EndpointImpl endpoint=new EndpointImpl(springBus(), t100Service);//绑定要发布的服务 endpoint.publish("/zhlxmes"); //显示要发布的名称 endpoint.getInInterceptors().add(new AuthInterceptor()); //增加用户名密码验证 return endpoint; } }

    增加用户名,密码验证配置

    public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> { private static final Logger logger = LoggerFactory.getLogger(AuthInterceptor.class); private SAAJInInterceptor saa = new SAAJInInterceptor(); private static final String USER_NAME = "t100"; private static final String USER_PASSWORD = "t100"; public AuthInterceptor() { super(Phase.PRE_PROTOCOL); getAfter().add(SAAJInInterceptor.class.getName()); } @Override public void handleMessage(SoapMessage message) throws Fault { SOAPMessage mess = message.getContent(SOAPMessage.class); if (mess == null) { saa.handleMessage(message); mess = message.getContent(SOAPMessage.class); } SOAPHeader head = null; try { head = mess.getSOAPHeader(); } catch (Exception e) { logger.error("getSOAPHeader error: {}", e.getMessage(), e); } if (head == null) { throw new Fault(new IllegalArgumentException("找不到Header,无法验证用户信息")); } NodeList users = head.getElementsByTagName("username"); NodeList passwords = head.getElementsByTagName("password"); if (users.getLength() < 1) { throw new Fault(new IllegalArgumentException("找不到用户信息")); } if (passwords.getLength() < 1) { throw new Fault(new IllegalArgumentException("找不到密码信息")); } String userName = users.item(0).getTextContent().trim(); String password = passwords.item(0).getTextContent().trim(); if (USER_NAME.equals(userName) && USER_PASSWORD.equals(password)) { logger.debug("admin auth success"); } else { SOAPException soapExc = new SOAPException("认证错误"); logger.debug("admin auth failed"); throw new Fault(soapExc); } } }

    客户端

    客户端可以通过两种方式调用服务端。

    1 .通过JaxWsDynamicClientFactory方式

    public static Object doPostSoap(Class<?> clazz, String postUrl, String methodName, String xml) { try { Object[] objects; if (postUrl.equals("dev")) { objects = new Object[]{TestConten}; } else { log.info("【T00/doPostSoap发送】:\n" + xml); // 创建动态客户端 JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance(); Client client = dcf.createClient(postUrl); // 设置超时单位为毫秒 HTTPConduit conduit = (HTTPConduit) client.getConduit(); HTTPClientPolicy policy = new HTTPClientPolicy(); policy.setConnectionTimeout(15000); policy.setAllowChunking(false); policy.setReceiveTimeout(30000); conduit.setClient(policy); // invoke("方法名",参数1,参数2,参数3....); objects = client.invoke(methodName, xml); log.info("【T00/doPostSoap接收】:\n" + objects[0].toString()); // 需要密码的情况需要加上用户名和密码 // client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME,PASS_WORD)); } Log loginfo = new Log(); loginfo.setType("info"); loginfo.setName("【请求T100接口】-doPostSoap"); loginfo.setContent(xml); loginfo.setMethodName(methodName); loginfo.setCreateTime(new Date()); loginfo.setCreateUserId(BaseContextHandler.getUserID()); loginfo.setCreateUserName(BaseContextHandler.getUsername()); loginfo.setResult(objects[0].toString()); try { logService.save(loginfo); } catch (Exception ex) { log.info("保存日志错误" + ex.getMessage()); } if (objects.length <= 0) { return null; } ReceivingData receivingData = (ReceivingData) XsteamUtil.toBean(ReceivingData.class, objects[0].toString()); // receivingData = (ReceivingData)XsteamUtil.toBean(ReceivingData.class,TestConten); Object rulest = XsteamUtil.toBean(clazz, receivingData.getPayload().getParam().getData()); return rulest; } catch (Exception e) { System.out.println("错误信息:" + e.getMessage()); return null; } }

    这种方式要注意的就是,如果调用的服务接口返回的是一个自定义对象,那么结果Object[]中的数据类型就成了这个自定义对象(组件帮你自动生成了这个对象),

    但是你本地可能并没有这个类,所以需要自行转换处理,最简单的是新建一个跟返回结果一模一样的类进行强转。

    客户端需要在header中添加用户信息,这样才能通过服务器的验证。

    public class ClientLoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> { private String username; private String password; public ClientLoginInterceptor(String username, String password) { super(Phase.PREPARE_SEND); this.username = username; this.password = password; } @Override public void handleMessage(SoapMessage soap) throws Fault { List<Header> headers = soap.getHeaders(); Document doc = DOMUtils.createDocument(); Element auth = doc.createElement("authrity"); Element username = doc.createElement("username"); Element password = doc.createElement("password"); username.setTextContent(this.username); password.setTextContent(this.password); auth.appendChild(username); auth.appendChild(password); headers.add(0, new Header(new QName("tiamaes"),auth)); } }

    2、通过wsimport生成客户端代码 通过jdk自带的wsimport生成客户端代码。进入$JAVA_HOME/bin下,新建bin和src两个文件夹,执行以下命令: wsimport -d ./bin -s ./src -keep http://localhost:8080/services/layout?wsdl 其中几个参数有以下几个, -d:生成客户端执行类的class文件存放目录, -s:生成客户端执行类的源文件存放目录, -p:定义生成类的包名   -verbose:显示生成过程 需要注意的是:无论-d或是-s后的目录必须在文件系统中存在,否则报错。

      将src下连同包一起复制到程序中,编写客户端入口类:

    public class WSClient { private static final Logger LOG = LoggerFactory.getLogger(WSClient.class); private static final String USER_NAME = "admin"; private static final String PASS_WORD = "pass"; public static void main(String[] args) { //方式二:通过wsimport生成客户端代码 LayoutServiceImpl impl = new LayoutServiceImpl(); impl.setHandlerResolver(new HandlerResolver() { @Override public List<Handler> getHandlerChain(PortInfo portInfo) { List<Handler> handlerList = new ArrayList<Handler>(); handlerList.add(new ClientHandler(USER_NAME, PASS_WORD)); return handlerList; } }); try { LayoutService layoutService = impl.getLayoutImpl(); String result_1 = layoutService.sayHello("jack"); LOG.debug("[2]sayHello:" + result_1); OperationResult result_2 = layoutService.addLayout("name", "content"); LOG.debug("[2]addLayout result_succeed:" + result_2.isSucceed()); LOG.debug("[2]addLayout result_msg:" + result_2.getMsg()); } catch (SOAPFaultException e) { LOG.error("SOAPFaultException occurs:{}", e.getMessage()); } }

     该方式的好处是可以像调用本地接口一样调用服务端方法,简单明了。缺点就是会生成一堆文件。

     用户的头信息设置类:

    public class ClientHandler implements SOAPHandler<SOAPMessageContext> { private String username; private String password; public ClientHandler(String username, String password) { this.username = username; this.password = password; } public boolean handleMessage(SOAPMessageContext ctx) { //出站,即客户端发出请求前,添加表头信息 Boolean request_p = (Boolean)ctx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (request_p) { try { SOAPMessage msg = ctx.getMessage(); SOAPEnvelope env = msg.getSOAPPart().getEnvelope(); SOAPHeader hdr = env.getHeader(); if (hdr == null) hdr = env.addHeader(); //添加认证信息头 QName name = new QName("http://service.webservice.com/", "LayoutImpl"); SOAPHeaderElement header = hdr.addHeaderElement(name); SOAPElement userElement = header.addChildElement("username"); userElement.addTextNode(username); SOAPElement passElement = header.addChildElement("password"); passElement.addTextNode(password); msg.saveChanges(); return true; } catch (Exception e) { e.printStackTrace(); } } return false; } @Override public boolean handleFault(SOAPMessageContext context) { // TODO Auto-generated method stub return false; } @Override public void close(MessageContext context) { // TODO Auto-generated method stub } @Override public Set<QName> getHeaders() { // TODO Auto-generated method stub return null; } }

     

    Processed: 0.009, SQL: 9