最近玩了玩 Google 云,上网找了下如何在 Google 云平台 上部署自己的 WebService,还是没有找到中文资料,不过找到了两份比较好的英文资料:
http://googcloudlabs.appspot.com/codelabexercise5.html
https://developers.google.com/appengine/articles/soap?hl=en
按照里边的例子,把前一阵 防XSS跨站脚本攻击的 WebService 部署到了 Goolge Appengine 云平台上,分享一下经验
原 Tomcat 版 Xss 过滤:http://blog.csdn.net/lxfan/article/details/8162257
部署到 Goolge Appengine 上的 Xss 过滤 WebService 地址:http://xssfilter.wegabrow.com/
注:这里我做了一个域名的映射,具体怎么将域名映射到 Google Appengine,大家可以 Google 一下。
下边说一下如何在 Google AppEngine (以下简称GAE)实现WebService。主要是使用 javax.xml.soap 和 JAX-B 来进行 SOAP 交互。
以下是将 AntiSamy Xss Filter 封装成 GAE WebService的具体步骤(开发环境 eclipse):
1. 新建一个GAE项目,定义 WebService 服务类,使用 JDK 6 自带的注释定义方法即可:
@WebService(targetNamespace = "http://www.wegabrow.com/xssfilter") public class AntiSamyFilter { @WebMethod public String Filter(String html) { try { CleanResults results = getAntiSamy().scan(html); return results.getCleanHTML(); } catch (Exception e) { throw new RuntimeException(e); } } @WebMethod public String FilterWithConfig(String html, String config) { try { ByteArrayInputStream stream = new ByteArrayInputStream( config.getBytes("UTF-8")); @SuppressWarnings("deprecation") Policy policy = Policy.getInstance(stream); AntiSamy samy = new AntiSamy(policy); CleanResults results = samy.scan(html); return results.getCleanHTML(); } catch (Exception e) { throw new RuntimeException(e); } } private static AntiSamy antiSamy; static AntiSamy getAntiSamy() throws PolicyException { if (antiSamy == null) { createAntiSamy(); } return antiSamy; } static synchronized void createAntiSamy() throws PolicyException { if (antiSamy == null) { Policy policy = Policy.getInstance(AntiSamyFilter.class .getResource("/antisamy-config.xml")); antiSamy = new AntiSamy(policy); } } }
这里定义了两个WebMethod,Filter 将使用默认配置对Html进行过滤,而 FilterWithConfig 可以使用客户端发送过来的配置进行过滤。
2. 使用 wsgen 工具生成WebService需要用的辅助对象和wsdl文件
按照英文文档里说的,新建一个. sh 文件(文件名无所谓),放在GAE项目的根目录下,右键点击该文件,选择 Open With -> Text Editor,编辑文件内容如下:
class=com.wegabrow.xssfilter.AntiSamyFilter clpth='./war/WEB-INF/classes' resourcedir='./war' outsourcedir='./src' outdir='./war/WEB-INF/classes' wsgen -cp "$clpth" -wsdl -keep -r "$resourcedir" -d "$outdir" -s "$outsourcedir" $class
这里说明一下,.sh 文件是 linux shell 脚本文件,在 Windows 下是不能直接运行的。我也懒的去找 Windows 下的 linux shell 工具,于是干脆就把这个文件移植成 Windows bat 文件(需要将扩展名改成.bat),编辑文件内容如下:
set class=com.wegabrow.xssfilter.AntiSamyFilter set clpth="%CD%\war\WEB-INF\classes" set resourcedir="%CD%\war" set outsourcedir="%CD%\src" set outdir="%CD%\war\WEB-INF\classes" wsgen -cp %clpth% -wsdl -keep -r %resourcedir% -d %outdir% -s %outsourcedir% %class%
在 eclipse 下是无法运行该 bat 的,需要启动 命令提示符,切换到 GAE 项目目录,然后运行该文件。会生成如下结构的 类 与 wsdl:
3. 建立WebService需要用到的 Adapter 类、 Handler 类和 Servlet 类:
AntiSamyFilterAdapter:
public class AntiSamyFilterAdapter { private AntiSamyFilter entityAPI = new AntiSamyFilter(); public FilterResponse Filter(Filter request) { String html = request.getArg0(); String status = entityAPI.Filter(html); FilterResponse response = new FilterResponse(); response.setReturn(status); return response; } public FilterWithConfigResponse FilterWithConfig(FilterWithConfig request) { String html = request.getArg0(); String config = request.getArg1(); String status = entityAPI.FilterWithConfig(html, config); FilterWithConfigResponse response = new FilterWithConfigResponse(); response.setReturn(status); return response; } }Adaper类非常简单,主要就是接受从Xml防序列化的参数,然后调用具体服务对象的方法,将调用结果生成需要序列化返回客户端的对象。
AntiSamyFilterHandler:
public class AntiSamyFilterHandler { private static final String NAMESPACE_URI = "http://www.wegabrow.com/xssfilter"; private static final QName FILTER_QNAME = new QName(NAMESPACE_URI, "Filter"); private static final QName FILTER_WITH_CONFIG_QNAME = new QName( NAMESPACE_URI, "FilterWithConfig"); private MessageFactory messageFactory; private AntiSamyFilterAdapter xssFilterAdapter; public AntiSamyFilterHandler() throws SOAPException { messageFactory = MessageFactory.newInstance(); xssFilterAdapter = new AntiSamyFilterAdapter(); } @SuppressWarnings("rawtypes") public SOAPMessage handleSOAPRequest(SOAPMessage request) throws SOAPException { SOAPBody soapBody = request.getSOAPBody(); Iterator iterator = soapBody.getChildElements(); Object responsePojo = null; while (iterator.hasNext()) { Object next = iterator.next(); if (next instanceof SOAPElement) { SOAPElement soapElement = (SOAPElement) next; QName qname = soapElement.getElementQName(); if (FILTER_QNAME.equals(qname)) { responsePojo = handleFilterRequest(soapElement); break; } else if (FILTER_WITH_CONFIG_QNAME.equals(qname)){ responsePojo = handleFilterWithConfigRequest(soapElement); break; } } } SOAPMessage soapResponse = messageFactory.createMessage(); soapBody = soapResponse.getSOAPBody(); if (responsePojo != null) { JAXB.marshal(responsePojo, new SAAJResult(soapBody)); } else { SOAPFault fault = soapBody.addFault(); fault.setFaultString("Unrecognized SOAP request."); } return soapResponse; } private Object handleFilterRequest(SOAPElement soapElement) { Filter request = JAXB.unmarshal(new DOMSource( soapElement), Filter.class); return xssFilterAdapter.Filter(request); } private Object handleFilterWithConfigRequest(SOAPElement soapElement) { FilterWithConfig request = JAXB.unmarshal(new DOMSource( soapElement), FilterWithConfig.class); return xssFilterAdapter.FilterWithConfig(request); } }Handler的主要功能就是反序列化Xml数据,然后判断调用哪个Apapter方法,再将结果序列化返回。
AntiSamyFilterSerivceServlet:
public class AntiSamyFilterServiceServlet extends HttpServlet { /** * */ private static final long serialVersionUID = 1L; static MessageFactory messageFactory; static AntiSamyFilterHandler soapHandler; static String wsdl; static String xsd; static { try { messageFactory = MessageFactory.newInstance(); soapHandler = new AntiSamyFilterHandler(); wsdl = Utils.getStringFromResource("/AntiSamyFilterService.wsdl") .replace("/n", ""); xsd = Utils.getStringFromResource( "/AntiSamyFilterService_schema1.xsd").replace("/n", ""); } catch (Exception ex) { throw new RuntimeException(ex); } } public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("text/xml;charset=\"utf-8\""); if (req.getParameter("wsdl") != null) { String url = req.getRequestURL().toString(); String respWsdl = wsdl.replace("REPLACE_WITH_ACTUAL_URL", url) // 动态替换服务地址 .replace("REPLACE_WITH_XSD_URL", url + "?xsd"); // 动态替换XSD地址 resp.getWriter().print(respWsdl); } else if (req.getParameter("xsd") != null) { resp.getWriter().print(xsd); } else { resp.setContentType("text/html;charset=\"utf-8\""); resp.getWriter() .print("<html><head><title>xss filter web service</title></head><body>"); resp.getWriter().print("<h1>Xss filter web service.</h1>"); resp.getWriter().print("<div>"); resp.getWriter().print("<a href='?wsdl'>See the wsdl.</a>"); resp.getWriter().print("</div>"); resp.getWriter().print("</body></html>"); } } @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { try { // Get all the headers from the HTTP request MimeHeaders headers = getHeaders(req); // Construct a SOAPMessage from the XML in the request body InputStream is = req.getInputStream(); SOAPMessage soapRequest = messageFactory.createMessage(headers, is); // Handle soapReqest SOAPMessage soapResponse = soapHandler .handleSOAPRequest(soapRequest); // Write to HttpServeltResponse resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("text/xml;charset=\"utf-8\""); OutputStream os = resp.getOutputStream(); soapResponse.writeTo(os); os.flush(); } catch (SOAPException e) { throw new IOException("Exception while creating SOAP message.", e); } } static MimeHeaders getHeaders(HttpServletRequest req) { @SuppressWarnings("rawtypes") Enumeration headerNames = req.getHeaderNames(); MimeHeaders headers = new MimeHeaders(); while (headerNames.hasMoreElements()) { String headerName = (String) headerNames.nextElement(); String headerValue = req.getHeader(headerName); StringTokenizer values = new StringTokenizer(headerValue, ","); while (values.hasMoreTokens()) { headers.addHeader(headerName, values.nextToken().trim()); } } return headers; } }Servlet 处理的就比较简单了,就是具体的输入输出。
这里对原版程序进行了一些改进,一个是支持 Get 方法获取 wsdl 文件。在本项目中,我把 wsdl 和 xsd 文件 copy 到了 src 目录下,并对 wsdl 文件中的 xsd 地址做了处理。可以使用 http://xssfilter.wegabrow.com/XssFilter.asmx?wsdl 的方式获取到wsdl文件,并在访问wsdl地址的时候,动态的替换掉wsdl文件中的服务地址和xsd文件的地址,这样无论服务部署到哪,客户端都可以简单而正确的得到服务的实际地址,避免了调试和发布需要修改wsdl文件中的地址的麻烦。还提供了默认的服务视图。
另外新加的 Utils 类:
public class Utils { /** * 读取资源文件 * * @param user * @return */ public static String getStringFromResource(String resource) { InputStream inputStream = Utils.class.getResourceAsStream(resource); String result = readStream(inputStream); return result; } /** * 从数据流中读取字符串 * * @param input * @return */ public static String readStream(InputStream input) { String output = ""; try { BufferedReader inputReader = new BufferedReader( new InputStreamReader(input, "UTF-8")); StringBuffer buffer = new StringBuffer(); String text; while ((text = inputReader.readLine()) != null) { buffer.append(text + "/n"); } output = buffer.toString(); } catch (IOException ioException) { System.err.println("File Error!"); } return output; } }
到此就完成了制作WebService的全部工作,发布到 GAE 平台上,运行成功。
项目源码下载地址:
http://download.csdn.net/detail/lxfan/4758624