JDK1.6自带的Jax-Ws详解(含权限检查完整解决方案)
2010-11-01 20:23
267 查看
最近参与开发一个防火墙项目,主要负责Web Services,因为实际情况选用了JDK1.6自带的Jax-Ws,它比当前比较主流的CXF(已经替代了以前的XFIRE)轻量,不依赖任何容器,一个简单的Java Application就可以发布Web Services,这是一件多么令人兴奋的事,但是权限检查却非常费力,查看了很多解决方案,他们大多数是针对容器上部署的Web Services的权限检查......最后终于在官网文档(https://xwss.dev.java.net/Securing_JAVASE6_WebServices.html)上找到了解决思路,通过不断尝试得到一个比较完整的Jax-Ws权限检查方案.
要使用Jax-Ws,首先得引入相关Jar包(webservices-api.jar、webservices-rt.jar、webservices-tools.jar),下面是一个简单定义Web Services接口的实现类:
然后是发布Web Services的main方法:
其中192.168.1.109为本机的IP,8888为发布Web Services的端口号,一般运行这个main方法(加上下文的权限检查文件后),你的Web Services就算发布成功了(通过浏览器访问http://192.168.1.109:8888/fimas?wsdl即可看到wsdl文件,说明发布成功),但是如果你的接口类Processor里使用了自定义的异常类,则还得操作如下生成异常Bean(运行main才不会出错):首先在你的java项目根目录下新建一个文件夹(名字为wsdl,它和src同级)存放wsdl文件,然后通过cmd----》cd XXXXXXXXXX进入你的java项目根路径,然后执行命令:
,这样通过JDK自带的wsgen工具就会自动在项目中生成异常Bean类和wsdl文件,这样Web Services就会成功发布了.
下面才是重点关于权限检查:
在Processor.java同级下加入上面的SecurityEnvironmentHandler.java和SecurityHandler.java两个文件,还有在java项目根路径下新建一个包(包名为META-INF,它和src同级),META-INF里面放着两个文件user-pass-authenticate-client.xml(里面的内容是:
这样一个含身份验证的Web Services服务器端已经构建成功。
客户端:
首先访问http://192.168.1.109:8888/fimas?wsdl看看服务器端是否发布成功Web Services,然后cmd---》cd XXXXXXXX进入本java项目某一个包下通过JDK工具生成本地web services调用文件,执行
客户端的权限检查文件(有部分代码和服务器端代码不一样):
在该java项目根路径下新建一个文件夹META-INF(它仍然和src同级) ,其内容与服务器端的META-INF内容一样,最后是客户端测试文件:
服务器端数据库权限控制表内必须有username:admin password:admin该用户,客户端才能成功调用服务器端发布的Web Services(即客户端通过服务器端授权(密码经过MD5加密)).
要使用Jax-Ws,首先得引入相关Jar包(webservices-api.jar、webservices-rt.jar、webservices-tools.jar),下面是一个简单定义Web Services接口的实现类:
import javax.annotation.Resource; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.jws.WebParam.Mode; import javax.jws.soap.SOAPBinding; import javax.xml.ws.WebServiceContext; import com.sun.xml.wss.SubjectAccessor; @WebService(name="Processor",targetNamespace="http://www.qingyh.fimas.command",serviceName="Processor") @SOAPBinding(style=SOAPBinding.Style.RPC) public class Processor { @Resource WebServiceContext context; @WebMethod(operationName="start",action="start",exclude=false) @WebResult(name="returnStart") public String start() { try { System.out.println("/nRequester Subject " + SubjectAccessor.getRequesterSubject(context)); } catch (Exception e) { throw new RuntimeException(e); } //................To Achieve In Detail..................... return "数据"; } @WebMethod(operationName="getListernerByFw",action="getListernerByFw",exclude=false) @WebResult(name="returnGetListernerByFw") public String getListernerByFw(@WebParam(partName="id",mode=Mode.IN)String id,@WebParam(partName="type",mode=Mode.IN)String type) { try { System.out.println("/nRequester Subject " + SubjectAccessor.getRequesterSubject(context)); } catch (Exception e) { throw new RuntimeException(e); } //................To Achieve In Detail..................... return type+"=。。。。。。"; } @WebMethod(operationName="executeCMD",action="executeCMD",exclude=false) @WebResult(name="returnExecuteCMD") public String execute(@WebParam(partName="ucmd",mode=Mode.IN)String ucmd,@WebParam(partName="firewallID",mode=Mode.IN)String firewallID) { try { System.out.println("/nRequester Subject " + SubjectAccessor.getRequesterSubject(context)); } catch (Exception e) { throw new RuntimeException(e); } //................To Achieve In Detail..................... return "Congratulations,your"+ucmd+"has executed successfully on firewall"+firewallID; } }
然后是发布Web Services的main方法:
import java.util.LinkedList; import java.util.List; import javax.xml.ws.Binding; import javax.xml.ws.Endpoint; import javax.xml.ws.handler.Handler; public class StartServer { public static void main(String[] args) { Endpoint endpoint = Endpoint.create(new Processor()); Binding binding = endpoint.getBinding(); List<Handler> handlerChain = new LinkedList<Handler>(); handlerChain.add(new SecurityHandler("server")); binding.setHandlerChain(handlerChain); endpoint.publish("http://192.168.1.109:8888/fimas"); System.out.println("Fimas is Working:http://192.168.1.109:8888/fimas"); } }
其中192.168.1.109为本机的IP,8888为发布Web Services的端口号,一般运行这个main方法(加上下文的权限检查文件后),你的Web Services就算发布成功了(通过浏览器访问http://192.168.1.109:8888/fimas?wsdl即可看到wsdl文件,说明发布成功),但是如果你的接口类Processor里使用了自定义的异常类,则还得操作如下生成异常Bean(运行main才不会出错):首先在你的java项目根目录下新建一个文件夹(名字为wsdl,它和src同级)存放wsdl文件,然后通过cmd----》cd XXXXXXXXXX进入你的java项目根路径,然后执行命令:
wsgen -cp ./bin -r ./wsdl -s ./src -d ./bin -wsdl Processor的包名.Processor
,这样通过JDK自带的wsgen工具就会自动在项目中生成异常Bean类和wsdl文件,这样Web Services就会成功发布了.
下面才是重点关于权限检查:
import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import com.sun.xml.wss.impl.callback.PasswordCallback; import com.sun.xml.wss.impl.callback.PasswordValidationCallback; import com.sun.xml.wss.impl.callback.UsernameCallback; public class SecurityEnvironmentHandler implements CallbackHandler { private static final UnsupportedCallbackException unsupported = new UnsupportedCallbackException( null, "Unsupported Callback Type Encountered"); /** Creates a new instance of SecurityEnvironmentHandler */ public SecurityEnvironmentHandler(String arg) { } public String getPassword(String userName) { //..............根据用户名从数据库获得相应的密码........................... return ""; } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof PasswordValidationCallback) { PasswordValidationCallback cb = (PasswordValidationCallback) callbacks[i]; if (cb.getRequest() instanceof PasswordValidationCallback.PlainTextPasswordRequest) { cb.setValidator(new PlainTextPasswordValidator()); } else if (cb.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) { PasswordValidationCallback.DigestPasswordRequest request = (PasswordValidationCallback.DigestPasswordRequest) cb .getRequest(); String username = request.getUsername(); String pwd = getPassword(username); if (pwd != "" && pwd != null) { request.setPassword(pwd); cb.setValidator(new PasswordValidationCallback.DigestPasswordValidator()); } } } else if (callbacks[i] instanceof UsernameCallback) { UsernameCallback cb = (UsernameCallback) callbacks[i]; String username = (String) cb.getRuntimeProperties().get( com.sun.xml.wss.XWSSConstants.USERNAME_PROPERTY); System.out.println("Got Username......... : " + username); cb.setUsername(username); } else if (callbacks[i] instanceof PasswordCallback) { PasswordCallback cb = (PasswordCallback) callbacks[i]; String password = (String) cb.getRuntimeProperties().get( com.sun.xml.wss.XWSSConstants.PASSWORD_PROPERTY); System.out.println("Got Password......... : " + password); cb.setPassword(getMd5(password)); } else { throw unsupported; } } } private class PlainTextPasswordValidator implements PasswordValidationCallback.PasswordValidator { public boolean validate(PasswordValidationCallback.Request request) throws PasswordValidationCallback.PasswordValidationException { PasswordValidationCallback.PlainTextPasswordRequest plainTextRequest = (PasswordValidationCallback.PlainTextPasswordRequest) request; String pwd = getPassword(plainTextRequest.getUsername()); if (pwd != null && pwd != "" && pwd.equals(getMd5(plainTextRequest.getPassword()))) { return true; } return false; } } /** * MD5加密 * * @param password * @return */ public String getMd5(String password) { String Md5Password = ""; try { java.security.MessageDigest encryptMethod = java.security.MessageDigest .getInstance("MD5"); encryptMethod.update(password.getBytes()); Md5Password = byte2hex(encryptMethod.digest()); } catch (Exception e) { e.printStackTrace(); } return Md5Password; } /** * 二进制转字符串 * * @param b * @return */ public String byte2hex(byte[] b) { String hs = ""; String stmp = ""; for (int n = 0; n < b.length; n++) { stmp = (java.lang.Integer.toHexString(b & 0XFF)); if (stmp.length() == 1) hs = hs + "0" + stmp; else hs = hs + stmp; } return hs.toLowerCase(); } }
import java.io.InputStream; import java.util.Set; import java.util.HashSet; import javax.xml.namespace.QName; import javax.xml.soap.SOAPMessage; import javax.xml.ws.WebServiceException; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; import com.sun.xml.wss.ProcessingContext; import com.sun.xml.wss.SubjectAccessor; import com.sun.xml.wss.XWSSProcessorFactory; import com.sun.xml.wss.XWSSProcessor; import com.sun.xml.wss.XWSSecurityException; public class SecurityHandler implements SOAPHandler<SOAPMessageContext> { XWSSProcessor sprocessor = null; String clientOrServer = null; XWSSProcessor cprocessor = null; /** Creates a new instance of SecurityHandler */ public SecurityHandler(String cOrs) { InputStream serverConfig = null; InputStream clientConfig = null; this.clientOrServer = cOrs; try { if ("client".equals(this.clientOrServer)) { // read client side security config clientConfig = new java.io.FileInputStream( new java.io.File("META-INF/user-pass-authenticate-client.xml")); //clientConfig =getClass().getResourceAsStream("/cn/com/letour/fimascontroller/META-INF/user-pass-authenticate-client.xml"); // Create a XWSSProcessFactory. XWSSProcessorFactory factory = XWSSProcessorFactory .newInstance(); cprocessor = factory.createProcessorForSecurityConfiguration( clientConfig, new SecurityEnvironmentHandler("client")); clientConfig.close(); } else { // read server side security configuration serverConfig = new java.io.FileInputStream( new java.io.File("META-INF/user-pass-authenticate-server.xml")); //serverConfig = getClass().getResourceAsStream("/cn/com/letour/fimascontroller/META-INF/user-pass-authenticate-server.xml"); // Create a XWSSProcessFactory. XWSSProcessorFactory factory = XWSSProcessorFactory .newInstance(); sprocessor = factory.createProcessorForSecurityConfiguration( serverConfig, new SecurityEnvironmentHandler("server")); serverConfig.close(); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } public Set<QName> getHeaders() { QName securityHeader = new QName( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "wsse"); HashSet<QName> headers = new HashSet<QName>(); headers.add(securityHeader); return headers; } public boolean handleFault(SOAPMessageContext messageContext) { return true; } public boolean handleMessage(SOAPMessageContext messageContext) { if ("client".equals(this.clientOrServer)) { secureClient(messageContext); } else { secureServer(messageContext); } return true; } public void close(MessageContext messageContext) { } @SuppressWarnings("unchecked") private void secureServer(SOAPMessageContext messageContext) { Boolean outMessageIndicator = (Boolean) messageContext .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); SOAPMessage message = messageContext.getMessage(); if (outMessageIndicator.booleanValue()) { System.out.println("/nOutbound SOAP:"); // do nothing.... return; } else { System.out.println("/nInbound SOAP:"); // verify the secured message. try { ProcessingContext context = sprocessor .createProcessingContext(message); context.getExtraneousProperties().putAll(messageContext); context.setSOAPMessage(message); SOAPMessage verifiedMsg = null; verifiedMsg = sprocessor.verifyInboundMessage(context); System.out.println("/nRequester Subject " + SubjectAccessor.getRequesterSubject(context)); messageContext.setMessage(verifiedMsg); } catch (XWSSecurityException ex) { // create a Message with a Fault in it // messageContext.setMessage(createFaultResponse(ex)); ex.printStackTrace(); throw new WebServiceException(ex); } catch (Exception ex) { ex.printStackTrace(); throw new WebServiceException(ex); } } } @SuppressWarnings("unused") private SOAPMessage createFaultResponse(XWSSecurityException ex) { // TODO: add code here return null; } private void secureClient(SOAPMessageContext messageContext) { Boolean outMessageIndicator = (Boolean) messageContext .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); SOAPMessage message = messageContext.getMessage(); if (outMessageIndicator.booleanValue()) { System.out.println("/nOutbound SOAP:"); ProcessingContext context; try { context = cprocessor.createProcessingContext(message); context.setSOAPMessage(message); SOAPMessage secureMsg = cprocessor .secureOutboundMessage(context); secureMsg.writeTo(System.out); messageContext.setMessage(secureMsg); } catch (XWSSecurityException ex) { ex.printStackTrace(); throw new RuntimeException(ex); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } return; } else { System.out.println("/nInbound SOAP:"); // do nothing return; } } }
在Processor.java同级下加入上面的SecurityEnvironmentHandler.java和SecurityHandler.java两个文件,还有在java项目根路径下新建一个包(包名为META-INF,它和src同级),META-INF里面放着两个文件user-pass-authenticate-client.xml(里面的内容是:
<!-- Copyright 2004 Sun Microsystems, Inc. All rights reserved. SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. --> <xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config" dumpMessages="true" > <xwss:UsernameToken digestPassword="false"/> </xwss:SecurityConfiguration>)和user-pass-authenticate-server.xml(里面的内容是:
<!-- Copyright 2004 Sun Microsystems, Inc. All rights reserved. SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. --> <xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config" dumpMessages="true" > <xwss:RequireUsernameToken passwordDigestRequired="false"/> </xwss:SecurityConfiguration>)
这样一个含身份验证的Web Services服务器端已经构建成功。
客户端:
首先访问http://192.168.1.109:8888/fimas?wsdl看看服务器端是否发布成功Web Services,然后cmd---》cd XXXXXXXX进入本java项目某一个包下通过JDK工具生成本地web services调用文件,执行
wsimport -d ./bin -s ./src -p 你的包名 http://192.168.1.109:8888/fimas?wsdl
客户端的权限检查文件(有部分代码和服务器端代码不一样):
import java.io.FileInputStream; import java.net.URL; import java.util.HashSet; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.soap.SOAPMessage; import javax.xml.ws.WebServiceException; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; import com.sun.xml.wss.ProcessingContext; import com.sun.xml.wss.SubjectAccessor; import com.sun.xml.wss.XWSSProcessor; import com.sun.xml.wss.XWSSProcessorFactory; import com.sun.xml.wss.XWSSecurityException; @SuppressWarnings("restriction") public class SecurityHandler implements SOAPHandler<SOAPMessageContext> { String username; String password; XWSSProcessor sprocessor = null; String clientOrServer = null; XWSSProcessor cprocessor = null; /** Creates a new instance of SecurityHandler */ public SecurityHandler(String cOrs,String username,String password) { this.username=username; this.password=password; FileInputStream serverConfig = null; FileInputStream clientConfig = null; this.clientOrServer = cOrs; try { if ("client".equals(this.clientOrServer)) { //read client side security config clientConfig = new java.io.FileInputStream( new java.io.File("META-INF/user-pass-authenticate-client.xml")); //Create a XWSSProcessFactory. XWSSProcessorFactory factory = XWSSProcessorFactory.newInstance(); cprocessor = factory.createProcessorForSecurityConfiguration( clientConfig, new SecurityEnvironmentHandler("client",username,password)); clientConfig.close(); } else { //read server side security configuration serverConfig = new java.io.FileInputStream( new java.io.File("META-INF/user-pass-authenticate-server.xml")); //Create a XWSSProcessFactory. XWSSProcessorFactory factory = XWSSProcessorFactory.newInstance(); sprocessor = factory.createProcessorForSecurityConfiguration( serverConfig, new SecurityEnvironmentHandler("server",username,password)); serverConfig.close(); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } public Set<QName> getHeaders() { QName securityHeader = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "wsse"); HashSet<QName> headers = new HashSet<QName>(); headers.add(securityHeader); return headers; } @SuppressWarnings("restriction") public boolean handleFault(SOAPMessageContext messageContext) { return true; } public boolean handleMessage(SOAPMessageContext messageContext) { if ("client".equals(this.clientOrServer)) { secureClient(messageContext); } else { secureServer(messageContext); } return true; } public void close(MessageContext messageContext) {} private void secureServer(SOAPMessageContext messageContext) { Boolean outMessageIndicator = (Boolean) messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY); SOAPMessage message = messageContext.getMessage(); if (outMessageIndicator.booleanValue()) { System.out.println("/nOutbound SOAP:"); // do nothing.... return; } else { System.out.println("/nInbound SOAP:"); //verify the secured message. try{ ProcessingContext context = sprocessor.createProcessingContext(message); context.setSOAPMessage(message); SOAPMessage verifiedMsg= null; verifiedMsg= sprocessor.verifyInboundMessage(context); System.out.println("/nRequester Subject " + SubjectAccessor.getRequesterSubject(context)); messageContext.setMessage(verifiedMsg); } catch (XWSSecurityException ex) { //create a Message with a Fault in it //messageContext.setMessage(createFaultResponse(ex)); ex.printStackTrace(); throw new WebServiceException(ex); } catch(Exception ex){ ex.printStackTrace(); throw new WebServiceException(ex); } } } @SuppressWarnings("unused") private SOAPMessage createFaultResponse(XWSSecurityException ex) { // TODO: add code here return null; } @SuppressWarnings("unchecked") private void secureClient(SOAPMessageContext messageContext) { Boolean outMessageIndicator = (Boolean) messageContext.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY); SOAPMessage message = messageContext.getMessage(); if (outMessageIndicator.booleanValue()) { System.out.println("/nOutbound SOAP:"); ProcessingContext context; try { context = cprocessor.createProcessingContext(message); context.setSOAPMessage(message); SOAPMessage secureMsg = cprocessor.secureOutboundMessage(context); context.getExtraneousProperties().putAll(messageContext); secureMsg.writeTo(System.out); messageContext.setMessage(secureMsg); } catch (XWSSecurityException ex) { ex.printStackTrace(); throw new RuntimeException(ex); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } return; } else { System.out.println("/nInbound SOAP:"); //do nothing return; } } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import com.sun.xml.wss.impl.callback.PasswordCallback; import com.sun.xml.wss.impl.callback.PasswordValidationCallback; import com.sun.xml.wss.impl.callback.UsernameCallback; public class SecurityEnvironmentHandler implements CallbackHandler { String username; String password; private static final UnsupportedCallbackException unsupported = new UnsupportedCallbackException(null, "Unsupported Callback Type Encountered"); /** Creates a new instance of SecurityEnvironmentHandler */ public SecurityEnvironmentHandler(String arg, String username, String password) { this.username = username; this.password = password; } private String readLine() throws IOException { return new BufferedReader(new InputStreamReader(System.in)).readLine(); } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof PasswordValidationCallback) { PasswordValidationCallback cb = (PasswordValidationCallback) callbacks[i]; if (cb.getRequest() instanceof PasswordValidationCallback.PlainTextPasswordRequest) { cb.setValidator(new PlainTextPasswordValidator()); } else if (cb.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) { PasswordValidationCallback.DigestPasswordRequest request = (PasswordValidationCallback.DigestPasswordRequest) cb .getRequest(); String username1 = request.getUsername(); if (username.equals(username1)) { request.setPassword(password); cb.setValidator(new PasswordValidationCallback.DigestPasswordValidator()); } } } else if (callbacks[i] instanceof UsernameCallback) { UsernameCallback cb = (UsernameCallback) callbacks[i]; // String username = // (String)cb.getRuntimeProperties().get(com.sun.xml.wss.XWSSConstants.USERNAME_PROPERTY); // String username="Fimas"; System.out.println("Got Username......... : " + username); cb.setUsername(username); } else if (callbacks[i] instanceof PasswordCallback) { PasswordCallback cb = (PasswordCallback) callbacks[i]; // String password = // (String)cb.getRuntimeProperties().get(com.sun.xml.wss.XWSSConstants.PASSWORD_PROPERTY); // String password="samiF"; System.out.println("Got Password......... : " + password); cb.setPassword(password); } else { throw unsupported; } } } private class PlainTextPasswordValidator implements PasswordValidationCallback.PasswordValidator { public boolean validate(PasswordValidationCallback.Request request) throws PasswordValidationCallback.PasswordValidationException { PasswordValidationCallback.PlainTextPasswordRequest plainTextRequest = (PasswordValidationCallback.PlainTextPasswordRequest) request; if (username.equals(plainTextRequest.getUsername()) && password.equals(plainTextRequest.getPassword())) { return true; } return false; } } }
在该java项目根路径下新建一个文件夹META-INF(它仍然和src同级) ,其内容与服务器端的META-INF内容一样,最后是客户端测试文件:
import java.util.ArrayList; import java.util.List; import javax.xml.ws.BindingProvider; import javax.xml.ws.handler.Handler; public class RunClient { public static void main(String[] args) { Processor_Service service=new Processor_Service(); Processor processor=service.getProcessorPort(); List<Handler> chain=new ArrayList<Handler>(); chain.add(new SecurityHandler("client","admin","admin")); ((BindingProvider)processor).getBinding().setHandlerChain(chain); System.out.println(processor.getListernerByFw("100","it is a test!")); System.out.println(processor.execute("UCMD","200")); } }
服务器端数据库权限控制表内必须有username:admin password:admin该用户,客户端才能成功调用服务器端发布的Web Services(即客户端通过服务器端授权(密码经过MD5加密)).
相关文章推荐
- JAX-WS开发webservice接口的注解的应用和注解的详解
- webservice使用JDK自带的jax-ws创建并发布一个简单的webservice
- 从jdk自带的jax-ws说WebService
- JAX-WS - Soap详解
- 详解Android通知栏沉浸式/透明化完整解决方案
- Web Service学习小结——基于JDK自带JAX-WS实现的web service
- JAX-WS使用Handler实现简单的WebService权限验证
- JAX-WS - Soap详解
- JAX-WS - Handler详解
- Java中出现Jax-ws的解决方案
- 基于qmail的完整WEBMAIL解决方案安装详解
- WebService基本入门(基于jdk自带的jax-ws)
- .woff字体上传后无效的解决方案,同时包含:@font-face 未能完成opentype嵌入权限检查。权限必须是可安装的解决方案
- java6 自带的web服务类 JAX-WS
- JAX-WS使用Handler实现简单的WebService权限验证
- JAX-WS开发webservice接口的注解的应用和注解的详解
- 详解Docker创建php开发环境遇到的权限问题解决方案
- XP局域网访问无权限、不能互相访问问题的完整解决方案
- JAX-WS HandlerChain使用详解
- iOS10 权限崩溃问题详解及解决方案