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

webservice设计

发布时间:2020-12-16 23:35:12 所属栏目:安全 来源:网络整理
导读:?? 因此好的WebService接口,应该从下面几个方面仔细考虑: 一. 参数 (1) 参数应该直接使用简单的数据类型(POCO、POJO),甚至时间类型都可以考虑用string,只要双方约束好时间字符串的格式。 (2) 如果参数个数超过3个,那就需要考虑设计一个Class了,避免
??
因此好的WebService接口,应该从下面几个方面仔细考虑:

一. 参数
(1) 参数应该直接使用简单的数据类型(POCO、POJO),甚至时间类型都可以考虑用string,只要双方约束好时间字符串的格式。
(2) 如果参数个数超过3个,那就需要考虑设计一个Class了,避免参数列表过长,当然这没有硬性规定。
(3) 设计统一的参数规则。比如对外提供的查询接口就要考虑分页相关的数据。保证类似的接口都有统一的参数定义,形成习惯是提升效率最好方式。
? ? ? 业务参数和非业务参数应该分开,比如分页的数据就可以抽象出基类。


二. 异常
(1) 使用框架中定义的Exception类型,比如:SoapException,FaultException(WCF)。
(2) 尽量避免将异常定义在返回值中,通过返回值定义错误那么无论服务端还是客户端都要写很多if ... else 分支。
(3) 系统异常和业务异常要区分好,比如使用 SoapException 可以用 Code 来区分,比如:System.Error 表示系统错误,Bussiness.Error 表示业务错误。
(4) 补充:.net framework ?如果没有包装那么默认有两种 fautCode: ?soap:Client 和 soap:Server。假设客户端传入BadRequest 基本就是 soap:Client 错误,其他?没有自定义code的则就是 soap:Server 的错误。

三. 安全
无论何时都要保证系统的安全性,我觉得安全也分系统安全和业务安全两种:
(1) 系统安全主要是指客户端的认证授权,调用次数(需要考虑会不会拖垮业务系统) 等
(2) 业务安全主要是指数据查询/操作权限,当然这个主要是从业务角度考虑的。


四. 日志
日志可以方便排查错误,还可以通过日志来分析服务基本信息(比如:调用次数,失败次数等),必要时还可以通过日志来进行重试。
另外要考虑开发的便捷,设计统一的日志拦截处理。

以 WebService Application (.NET 3.5) 为例,记录几种常用的编程技巧。
原始的 WebService 如下:

[csharp] view plain copy print ?

  1. using?System;??
  2. using?System.Collections.Generic;??
  3. using?System.Linq;??
  4. using?System.Web;??
  5. using?System.Web.Services;??
  6. using?WebService1.Entity;??
  7. using?WebService1.Service;??
  8. using?System.Web.Services.Protocols;??
  9. ??
  10. namespace?WebService1??
  11. {??
  12. ????[WebService(Namespace?=?"http://tempuri.org/")]??
  13. ????[WebServiceBinding(ConformsTo?=?WsiProfiles.BasicProfile1_1)]??
  14. ????[System.ComponentModel.ToolboxItem(false)]??
  15. ????public?class?Service1?:?System.Web.Services.WebService??
  16. ????{??
  17. ????????[WebMethod]?????????
  18. ????????public?PageResult<Order>?QueryOrder(Query<OrderCondition>?queryInfo)??
  19. ????????{??
  20. ????????????OrderService?service?=?new?OrderService();??
  21. ????????????return?service.Query(queryInfo);??
  22. ????????}??
  23. ????}??
  24. }??

PageResult<T>,Query<T> ?将统一的业务部分抽取出来,这样定义其他的业务对象就能简化了。

[html] view plain copy print ?

  1. using?System;??
  2. using?System.Collections.Generic;??
  3. ??
  4. namespace?WebService1.Entity??
  5. {??
  6. ????[Serializable]??
  7. ????public?class?PageResult<T>??
  8. ????{??
  9. ????????public?int?PageNo?{?get;?set;?}??
  10. ????????public?int?PageSize?{?get;?set;?}??
  11. ????????public?int?TotalCount?{?get;?set;?}??
  12. ????????public?int?PageCount?{?get;?set;?}??
  13. ????????public?bool?HasNextPage?{?get;?set;?}??
  14. ????????public?List<T>?Data?{?get;?set;?}??
  15. ????}??
  16. }??
[csharp] view plain copy print ?

  1. using?System;??
  2. using?System.Collections.Generic;??
  3. ??
  4. namespace?WebService1.Entity??
  5. {??
  6. ????[Serializable]??
  7. ????public?class?Query<T>??
  8. ????{??
  9. ????????public?int?PageNo?{?get;?set;?}??
  10. ????????public?int?PageSize?{?get;?set;?}??
  11. ????????public?T?Condition?{?get;?set;?}??
  12. ????}??
  13. }??

跳过业务处理部分,来关注一下应用框架考虑的日志和安全拦截。可以利用 .NET framework 的 Soap Extensions ( msdn) ?很容易地实现对 WebMethod 的 AOP。
Soap Extensions 可以通过两种方式“注入”: 自定义Atrribute 或者通过 Web.config 里的?soapExtensionTypes 进行声明。

TraceExtension 的实现:
[csharp] view plain copy print ?

  1. using?System;??
  2. using?System.Collections.Generic;??
  3. using?System.Linq;??
  4. using?System.Web;??
  5. using?System.IO;??
  6. using?System.Web.Services.Protocols;??
  7. using?log4net;??
  8. using?System.Xml;??
  9. ??
  10. namespace?WebService1.Common??
  11. {??
  12. ????public?class?TraceExtension?:?SoapExtension??
  13. ????{??
  14. ????????private?ILog?logger?=?LogManager.GetLogger(typeof(TraceExtension));??
  15. ??
  16. ????????Stream?oldStream;??
  17. ????????Stream?newStream;??
  18. ??????????
  19. ????????public?override?System.IO.Stream?ChainStream(System.IO.Stream?stream)??
  20. ????????{??
  21. ????????????oldStream?=?stream;??
  22. ????????????newStream?=?new?MemoryStream();??
  23. ????????????return?newStream;??
  24. ????????}??
  25. ??
  26. ????????public?override?void?ProcessMessage(SoapMessage?message)??
  27. ????????{??
  28. ????????????switch?(message.Stage)??
  29. ????????????{??
  30. ????????????????case?SoapMessageStage.BeforeDeserialize:??
  31. ??????????????????????
  32. ????????????????????log4net.ThreadContext.Properties["ip"]?=?HttpContext.Current.Request.UserHostAddress;??
  33. ????????????????????log4net.ThreadContext.Properties["action"]?=?message.Action;??
  34. ??
  35. ????????????????????WriteInput(message);??
  36. ????????????????????break;??
  37. ????????????????case?SoapMessageStage.AfterDeserialize:??
  38. ????????????????????break;??
  39. ????????????????case?SoapMessageStage.BeforeSerialize:??
  40. ????????????????????break;??
  41. ????????????????case?SoapMessageStage.AfterSerialize:??
  42. ????????????????????WriteOutput(message);??
  43. ????????????????????break;??
  44. ????????????????default:??
  45. ????????????????????throw?new?Exception("Invalid?Stage");??
  46. ????????????}??
  47. ????????}??
  48. ??
  49. ????????public?override?object?GetInitializer(Type?serviceType)??
  50. ????????{??
  51. ????????????return?null;??
  52. ????????}??
  53. ??
  54. ????????public?override?object?GetInitializer(LogicalMethodInfo?methodInfo,?SoapExtensionAttribute?attr)??
  55. ????????{??
  56. ????????????return?null;??
  57. ????????}??
  58. ??
  59. ????????public?override?void?Initialize(object?initializer)??
  60. ????????{??
  61. ????????????//filename?=?(string)initializer;??
  62. ????????}??
  63. ??
  64. ????????public?void?WriteOutput(SoapMessage?message)??
  65. ????????{??
  66. ????????????string?soapString?=?(message?is?SoapServerMessage)???"SoapResponse"?:?"SoapRequest";??
  67. ????????????string?content?=?GetContent(newStream);??
  68. ????????????//?为了Format?XML,如果从性能考虑应该去掉此处的处理??
  69. ????????????if?(!string.IsNullOrEmpty(content))??
  70. ????????????{??
  71. ????????????????XmlDocument?xmlDoc?=?new?XmlDocument();??
  72. ????????????????xmlDoc.LoadXml(content);??
  73. ????????????????using?(StringWriter?sw?=?new?StringWriter())??
  74. ????????????????{??
  75. ????????????????????using?(XmlTextWriter?xtw?=?new?XmlTextWriter(sw))??
  76. ????????????????????{??
  77. ????????????????????????xtw.Formatting?=?Formatting.Indented;??
  78. ????????????????????????xmlDoc.WriteTo(xtw);??
  79. ????????????????????????content?=?sw.ToString();??
  80. ????????????????????}??
  81. ????????????????}??
  82. ????????????}??
  83. ??
  84. ????????????logger.Info(soapString?+?":n"?+?content);??
  85. ??
  86. ????????????Copy(newStream,?oldStream);??
  87. ????????}??
  88. ??
  89. ????????public?void?WriteInput(SoapMessage?message)??
  90. ????????{??
  91. ????????????Copy(oldStream,?newStream);??
  92. ??
  93. ????????????string?soapString?=?(message?is?SoapServerMessage)???"SoapRequest"?:?"SoapResponse";??
  94. ????????????string?content?=?GetContent(newStream);??
  95. ????????????logger.Info(soapString?+?":n"?+?content);??
  96. ????????}??
  97. ??
  98. ????????void?Copy(Stream?from,?Stream?to)??
  99. ????????{??
  100. ????????????TextReader?reader?=?new?StreamReader(from);??
  101. ????????????TextWriter?writer?=?new?StreamWriter(to);??
  102. ????????????writer.WriteLine(reader.ReadToEnd());??
  103. ????????????writer.Flush();??
  104. ????????}??
  105. ??
  106. ????????string?GetContent(Stream?stream)??
  107. ????????{??
  108. ????????????stream.Position?=?0;??
  109. ????????????TextReader?reader?=?new?StreamReader(stream);??
  110. ????????????string?content?=?reader.ReadToEnd();??
  111. ????????????stream.Position?=?0;??
  112. ????????????return?content;??
  113. ????????}??
  114. ??
  115. ????}??
  116. ??
  117. }??
TraceAttribute 实现如下:
[csharp] view plain copy print ?

  1. using?System;??
  2. using?System.Web.Services.Protocols;??
  3. ??
  4. namespace?WebService1.Common??
  5. {??
  6. ????[AttributeUsage(AttributeTargets.Method)]??
  7. ????public?class?TraceAttribute?:?SoapExtensionAttribute??
  8. ????{??
  9. ????????private?int?priority?=?0;??
  10. ????????public?override?Type?ExtensionType??
  11. ????????{??
  12. ????????????get?{?return?typeof(TraceExtension);?}??
  13. ????????}??
  14. ??
  15. ????????public?override?int?Priority??
  16. ????????{??
  17. ????????????get?{?return?priority;?}??
  18. ????????????set?{?priority?=?value;?}??
  19. ????????}??
  20. ????}??
  21. }??

其中 TraceExtension 利用 log4net 来记录调用 WebMethod 的Request 和 Response,还包括 ip 和 Action(Action其实对应的 WebMethod)
对应的 log4net 配置如下:
[html] view plain copy print ?

  1. <log4net>??
  2. ????<appender?name="RollingFileAppender"?type="log4net.Appender.RollingFileAppender">??
  3. ????????<param?name="File"?value="F:ProgrammingVSProject2008WebServiceSampleWebService1WebService1Logsservice.log"/>??
  4. ???????????????????????<param?name="DatePattern"?value=".yyyy-MM-dd'.log'"?/>??
  5. ????????<param?name="AppendToFile"?value="true"/>??
  6. ????????<param?name="MaxSizeRollBackups"?value="10"/>??
  7. ????????<param?name="MaximumFileSize"?value="5MB"/>??
  8. ????????<param?name="RollingStyle"?value="Date"/>??
  9. ????????<param?name="StaticLogFileName"?value="false"/>??
  10. ????????<layout?type="log4net.Layout.PatternLayout">??
  11. ????????????<param?name="ConversionPattern"?value="%d?[%t]?%-5p?[%property{ip}]?[%property{action}]?-?%m%n"/>??
  12. ????????</layout>??
  13. ????</appender>??
  14. ????<root>??
  15. ????????<level?value="DEBUG"/>??
  16. ????????<appender-ref?ref="RollingFileAppender"/>??
  17. ????</root>??
  18. </log4net>??

那么 WebMethod 只要加上 [Trace] 特性,就可以开启日志记录功能。
[csharp] view plain copy print ?

  1. [WebMethod]??
  2. [Trace]??
  3. public?PageResult<Order>?QueryOrder(Query<OrderCondition>?queryInfo)??
  4. {??
  5. ????OrderService?service?=?new?OrderService();??
  6. ????return?service.Query(queryInfo);??
  7. }??


输出日志如下:

[html] view plain copy print ?

  1. 2014-05-25?22:05:02,292?[8]?INFO??[127.0.0.1]?[http://tempuri.org/QueryOrder]?-?SoapRequest:??
  2. <soapenv:Envelope?xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"?xmlns:tem="http://tempuri.org/">??
  3. ???<soapenv:Body>??
  4. ??????<tem:QueryOrder>??
  5. ?????????<!--Optional:-->??
  6. ?????????<tem:queryInfo>??
  7. ????????????<tem:PageNo>1</tem:PageNo>??
  8. ????????????<tem:PageSize>1</tem:PageSize>??
  9. ????????????<!--Optional:-->??
  10. ????????????<tem:Condition>??
  11. ???????????????<!--Optional:-->??
  12. ???????????????<tem:StartTime>?</tem:StartTime>??
  13. ???????????????<!--Optional:-->??
  14. ???????????????<tem:EndTime>?</tem:EndTime>??
  15. ???????????????<!--Optional:-->??
  16. ???????????????<tem:ShopId>?</tem:ShopId>??
  17. ???????????????<!--Optional:-->??
  18. ???????????????<tem:ProductId>?</tem:ProductId>??
  19. ????????????</tem:Condition>??
  20. ?????????</tem:queryInfo>??
  21. ??????</tem:QueryOrder>??
  22. ???</soapenv:Body>??
  23. </soapenv:Envelope>??
  24. ??
  25. 2014-05-25?22:05:02,357?[8]?INFO??[127.0.0.1]?[http://tempuri.org/QueryOrder]?-?SoapResponse:??
  26. <?xml?version="1.0"?encoding="utf-8"?>??
  27. <soap:Envelope?xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"?xmlns:xsd="http://www.w3.org/2001/XMLSchema">??
  28. ??<soap:Body>??
  29. ????<QueryOrderResponse?xmlns="http://tempuri.org/">??
  30. ??????<QueryOrderResult>??
  31. ????????<PageNo>1</PageNo>??
  32. ????????<PageSize>1</PageSize>??
  33. ????????<TotalCount>3</TotalCount>??
  34. ????????<PageCount>1</PageCount>??
  35. ????????<HasNextPage>false</HasNextPage>??
  36. ????????<Data>??
  37. ??????????<Order>??
  38. ????????????<Id>1</Id>??
  39. ????????????<OrderDate>2014-05-25?22:05:02</OrderDate>??
  40. ????????????<ShopId>SHOP001</ShopId>??
  41. ????????????<ProductId>PRD001</ProductId>??
  42. ????????????<Quantity>1</Quantity>??
  43. ????????????<Price>59</Price>??
  44. ??????????</Order>??
  45. ??????????...??
  46. ????????</Data>??
  47. ??????</QueryOrderResult>??
  48. ????</QueryOrderResponse>??
  49. ??</soap:Body>??
  50. </soap:Envelope>??

接下来利用 SoapHeader 实现最基本的 Basic Authentication 校验,当然你不想每一个 WebMethod 去做相同的Check,同样我们实现一个 Soap Extension。

Authentication (SoapHeader) 的定义:
[csharp] view plain copy print ?

  1. using?System;??
  2. using?System.Web.Services.Protocols;??
  3. ??
  4. namespace?WebService1.Common??
  5. {??
  6. ????public?class?Authentication?:?SoapHeader??
  7. ????{??
  8. ????????public?string?UserName?{?get;?set;?}??
  9. ????????public?string?Password?{?get;?set;?}??
  10. ????}??
  11. }??
AuthCheckExtension 的实现:在 SoapMessage AfterDeserialize 这个阶段,取出客户端传的 SoapHeader 验证 UserName 和 Password 在服务端是否存在。
如果不存在或者错误则抛出 no auth ! 的错误。
[csharp] view plain copy print ?

  1. using?System;??
  2. using?System.Collections.Generic;??
  3. using?System.Linq;??
  4. using?System.Web;??
  5. using?System.IO;??
  6. using?System.Web.Services.Protocols;??
  7. using?WebService1.Config;??
  8. ??
  9. namespace?WebService1.Common??
  10. {??
  11. ????public?class?AuthCheckExtension?:?SoapExtension??
  12. ????{??
  13. ????????public?override?void?ProcessMessage(SoapMessage?message)??
  14. ????????{??
  15. ????????????if?(message.Stage?==?SoapMessageStage.AfterDeserialize)??
  16. ????????????{??
  17. ????????????????foreach?(SoapHeader?header?in?message.Headers)??
  18. ????????????????{??
  19. ????????????????????if?(header?is?Authentication)??
  20. ????????????????????{??
  21. ????????????????????????var?authHeader?=?header?as?Authentication;??
  22. ????????????????????????var?isValidUser?=?true;??
  23. ????????????????????????var?users?=?AuthConfiguration.AuthSettings.Users;??
  24. ????????????????????????if?(users?!=?null?&&?users.Count?>?0)??
  25. ????????????????????????{???
  26. ????????????????????????????isValidUser?=?users.Any(u?=>?u.UserName?==?authHeader.UserName?&&?u.Password?==?authHeader.Password);??
  27. ????????????????????????}??
  28. ??
  29. ????????????????????????if?(!isValidUser)??
  30. ????????????????????????????throw?new?BizException("no?auth?!");??
  31. ????????????????????}??
  32. ????????????????}??
  33. ????????????}??
  34. ????????}??
  35. ??
  36. ??
  37. ????????public?override?object?GetInitializer(Type?serviceType)??
  38. ????????{??
  39. ????????????return?null;??
  40. ????????}??
  41. ??
  42. ????????public?override?object?GetInitializer(LogicalMethodInfo?methodInfo,?SoapExtensionAttribute?attribute)??
  43. ????????{??
  44. ????????????return?null;??
  45. ????????}??
  46. ??
  47. ????????public?override?void?Initialize(object?initializer)??
  48. ????????{??
  49. ????????????//?初始化?AuthSettings???
  50. ????????????AuthConfiguration.Config();??
  51. ????????}??
  52. ????}??
  53. ??
  54. }??
然后给 WebMethod 加上 [SoapHeader("Authentication"),AuthCheck]?就OK了。
[csharp] view plain copy print ?

  1. using?System;??
  2. using?System.Collections.Generic;??
  3. using?System.Web;??
  4. using?System.Web.Services;??
  5. using?WebService1.Entity;??
  6. using?WebService1.Service;??
  7. using?System.Web.Services.Protocols;??
  8. using?WebService1.Common;??
  9. ??
  10. namespace?WebService1??
  11. {??
  12. ????[WebService(Namespace?=?"http://tempuri.org/")]??
  13. ????[WebServiceBinding(ConformsTo?=?WsiProfiles.BasicProfile1_1)]??
  14. ????[System.ComponentModel.ToolboxItem(false)]??
  15. ????public?class?Service1?:?System.Web.Services.WebService??
  16. ????{??
  17. ????????public?Authentication?Authentication?{?get;?set;?}??
  18. ??
  19. ????????[WebMethod]??
  20. ????????[Trace]??
  21. ????????[SoapHeader("Authentication"),?AuthCheck]??
  22. ????????public?PageResult<Order>?QueryOrder(Query<OrderCondition>?queryInfo)??
  23. ????????{??
  24. ????????????OrderService?service?=?new?OrderService();??
  25. ????????????return?service.Query(queryInfo);??
  26. ????????}??
  27. ??
  28. ????}??
  29. }??

最后我们拿 SoapUI 来测试一下:





再来看看错误处理,如果故意输错 UserName:



顺便要赞一下 SoapUI,真是 WebService 调试的利器,还可以生成 .NET / Java 代码,推荐大家使用。我们用 SoapUI 生成一下 Java 代码。
Java 客户端我决定用 CXF 来实现。所以要先配置一下 SoapUI:



JAVA CXF Client 代码:

[java] view plain copy print ?

  1. public?static?void?main(String[]?args)?{??
  2. ????????try?{??
  3. ??
  4. ????????????Service1?service1?=?new?Service1();??
  5. ????????????Service1Soap?service1Soap?=?service1.getService1Soap();??
  6. ????????????BindingProvider?provider?=?(BindingProvider)service1Soap;??
  7. ??
  8. ????????????List<Header>?headers?=?new?ArrayList<Header>();??
  9. ????????????Authentication?authentication?=?new?Authentication();??
  10. ????????????authentication.setUserName("fangxing");??
  11. ????????????authentication.setPassword("123456");??
  12. ????????????Header?authHeader?=?new?Header(ObjectFactory._Authentication_QNAME,?authentication,??
  13. ????????????????????new?JAXBDataBinding(Authentication.class));??
  14. ??
  15. ????????????headers.add(authHeader);??
  16. ????????????provider.getRequestContext().put(Header.HEADER_LIST,?headers);??
  17. ??
  18. ????????????QueryOfOrderCondition?queryInfo?=?new?QueryOfOrderCondition();??
  19. ????????????queryInfo.setPageNo(1);??
  20. ????????????queryInfo.setPageSize(1000);??
  21. ??
  22. ????????????OrderCondition?condition?=?new?OrderCondition();??
  23. ????????????condition.setShopId("SHOP001");??
  24. ????????????condition.setStartTime("2014-05-01?00:00:00");??
  25. ????????????condition.setEndTime("2014-05-10?23:59:59");??
  26. ??
  27. ????????????queryInfo.setCondition(condition);??
  28. ??
  29. ????????????PageResultOfOrder?result?=?service1Soap.queryOrder(queryInfo);??
  30. ????????????System.out.println("get?order?size:?"?+?result.getData().getOrder().size());??
  31. ??
  32. ????????}?catch?(Exception?e)?{??
  33. ????????????e.printStackTrace();??
  34. ????????}??
  35. ??
  36. ????}??
示例代码下载,下载请阅 Readme.txt

(编辑:李大同)

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

    推荐文章
      热点阅读