当前位置: 首页 > news >正文

基于OAUTH的电子商务支付集成研究与实现

Author: Xie, James

背景

      支付实际上可以分为线上支付和线下支付。线上支付也就是通常所说的网银支付,线下支付通常指的是POS机刷卡支付。

      第三方支付公司产生之前,对于线下支付而言,商户,包括酒店,商场,保险公司等,如果想要做银行卡支付业务,那么首先需要到银行开具资产证明等一系列担保措施,充分认定资质以后或许能开立一个POS机刷卡帐户,才能让银行给你架设POS机的网络和设备。

      电子商务出现以后,由于商品在线销售,所以网银支付逐渐兴起,每个银行都实现了在线版的网上银行支付系统。一开始电子商务交易不多,使用SSL安全协议就足以实现交易过程中的安全。随着互联网的普及,网络安全问题成了需要重点考虑的问题,于是SET安全协议的出现,弥补了在线安全协议的不足。

      无论是线下POS支付还是线上网银支付,商家跟银行的关系如下图1-1所示:

      可以看到商户和银行之间的交互是直接的,繁琐严格的开户手续,银行与银行之间接口和消息格式不一样或者平台技术根本不一样。以及清分结算固定,无法灵活设置。手续费标准无法优惠。

      因此,由于中国各个银行的独立性,商户需要在各个银行开立账户才能满足所有银行卡的支付,小商户往往无资质或者大商户需要大额支付等一些特殊操作,先不说这其中商户与各个银行需要签订的协议和需要上交的保证金外,开发成本和维护成本也非常高。

      这样第三方公司就应运而生了。商户只需要跟一家第三方支付公司打交道,而无需关心什么银行,银行发生了什么事,而且第三方支付还可以提供额外的定制化服务,这是银行无法提供的。第三方支付的模式如下图1-2所示:

      可以看到新的交互关系中,商户和银行之间的交互是间接的,商户只要跟第三方支付公司接入,避免了繁琐严格开户手续,各种银行接口和消息格式或者平台技术不统一的问题得到解决,以及清分结算灵活灵活设置,手续费优惠政策开放。

      既然第三方支付有这么多的优势,因此得到了很多商户的喜爱。目前第三方支付公司越来越多。在国外,几乎每个国家都有主流的第三方支付公司。在国内,第三方支付也越来越多,尤其是中国银监会出台了第三方支付准入机制以后,几乎每个大型机构都实现了自己的第三方支付平台。

      但是问题是,在如此多的第三方支付平台的背景下,怎么让电子商务平台与第三方支付平台对接集成的过程变成了首要的技术问题。由于电子商务平台和第三方支付平台往往分属于不同的公司,或者同一个公司,不同的域名网站,所以交易的信息都是通过互联网传递的。在这个过程中,安全性是首要的,没有安全就谈不上正确的支付,对于个人支付信息的泄漏很容易导致金钱的损失,这是每个实体最关心的问题。其次就是方便快捷,一方面对于使用者来说,由于是网上购物,因此电子商务平台会极力撮合买卖双方交易的达成。卖方希望能更快的卖出去自己的商品。买方也希望自己的网上购物体验是方便快捷的,而不是要输入这样或者那样的信息和验证才能买到自己想要的东西。另一方面对于平台接入开发人员来说,一个复杂的低效的信息交互过程将带来更多的开发人力资源的浪费,而且有时候事倍功半,容易出错,因此构建一个简单高效的信息安全交互过程是非常关键的。另外,对于电子商务公司和第三方支付公司维护上来说,公司的目的是盈利,因此,如何使得开发成本可控,维护成本低廉的集成也是其中要考虑的主要因素。

      因此,这些都对电子商务平台和第三方支付平台的集成提出了更高的要求。不仅要完成交易,还要重点实现以下三个方面。

  1. 交易过程安全性
  2. 平台接入简单易实现
  3. 技术上易于维护

      目前普遍采用通道加密的方式,比如SSL,专线等方式实现。这些方式虽然安全,但是网络集成非常麻烦。主要存在以下问题:

  1. 每次系统集成的方式基本上都不同,都需要重新开发,研发成本高;
  2. 一旦被攻击,就是全局的影响,对于海量的电子商务交易来说,不仅性能有影响,而且认证和授权不能达到更细的粒度,例如不能实现单个买家对单个卖家的临时授权等。
  3. 目前关于支付方面,大部分研究主要集中在如何保证交易过程安全方面其中很多研究都是基于SSL协议的通讯加密方式和基于SET协议的数据交换方式。
  4. SET协议采用了多层次的安全保障,增强了系统集成各方的开发成本,导致了技术上的复杂性。增加了推广的难度。同时SET协议交易过程还存在着安全性隐患。比如客户信息必须经过商家转到支付网关,这就增加了商家留下客户账号的副本的可能性。因此也有一些关于如何优化和改进SET协议的研究,以提高交易安全,减少交易代价。
  5. 另外一方面,也有关于其他第三方支付网关的安全在线支付模型的研究,比如SNOPP(Secure Network Online Payment Protocol);基于信用卡的在线支付协议iKP协议和SET协议;基于电子现金的在线支付协议Digicash协议和NetCash协议;基于电子支票的在线支付协议的NetBill 协议。
  6. 此外,SSL协议的通讯加密方式也从通道上一定程度上保证了安全性。安全套接层协议SSL(Secure Sockets Layer)是网景(Netscape)公司在推出网页浏览器的同时提出的协议。目前版本是3.0,它被广泛用于浏览器和服务器之间的身份认证和加密数据传输。SSL安全协议建立在TCP/IP协议之上,而又独立于应用层协议(HTTP,FTP等),专门为其它应用层协议提供专业的安全服务。例如基于SSL的HTTP应用协议就是经常使用的HTTPS应用协议。

      而关于OAuth的应用研究主要集中在社交网站等非金融交易领域,而对于金融交易等领域目前还是普遍采用封闭式的网络完成。比如新浪微博,人人网等公共社交平台,这些对个人信息大批量授权应用都普遍采用了OAuth的协议。还有国外的社交网站,比如twitter和facebook,都喜欢用OAuth协议来实现第三方应用的集成,因此业内非常看好OAuth的规范,都努力实现自己的OAuth第三方应用授权平台。

       在国内,像淘宝这样的电子商务网站,虽然也应用了OAuth,但是往往应用于那些安全性要求并不是很高的第三方应用集成,而对于第三方支付的集成这种安全性要求特别高的方式一般还是通过内部API实现。而且对于支付宝和淘宝之间来说都是内部网可以完成的。而对于第三方支付公司与电子商务公司之间应用的还是不多。

       在国外,贝宝是最大的最流行的支付方式,因此很多电子商务网站都集成了贝宝的支付方式。但是贝宝的支付方式有很大的局限性,尤其是它的快速结账(ExpressCheckout),虽然都遵守SET协议,但是集成过程相对麻烦,不是国际标准,授权粒度基本是应用级别,虽然实现了会话令牌来标识每笔交易,但是资源交互高度耦合,集成过程较复杂。

OAuth的原理和协议

      OAUTH协议提供了一个安全的、开放而又简易的标准。它与以往的授权方式有所不同,它的授权不会涉及到具体的用户名和密码即可实现第三方获得用户的授权,因此是相对安全的。同时OAUTH是为API访问授权为提供的一种开放的标准。而业界也提供了OAUTH的多种实现如PHP,JavaScript,Java等各种语言的开发包,大大节省了开发时间。另外,因为OAUTH是基于Token的模式,所以授权是临时的,而且授权的资源可以达到更细的粒度。因此,将OAUTH应用于电子商务网站和第三方支付公司的集成中,不仅同样保证了安全性,而且提供了灵活性,可靠性,而且对于跨公司的集成提供了简单易用的开放授权标准。

      OAuth是不是一种安全加密机制,它是一种对受保护资源的客户端进行认证和授权的方法。在客户端请求资源之前,需要资源所有者颁发访问许可给客户端。这样客户端才能用这个访问许可来获取授权Token,从而从资源服务器上获得受保护的资源。

      授权Token是将用户名和密码转换成一串字符产Token。所以是用户的一种抽象,这种抽象使得服务器可以知道这次授权是一种短期行为,因此资源服务器只要关心Token是否有效。OAuth的授权协议总体流程如下图2-1所示:

      它主要包含下列步骤:

1. 客户端发送请求授权到资源服务器。

2. 资源服务器返回一个访问许可给客户端。

3. 客户端使用访问许可访问授权服务器来请求一个授权Token。

4. 授权服务器校验访问许可的有效性,并在验证通过后返回授权Token。

5. 在客户端请求受保护资源的时候同时发送授权token给资源服务器。

6. 资源服务器验证授权Token是否有效,并在验证通过后返回资源包。

1.访问许可

      访问许可代表资源所有者提供的访问授权。访问许可的类型取决于客户端使用的获取方式和授权服务器所支持的方式。

2. 授权Token

      授权Token是通过将用户引导到授权服务器而获得的一种访问许可。授权服务器主要作用是验证用户,获得授权,然后向客户端分发一个授权Token。因为用户只在授权服务器上进行验证,所以终端用户的密码从来不用分享给客户端。

      授权码的获取过程如下图2-2所示:

 网络出现以后,信息交互和信息共享是其中的主要任务,在万维网出现以后,信息共享更加的简单高效。这里的信息共享是指信息系统之间的信息共享,在互联网上,从传统的web1.0那种服务器到用户单向的信息共享,到web2.0那种服务器和用户信息互相交互和共享的方式,因此在信息共享成了互联网的主要功能。信息共享可以使得资源能够更加合理的得到分配,节约社会成本,包括生产建设成本和维护成本,从某种以上上来说是创造了更多的财富。

      另一方面,由于信息的共享程度可以分为公开信息和保密信息。因此有些信保密信息的交互和共享必须在信息安全的条件下实现。

      开放平台是一种全新的思想,他的思想是吧自己的服务封装成标准的接口开放到互联网上供第三方开发。因此这种信息交互和共享不再是服务器和用户之间的交互和共享,这已经转变成了服务器和服务器之间的信息共享的问题了。通过开放平台,网站不仅能提供给终端用户的简单的页面访问,还可以与其他应用进行复杂的数据交互,将它们的内部系统转换成其他系统的开发平台。第三方应用开发者可以基于这些已经存在的、公开的Web网站,开发丰富多彩的衍生应用。

       开放平台主要通过Web Service实现,目前各大社交网站都有自己的开放平台,而且都使用了OAuth的作为自己的资源授权协议。所以本文以国外著名社交网站Twitter的的开放平台举例说明。

      在Twitter未支持OAuth之前,使用的是Basic Auth认证。Basic Auth要求Twitter应用把用户名和口令直接附加在HTTP或HTTPS协议头中发送给Twitter API。这样,Twitter应用势必要求用户在其应用中输入自己的Twitter用户名和口令,从而可以把Twitter的用户名和口令附加在HTTP(S)协议中发送给Twitter。这样Twitter应用开发者就能知道使用了他的Twitter应用的用户的所有用户名和密码,这样开发者就能随意使用这些Twitter账号登陆Twitter做任何操作了。比如,可以修改用户的Twitter密码,甚至直接去Twitter的Settings中删除这个帐号。这将带来潜在的安全性问题。

  而使用OAuth,Twitter应用无需知道用户的Twitter口令,只需要得到Twitter和用户双方的授权信息(后面会说这个授权信息——其实就是Token)即可。这样,Twitter应用开发者就不知道用户的Twitter口令,只能使用这个授权信息(Token)做有限的操作,无法修改用户的Twitter口令,也无法删除用户的Twitter账号。这在安全性上有了很大提高。

现行方案分析和新思路提出

      前面讲到电子商务与第三方支付问题的主要方面,接下来将要讨论如何接入第三方支付的系统。作为电子商务平台,保证用户交易数据的安全性,以及电子商务平台与第三方支付系统信息交互方面,必须得到保证。

      另一方面,第三方支付带来好处的同时,也增加了安全风险,因为用户敏感信息的交互过程又多了一个中间环节。

      目前国外的电子商务支付中,贝宝(PayPal)用的最普遍。国内电子商务支付中,支付宝(AliPay)用的比较多。现在以下本文分别简要分析一下这两种主流支付方式的接入方式。

      因为贝宝支持各种国际货币,因此,已经有越来越多的商家将贝宝作为他们的最常用国际外贸支付手段,而其中一些有大型的电子商务平台,更是会选择将PayPal集成在网站上,提高买家和卖家之间的信任度,贝宝的支付方式很多,其中大部分电子商务平台会选择快速结账,该方式可以有效拦截一些风险高的付款行为,从而达到最高限度地提高转换率和降低风险。

      快速结帐(Express Checkout,简称EC),是一种强大的基于API的付款解决方案,可以紧密集成到任何电子商务网站中。有了快速结账,网站客户可以使用他们已存储在PayPal上的发货及账单信息,而无需在每次购买时重新输入这些信息。

      上面提到,快速结帐是一种基于API的解决方案, 在整个支付流程中,共需调用三个API接口。实现Express Checkout 共分三步:

  1. 调用SetExpressCheckout, PayPal将返回一个Token,用于完成后续付款步骤,然后重定向客户的浏览器到PayPal网站允许其登陆;
  2.  客户在PayPal网站上确认其资金来源,配送信息和联系方式等;确认后即返回到你们的网站上,这时即可调用GetExpressCheckout获取客户确认的信息。
  3. 客户再次确认其付款,最后确认后调用DoExpressCheckout即可完成付款。

      客户对订单确认后,即可点击最后付款按钮完成付款动作。这个付款按钮实际上就是通过调用最后一个API函数DoExpressCheckoutPayment完成付款动作。在调用该函数后,PayPal将立即返回一个付款状态,您可以将付款细节及付款状态显示给客户看。

      从贝宝支付集成基本流程可以看出,贝宝的支付依赖于贝宝的三个API,而这三个API的安全性完全依靠于API自身的安全性,基本上是SET协议的指导原则下实现的。另外对于订单的信息,包括商品信息,交易金额信息,折扣,物流等标准信息都是在通道内完成的。大量的客户敏感信息都在API通道里面传输。由于通道时严格加密的,因此信息是安全的,但是另一方面,这种信息交互方式比较繁琐的,对于新的电子商务网站的对接过程中,建立这样的安全通道本身就是一个耗时耗力的过程。有数据统计,贝宝的三个API的平均耗时分别是:setExpressCheckout是2秒,getExpressCheckout是1秒,doExpressCheckout最慢,大概平均在5秒。而在节假日等大访问量的情况下,更会造成很多超时,有相当多的doExpressCheckout操作会在10秒以上,出现这个问题的原因很大因素是因为大量与支付没有特别相关的信息,比如订单信息通过加密通道传输交互。

      如果能找到一种更通用简便而又不失安全的方式,就能够改善一下大流量付款访问。

      支付宝的接口也很多,这里以支付宝提供给商户的标准双接口为例,支付宝也要求其他电子商务平台接入的方式必须符合它的规范。

    1)用户在电子商务网站上预览订单页面的时候选择支付宝支付手段

    2)用户点击确定,电子商务网站引导用户跳转到支付宝登录页面

    3)支付宝账户登录,订单支付信息被显示出来

      页面渲染之前,商户会调用支付宝的接口,把订单的信息,包括商品信息,物流信息等传递给支付宝,支付宝拿到这些信息后显示在下面的页面里面。

4)用户确认订单支付信息,确定提交完成支付宝支付

      以上这些过程都涉及到了系统与系统之间数据交互的问题,这也是电子商务支付集成要研究的主要问题。

      合作商户系统(电子商务平台)与支付宝的数据交互流程如下图3-1所示:

      基本信息包括了return_url, notify_url和密钥,签名等安全信息。

      业务信息包括了订单号,商品名称,商品数量,商品描述,折扣,交易金额买家支付宝信息,卖家支付宝信息,收货人姓名,地址,邮编,电话号码等,以及物流类型,运费,运费支付类型等。

      上述分析发现支付宝的集成方案与贝宝的方案类似,所有信息交互通过API实现,通道加密是必须的。对于某个电子商务网站来说,通道的密钥对只有一个,虽然定时会更新,但是一旦被破解,就是全局的。

      分析请求参数,可以总结出,一些商品信息和物流信息也是通过特定加密API交互的,扩展性不好。比如电子商务网站修改,增加或者删除了某些物流信息,需要检查对整个通道是否需要重新做签名验证方式(修改原始明文块来重新生成MAC块)。

      贝宝和支付宝作为国际国内主流的支付方式,已经受到了广大消费者的喜爱。但是现实中,由于这些大型第三方支付公司的庞大系统,使得他们的系统不得不保持复杂的架构来适应各种各样的需求,尤其是历史系统中的各种各样的古怪功能很多,系统设计往往不能脱离现有设计。比如贝宝的ExpressCheckout支付必须要求客户系统(比如电子商务系统)来适应EC的三步走过程。而这往往造成了系统的极度耦合。维护升级比较麻烦。

      而本文正是需要寻找这样一种方式,能使得这种系统间尤其是跨公司的系统间的授权集成方式更加简单高效的解决方案。

      那就是本文提出的方案:开放平台和OAuth的结合。

      上面分析了贝宝和支付宝与电子商务平台数据交互的参数看出,电子商务平台与第三方支付平台需要交互的的信息就是订单信息和支付信息。

      如果一个电子商务平台既要跟支付宝接入又要跟贝宝接入,或者其他的第三方支付接入,那么它就要开发多个程序去适配每个第三方支付公司的信息交互,明明是同样单信息却要封装在不同的API通道里面,也就是适应特定的信息安全。如下图3-2所示:

      回顾电子商务平台和第三方支付平台的集成要重点实现的三个方面。

1. 交易过程安全性;

2. 平台接入简单易实现;

3. 技术上易于维护。

      要实现上述几个方面,其中最先要做的,也是最基础最重要的就是统一数据格式,也就是标准的数据结构。如果数据结构和数据含义表征不一致,那么重复开发在所难免。比如移动平台上需要的数据和个人计算机上传统网页需要的数据,如果没有一个统一的数据格式和数据含义,那么传统网页就无法快速的移植到移动平台。这对于现如今移动平台市场至关重要的时刻来说,无论从技术上复杂度还是从业务发展上都是一个极大风险。

      开放平台很好的解决了这个信息重复封装的问题。如果开放平台的思想运用在上图中,信息的共享方式将变得统一,简洁。如下图3-3所示:

       从图中可以看出另外一个优势,信息的传递发起方已经是相互的了,电子商务平台不需要关心数据的封装和打包,第三方支付根据标准API来取订单信息。

      既然开放平台使得信息交互的过程变得统一简单,因此开放平台在电子商务与外界系统集成中显得非常有优势。

      另一方面,开放平台作为一个资源的平台,如何实现资源的授权是本文要重点讨论的。下面对如何将OAuth应用于电子商务支付集成的问题简要阐述。

      在OAuth应用于电子商务支付集成之前,电子商务平台与第三方支付交互数据的时候,第三方支付需要用明文保存用户的证书,以便能够下次还能拿到用户的订单信息。当然可以采用通道加密的方式共享一个电子商务平台自身的token的方式来克服这个缺点。但是又会带来下面的问题。

  1. 第三方支付可以提取该用户在电子商务平台上所有订单信息,因为它有了该用户在电子商务平台上的认证信息。如果采用通道加密的方式,那么第三方支付更可以提取所有用户的所有订单信息。
  2. 用户在电子商务平台不能单独撤销某些订单信息的授权。如果采用通道加密的方式,那么更不能撤销某些用户的订单查询授权。

      在OAuth应用于电子商务支付集成之后,OAuth的思想解决了资源的授权问题,而且资源是可以分拆成不同的子资源进行管理的,用户名密码的缺点被克服了。

      OAuth授权模式下,在电子商务平台与第三方支付交互数据的时候,第三方支付只需要保存用户给该订单信息查询的一个临时授权,该临时授权会在较短时间内失效,所以第三方支付根本不需要保存该临时授权。就算保存也是无效的。

      第三方支付不可以提取该用户在电子商务平台上其他订单信息,因为用户只是授与了临时权限。

用户可以单独撤销某些订单信息的授权,比如用户不想再支付了。

      另外,由于OAuth是开放的授权协议,因此,对于方案实施来说还是变得简单。目前业界提供了OAUTH的多种实现如PHP、JavaScript,Java,Ruby等各种语言开发包。而且OAuth协议本身是由IETF组织统一维护,因此对于升级维护也比较简单。

      因此,将OAuth应用于电子商务平台与第三方支付集成是一个不错的选择。本人在某大型电子商务网站与国际第三方支付公司集成中得到了很好的应用。下面章节会详细介绍设计实施过程。

OAuth电子商务支付集成方案设计

      前面分析了OAuth协议的基本原理和协议基本规范,以及OAuth基于开放平台实现的应用背景,并对应用现状和案例进行了分析,提出了一种基于OAuth的开放平台模式集成思路。接下来本文将讨论OAuth能否应用于支付系统对于电子商务平台的接入的问题。因为支付系统传统方式是通过SET协议实现,安全性要求很高。虽然现在OAuth的应用大都局限于社交网站应用中。但是该技术同样适用于电子商务平台这样的场景。

      接下来详细介绍了本人参与的某大型电子商务网站与其他第三方支付公司集成过程,并同时对该解决方案详细分析来探讨说明OAuth在电子商务平台接入过程中的合理性。

      目前OAuth是可以依赖于开放平台的建设的,因此,首先要做好一个开放平台。以本人该大型电子商务网站的开放平台入手来讲解该方案的时候过程。

      一般开放平台都是基于HTTP(S)协议的,而目前对于web共享,web资源共享用到最多最有优势的就是Web Service。

      对于第三方支付与电子商务平台集成的需求来说,主要涉及两个接口:FetchToken 相关的API和getXXXTransactions 相关的API。其中FetchToken API是用来换取买家的授权码并验证买家的真实性的,而getXXXTransactions API是用来得到真正的订单信息。

      设计这些接口的主要原因是基于OAuth的授权协议总体流程,如下图4-1所示:

      主要的步骤如下:

1. 客户端发送请求授权到资源服务器。

2. 资源服务器返回一个访问许可给客户端。

3. 客户端使用访问许可访问授权服务器来请求一个授权Token。

4. 授权服务器校验访问许可的有效性,并在验证通过后返回授权Token。

5. 在客户端请求受保护资源的时候同时发送授权token给资源服务器。

6. 资源服务器验证授权Token是否有效,并在验证通过后返回资源包。

      上述可以分析得出在第1,2,3,4步骤都是为了得到令牌,5,6步骤是得到令牌来访问授权资源的过程。

      针对前四个步骤,当然需要设计一个令牌获取接口,而为了获取令牌,需要设计一个获取访问许可的接口。

      访问许可代表资源拥有者提供的授权。访问许可的类型取决于客户端使用的获取方式和授权服务器所支持的方式。

      先看获取令牌的接口。本文设计了一个FetchToken接口,该接口的主要目的是获得一个用户的认证令牌。这个接口调用可以在该用户同意授权的情况下生成一个特殊的用户授权令牌。用户一般在登陆电子商务网站的时候同意授权。当令牌得到以后,该令牌可以用作认证其他的服务调用,而不用再次需要用户的用户名密码来验证,因为该令牌本身已经能代表相关联的那个用户名密码了。

      与其他的交易接口不同的是,FetchToken请求不需要用户令牌。跟该调用相对应的参数主要是应用本身的键值,比如AppID,DevID,AuthCert来认证授权。

      FetchToken接口具体的输入输出参数定义如下表4-1和4-2所示:

   表4-1 FetchToken输入参数定义和说明表

输入参数名

类型

必选/可选

含义

SercretID

字符串

可选

一个跟令牌获取请求相关的值。这个值将在下一节中详细说明如何拿到的。

但是SecretID在FetchToken的请求中需要用户的UserID,因为它本身不包括用户的信息。

如果SessionID提供了,那么SecreID和UserID就不需要提供了。

SessionID

字符串

可选

一个跟令牌获取请求相关的值。这个值将在下一节中详细说明如何拿到的。

如果SecretID提供了,那么SessionID就不需要提供了。二选一。

但是SessionID的好处就是在FetchToken的请求中不需要用户的UserID。

ErrorLanguage

字符串

可选

为了适应不同国家对于错误信息描述的,这里需要定义语言类型来满足错误信息本地化的要求。这里遵守RFC 3066规范。比如:

ID

国家

de_AT

奥地利

de_CH

瑞士

de_DE

德国

en_AU

澳大利亚

en_CA

加拿大

zh_CN

中国

 

MessageID

字符串

可选

可以用来追踪本次请求的详细信息。

Version

字符串

可选

接口总是要提供一个版本号,主要是为了表明一下两种含义:

  • 让平台知道什么样的代码或者数据版本是这次请求需要的
  • 让平台知道什么样的schema是这次请求需要的。

对于版本管理,需要定义一个版本管理的策略。版本管理策略的主要关键点是:

  1. 对象的弃用和删除策略

一个被弃用的对象是不建议使用的。但是客户端应用不是必须升级的。除非升级到最新的schema。以下是具体控制措施:

  • 如果是输入对象,那么客户端应用只会受到警告。
  • 如果是输出对象,那么客户端应用会收到一个稍微低于弃用版本的应答。
  • 如果弃用对象的版本低于最低支持版本,那么就会出错。
  1. 最小支持版本策略

为了更好的维护程序升级,会每六个月升级一次最小支持版本。最小支持版本会保留19个月。也就意味着,如果对象版本低于最小支持版本(18个月),那么应用就不能正常工作了。所以应用要越早越好的去掉弃用版本依赖。

WarningLevel

枚举

可选

在应用请求一个被弃用的对象的时候,控制是否返回警告。也就是上面所说的弃用版本不能满足的时候返回的警告级别。可能的选值有:

  • High
  • Lower

默认是Lower

 

                                         表4-2 FetchToken输出参数定义和说明表

输出参数名

类型

必选/可选

含义

authToken

字符串

必选

这就是用户的认证令牌,也就是OAuth中最重要的令牌。

HardExpirationTime

时间

必选

日期和时间,表明这个令牌过期的时间点,如果过了这个时间点,客户端应用就不能代表用户来获取用户资源了。

这个非常重要,将在下面的电子商务与支付集成中应用。

RESTToken

字符串

可选

REST方式下的用户认证令牌,可选,为了支持越来越多的REST服务。

Ack

枚举

必选

一个在应用级别上的,代表用户请求应答状态的值,可能的值有:

  • Success
  • Failure
  • Warning

CorrelationID

字符串

可选

如果请求的时候填了MessageID,那么这个值就是MessageID,如果没有填,那么就是空值。

Errors

枚举

可选

一个在应用级别上的错误信息的值,它包括了:

  • ErrorClassification
  • ErrorCode
  • ErrorParameters
  • LongMessage
  • SeverityCode
  • ShortMessage

Timestamp

时间

必选

遵循ISO8601,格式为:

YYYY-MM-DDTHH:MM:SS.SSSZ

Version

字符串

必选

请求信息schema版本号

 

      另外,还必须保证一个sessionID作为请求参数,这正如OAuth协议规范中第一第二步骤所说的,需要一个访问许可。

      所以需要设计另外一个接口来辅助生成这个访问许可,这里实现了两种方式,一种是通过SecretID的值,SecretID不需要开放平台接口的支持,在下一节中会详细展开SecretID的设计研究。另外一种是通过SessionID的值,也就是GetSessionID接口。

      GetSessionID接口主要目的是为了生成一个SessionID,也就是访问许可。上面分析发现SessionID是在令牌创建过程中用作唯一标记符的。在FetchToken的请求里面必须要求一个合法的SessionID值。

      跟FetchToken的接口一样,GeSessionID接口也设计成了需要应用键值(AppID,DevID,CERT),与此同时,还需要额外一个RuName值。

      一旦得到了这个SessionID值,就可以用它来构造一个URL,然后发送给用户登陆页面。当用户输入用户名密码之后,FetchToken就能根据这个SessionID来生成中正确的访问令牌了。具体输入输出定义如下表4-3和4-4所示:

                                         表4-3 GetSessionID输入参数定义和说明表

输入参数名

类型

必选/可选

含义

RuName

字符串

必选

电子商务应用标识,详见下一小节

ErrorLanguage

字符串

可选

标准输入参数,同FetchToken

MessageID

字符串

可选

标准输入参数,同FetchToken

Version

字符串

可选

标准输入参数,同FetchToken

WarningLevel

枚举

可选

标准输入参数,同FetchToken

                                        表4-4 GetSessionID输出参数定义和说明表

输出参数名

类型

必选/可选

含义

SessionID

字符串

必选

一个40个字符串长度的标识。应用可以把它拼在用户跳转地址上,来确认用户标识并获得用户令牌,进而在FetchToken的时候能代表用户自身做一些认证授权。上一节已经稍微讲述了基本用途。

Ack

枚举

必选

标准输出参数,同FetchToken

CorrelationID

字符串

可选

标准输出参数,同FetchToken

Errors

枚举

可选

标准输出参数,同FetchToken

Timestamp

时间

必选

标准输出参数,同FetchToken

Version

字符串

必选

标准输出参数,同FetchToken

 

 上面提到在GetSessionID的时候需要RuName值,接下来将讨论RuName值的生成过程。OAuth规范中也没有要求这个。考虑到大型电子商务平台的适用性,定义一套RuName是非常必要的。

      其实RuName值就是你的应用在电子商务平台上的应用标识。本身代表了一个电子商务平台第三方应用的名字。这里把这个RuName与应用配置里面关联起来。应用可以创建很多RuName,比如对于不同的国家和市场推广的考虑,这个应用的名字将是不一样的,因此应用使用者看到的名字也可以不一样。

      因此本文设计了如下生成RuName的过程,需要提供以下信息:

  1. 应用使用者在同意授权的页面中显示的内容
  2. 应用认证用户和得到令牌的一些规则
  3. 应用的URL,比如AcceptURL,这个URL会让电子商务平台会让用户同意授权以后自动跳转到该第三方应用。
  4. 管理RuName的细节内容。包括:
  • 显示标题:也就是要显示在用户同意授权页面上的公司或者应用的名字
  • 显示描述:也就是显示在用户同意授权页面上公司或者应用的描述文本
  • 令牌返回方法:可以选择”FetchToken”, 因为一旦用户同意授权以后,第三方应用将用FetchTOken来换取令牌。所以FetchToken是推荐的令牌返回方法。(考虑到系统的兼容性,这个是预留选项)
  • 授权类型:这里系统里面设计了两种类型的授权类型, Authorization和IDVerification。如果应用需要电子商务平台的用户来授权,那么就应该选择Authorzation。如果应用本身只需要电子商务平台本身做确认,而并不需要访问用户的隐私数据。那么就选择IDVerification。这个时候用户授权页面就会呈现一个更加随意的文本,而生成的令牌本身也不是专门为该应用设计的,这里就不展开了。
  • 应用类型:从OAuth的协议规范上看,OAuth并不一定是基于web的,因此,任何应用类型都是可以的。因此这里需要制定应用的类型。基本上有两种,第一种是基于web的应用就能在用户同意授权后跳转掉应用定义的一个URL。第二种就是基于desktop的应用,这个时候就没有一个URL来跳转。
  • 同意授权后跳转URL: 这个URL就是上面提到的跳转URL,当用户同意授权的时候,电子商务平台会跳转用户页面到应用定义的这个URL. 这个URL必须支持SSL,由于只有基于web的才有这个URL,因此,这个URL必须是基于HTTPS协议的。
  • 拒绝授权后跳转URL:既然用户可以同意授权,那么用户同样可以拒绝授权。因此应用必须定义一个拒绝授权后要跳转的URL。与上面的URL一样,这个URL也必须支持SSL,HTTPS的协议。如果没有指定的话,那么这里设计了一个电子商务平台的标准页面。
  • PrivacyPolicyURL: 也就是应用免责申明和特殊政策页面。

 

      上面已经提到了授权相关的基本接口的设计,接下来是拿到授权token如何使用的过程,也就是如何得到资源的过程。

      这种信任机制应该是对所有资源获取接口都是统一的。不管是访问getXXXTransaction来获取部分订单信息还是getOrders来获取整个订单信息都是一样的信任机制。

      一个给定的接口一般有两种信息。一种是对所有接口都通用的数据。一种是对某些接口使用的数据。比如有些情况下,你只要输入在AbstractRequestType里面数据。而有些情况就需要在URL或者HTTP头上。不管怎么样,下面几个数据是共同的。

  • 定义网站目标地址
  • 定义Schema版本
  • 定义一个MessageID把请求和应答联系起来
  • 定义一个错误信息语言
  • 定义细节等级和粒度等级
  • 定义写入操作的UUID

      本文主要讨论授权token相关问题,所以重点描述一下安全相关设计。

      在实际过程中,当一个用户要在电子商务操作之前,比如列商品,买商品,用户必须先用自己的用户名和密码登陆电子商务平台。同样的,对于开放平台来说,用户需要获得授权token,也就是通过上面的这些接口,然后才能访问电子商务平台的资源。也就是说授权token可以代替用户名和密码来完成一般用户登陆以后才能完成的操作。开放平台资源请求接口拿到授权token以后,就可以去电子商务平台合法的请求资源了。

      本文中,解决方案提供了两种验证方式。一种是授权token,一种用户SessionID加上一组关于开放平台开发者的信任信息。一般采用前者。

      在标准的SOAP接口中,通常会要求在请求头上输入授权token。请求结构如下:

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

               xmlns:xs="http://www.w3.org/2001/XMLSchema"

               xmlns:bl="urn:xxx:apis:eBLBaseComponents">

  <soap:Header>

    <bl:RequesterCredentials soapenv:mustUnderstand="0">

      <xxxAuthToken>

          ... AUTHENTICATION TOKEN GOES HERE ...

      </xxxAuthToken

    </bl:RequesterCredentials>

  </soap:Header>

... Call body ...

</soap:Envelope>

 

      在XML请求中,可以定义如下:

<?xml version="1.0" encoding="utf-8"?>

<GetXXXOfficialTimeRequest xmlns="urn:xxx:apis:bLBaseComponents">

  <RequesterCredentials>

    <xxxAuthToken> Token goes here </xxxAuthToken>

  </RequesterCredentials>

  <Version>383</Version>

</GetexxxOfficialTimeRequest>

 

      目前对于客户端应用来说,主要是两种,一种是客户端/桌面应用,另一种是Web/服务器应用。下面对这两种场景进行授权分析来探讨OAuth授权机制的方案。

      如果客户端的应用没有网页组件,那么在获取令牌的时候,客户端就不需要实现AcceptURL和RejectURL页面。

      因此在处理的过程中,可以设计成如下图4-2所示:

 

  1. 客户端桌面应用使用者发起一个授权意愿请求。
  2. 客户端桌面应用自身发起一个GetSessionID请求到电子商务开放平台上,这个过程就是上一节描述的GetSessionID的过程。这个时候,客户端桌面应用拿到一个SessionID。
  3. 桌面应用组建一个电子商务平台登陆页面地址,这个地址会引导用户到电子商务登陆页面并显示是否同意授权表单。该地址形式如下:

https://<host>?SignIn&RuName=<RuName>&SessionID=<SessionID>

  1. 用户确认用户名和密码,登陆电子商务平台。
  2. 电子商务平台引导用户页面到桌面应用。
  3. 在应用授权页面,用户可以选择同意或者拒绝授权。
  4. 如果用户同意授权,桌面应用会引导用户到电子商务平台确认页面,页面可以显示一些感谢信息和“你可以关闭这个窗口了”等信息。
  5. 如果用户拒绝授权,桌面应用自行应到到应用其它模块。
  6. 桌面应用然后调用FetchToken来换取用户授权Token。(上面FetchToken这一节已经详细描述了FetchToken的方法和原理)

 

      如果客户端的应用是个网页组件,那么在获取令牌的时候,客户端就需要实现AcceptURL和RejectURL页面。

      因此在处理的过程中,可以设计成如下图4-3所示:(有些步骤是用户做的,有些步骤是电子商务平台做的,有些是应用做的。)

 

  1. 网页应用使用者发起一个授权意愿请求。
  2. 网页应用自身发起一个GetSessionID请求到电子商务开放平台上,这个过程就是上一节描述的GetSessionID的过程。
  3. 网页应用拿到一个SessionID。
  4. 网页应用组建一个电子商务平台登陆页面地址,这个地址会引导用户到电子商务登陆页面并显示是否同意授权表单。该地址形式如下:

https://<host>?SignIn&RuName=<RuName>&SessionID=<SessionID>

  1. 用户确认用户名和密码,登陆电子商务平台。
  2. 电子商务平台引导用户页面到网页应用iframe。
  3. 在应用授权iframe中,用户可以选择同意或者拒绝授权。
  4. 如果用户同意授权,电子商务平台会引导用户到应用的AcceptURL页面,如果用户拒绝授权,电子商务平台会引导用户到应用的RejectURL页面。(这两个URL在上一节的RuName生成中已经详细描述)
  5. Web应用然后调用FetchToken来换取用户授权Token。(上面FetchToken这一节已经详细描述了FetchToken的方法和原理)

 

      从上面两个常用的应用场景来看,主要的区别在于电子商务平台和应用之间的跳转的问题。对于桌面应用来说,因为电子商务平台通常是web服务区应用,因此无法通过常用的Url无缝对接,因此电子商务平台自身开放一组标准的服务接口,然后通过跳转(不管是网页之间的跳转还是网页与桌面应用之间的跳转)都可以实现Token的正常交换。

      上面已经对开放平台中OAuth的基本接口进行了详细的分析,也就是说万事俱备,接下来对第三方支付平台与电子商务平台对接过程中的设计方案进行详细分析和研究,探讨出一套实际可行的方案,并描述了经典OAuth方案的改进措施。

      这里分析一下OAuth协议中的几个基本子态。

      在Web Server子态中,网页客户端通过将终端用户的user-agent重定向到授权服务器来发起这个流程。客户端传入它的客户端标识符、请求作用域、本地状态和一个重定向URI,在访问被许可或被拒绝后授权服务器会重新将终端用户引导回这个URI。授权服务器借助于user-agent验证终端用户,并确定终端用户是否许可客户端的访问请求。假定终端用户许可了这次访问,授权服务器会将user-agent重定向到之前提供的重定向URI上去。授权服务器为客户端传回一个授权码做获取访问令牌之用。客户端通过验证并传入上一步取得的授权码从授权服务器请求一个访问令牌。授权服务器验证客户端私有证书和授权码的有效性并返回访问令牌。

      在User-Agent子态中,客户端将user-agent引导到终端用户授权endpoint。客户端传入它的客户端标识符、请求作用域、本地状态和一个重定向URI,在访问被许可(或被拒绝)后授权服务器会重新将终端用户引导回这个URI。授权服务器验证终端用户(通过user-agent)并确认终端用户是许可还是拒绝了客户端的访问请求。如果终端用户许可了这次访问,那么授权服务器会将user-agent引导到之前提供的重定向URI。重定向URI会在URI片断中包含访问令牌。user-agent响应重定向指令,向web服务器发送不包含URI片断的请求。user-agent在本地保存URI片断。web服务器返回一个web页面(通常是嵌入了脚本的HTML网页),这个页面能够访问完整的重定向URI,它包含了由user-agent保存的URI片断,同时这个页面能够将包含在URI片断中的访问令牌(和其它参数)提取出来。user-agent在本地执行由web服务器提供的脚本,该脚本提取出访问令牌并将它传递给客户端。

      这两种子态其实跟上一节的应用场景有点类似。所以对于一般的应用完全可以按照这两种子态来实现。

       但是,上面看出无论是Web Server子态还是User-Agent子态,都是通过客户自行跳转或者浏览器代理跳转来获取授权码。

      考虑到浏览器的不安全性和用户行为可能存在欺诈等考虑,这些方式似乎都不太适合第三方支付应用与电子商务平台集成这种对安全性要求相对较高的情况。

      使用现存的某种访问许可,它被表达成授权服务器所支持的某种断言格式。使用断言需要客户端从一个断言发行方获得一个断言(如SAML断言)或自己分发一个断言。断言的格式、获得断言的过程,以及验证断言的方法,由断言发行方和授权服务器定义。

      这里研究发现,用户与电子商务平台之间已经有了现成的信任关系或框架来建立授权。也就是说既然用户已经通过使用客户端私有证书与电子商务平台服务器进行验证,那么电子商务平台就可以跟授权服务器直接建立验证关系。因为对于一个内部的系统之间的相互授权和验证显然更加简单,比如可以通过内部的访问令牌相互访问资源,从而代理获得资源服务器的访问令牌。当然访问令牌的作用域局限于受客户端控制的受保护资源,或者其它资源拥有者与授权服务器预先约定的资源。

      另一方面,表单是一种资源,是属于买卖双方的一种资源。因此对于买家和卖家来说这种资源信息有些是卖家可以看到的,有些是买家可以看到的。由于对于卖家来说往往是卖家提前授权给应用,比如卖家同意某第三方支付公司访问该卖家在电子商务平台上的资源。因此可以按照上述的OAuth授权机制来实现。因为是授权是一次性,很少更新的。但是对于买家来说,每次交易订单买家对于卖家来说都是随机组合的。因此需要建立一种快速有效的授权方式,使得第三方应用能够验证买家并取得买家授权。

      这正是上面FetchToken接口分析中提到的SecretID方式来实现方式的原因,这种方式虽然需要UserID的支持,但是与传统的OAuth方式有明显的简化。 

      因为通常表单授权服务器与电子商务支付页面总是在一个安全区域内(除非是电子商务外部表单),因此买家用户总是在登陆状态下做的结账操作,那么UserID就是一个已知量。因此FetchToken总是可以通过SecretID和UserID联合实现。因为SecretID中并没有包含UserID这些敏感信息,因此比SessionID更加安全。即使被破解也不会造成敏感信息泄露的问题。

      首先,SecretID不需要加密,上面已经指出它用来让第三方支付来换取真正的授权Token,因此防止碰撞很重要很重要。因为SecretID是按照用户划分的,所以只要保证同一个买家在有效时间内(10分钟)不冲突就可以了。这里采用了UUID的实现。

      UUID是是这么一串数字,它是由机器生成,而且能保证在同一时间内的全局唯一性。它有很多种实现方式,在不同的平台中实现方式也不同,可以使用以太网卡的MAC地址,也可以使用纳秒级时间戳等许多其他的能表示全局唯一性的数字。

      因此UUID可以由以下三大部分的组成:

(1)当前时间和日期,因为时间的全局唯一性,保证了这一秒与下一秒的全局唯一性。

(2)当前时钟的序列,也就是说毫秒,纳秒级的时间戳来保证微小差别唯一。

(3)根据IEEE的标准,在全球范围内,每台机器都有一个全局唯一的机器识别号,比如可以从网卡获得MAC地址,而MAC地址在全球范围内是唯一的。

      UUID保证了全局唯一性,这在很多方面得到了应用,但是它的缺点在于长度比较长。

      本文的实现编程语言是JAVA语言,而JAVA语言自身有很好的API支持。它的实现方式很简单:UUID uuid = UUID.randomUUID();

      这个SecretID是信息安全的关键,下一章节中会详细的分析。

      电子商务平台的主要资源就是订单信息和支付信息,由于这些信息是用户私有信息,而第三方应用在使用的过程中又需要调取订单信息和用户支付信息来支持它们的第三方应用。因此基于OAUTH的授权服务就可以分为以下几步来实现。

      第一步,电子商务平台对订单支付信息资源授权

      对信息资源授权传统的做法是跟具体资源绑定,这就涉及到资源的粒度如何划分的问题,因为订单支付信息是基于用户的,也是基于第三方支付应用的,同时还是基于每个订单交易号的。所以这里可以有两种授权方式:

  • 用户对该第三方支付方式下的每个订单进行授权
  • 用户对该第三方支付方式下的最新一笔订单进行授权

      可以看到前者可以实现对更多资源的同时授权,需要授权token跟订单号绑定。而后者只是对最后一笔订单授权,因此授权token只要跟应用ID绑定就可以了,因为知道应用ID就知道该应用下这个用户的最新一笔订单。

      因此,本文的系统采用了后者,即对授权表的更新操作,

SQL update:

Update APP_AUTH_TOKEN set TOKEN=‘************’

Where USER_ID=20300290 AND APP_ID=7446

授权token数据表设计如表4-5所示:

表4-5 授权token数据表

USER_ID

1234567657

APP_ID

1231

TOKEN

7efCoKT+eQ1H99PQnxdbxxbL2ZadeJMI2toVKYltOY+Vt8OemhM7T/4TrvMQVy308VpdT4crY0YWKwyVu2aKg23hDwbufw+eBsfcif4JIGQn

TOKEN_TYPE

1

PRE_TOKEN

7efCoKT8OemhM7T/TrvMQVy308VpdT4crY0YWKwyVu2aKg23hDwbufw+eBsfcif4JIGQn

HARD_EXPIRE_TIME

2013/09/01 00:00:00:000

 

      这个Token设置了一个很短的过期时间,比如10分钟内必须完成订单支付,否则就要重新授权。

      第二步,第三方支付平台调用FetchToken

      因为Token是加密信息,因此按照OAUTH的规范,授权token应该用Access Token换取,FetchToken接口就是这个目的。

      第三步,第三方支付平台调用GetXXXTransactions

      上一步得到token的主要目的就是要得到订单支付信息,这个调用就是用该Token去拿到订单支付信息。

      第四步,第三方支付平台渲染订单支付信息

      订单支付信息包括了商品名称,数量,以及价格,价格包括商品本身价格,运费,折扣等。以及买家的联系地址,邮编和电子邮件等。

      第五步,买家确认支付

      在订单支付信息渲染成功后,买家可以确认信息并完成支付。

      这个Token主要包含了以下信息:

  • SecretID:就是上面生成的一个随机码,保证授权的不重复
  • 用户ID:电子商务买家的用户ID,用来表示此次授权的归属。
  • 应用ID:电子商务平台上第三方支付应用ID

      对于Token怎么对上面信息进行加解密的问题,在第三方支付拿到这个授权token之后,第三方支付需要验证这个授权token是来源于电子商务平台颁发的。对于这种身份认证工作,最好的方法就是用非对称加密技术。

      非对称密钥也称为公开密钥,每个人都有两个唯一配对的密钥:公开密钥(简称公钥)和私人密钥(简称私钥),公钥是对外公开的,私钥由私人秘密保存的;其中一把密钥是用来加密明文的,而另一把密钥是用来解密密文的。非对称密钥加密算法的典型代表是RSA。非对称算法原理如下图4-4所示:

 

     从对称密钥的你分析可知,任何单一密钥都可以实现数据的加密,只要这个单一密钥是保密的。但是非对称密钥由于是一对密钥,每个密钥除了分别能实现加密功能外,和在一起还能实现两外一个更重要的功能:实现数字签名.

      数字签名是非对称密钥技术和数字摘要的结合.

      非对称密钥技术与对称密钥不同。非对称密钥技术的核心就是一个密钥加密,只能用两外一个密钥解密。而对称密钥可以用原密钥解密.

      数字摘要能将任意长度的字符串变成固定长度的字符串,一般采用单项哈希函数来生成。不同的明文摘要成不同密文,同样的明文必然摘要成相同的密文.

      因此,数据签名就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。这个签名无法伪造,因为,只有发送者才有私钥,才能造出独特的加密后的数字串,这样的数字串能通过公钥(任何人都可以拿到的地方)来解密验证这个数字串是来自特定的发送者,因此,这种数字就是一种“签名”

      而为了在现实中运用公钥技术,需要一个载体,在PKI方案中用到了数字证书,这个数字证书就是包含了上面数字签名和验证需要的所有东西,包括,那个数字签名串,公钥等可以公开的信息,打成一个包让信息接收者更容易得到发送者的签名信息。而为了使得证书具有公信力,就产生了CA证书。

      而CA证书的交换是通过SSL协议实现的。电子商务平台本身签发了自己的CA证书。在第三方应用请求电子商务平台服务的时候,电子商务平台会把包含公钥的CA证书发送给第三方应用。

      明白了非对称密钥的基本工作原理,就可以很容易理解授权token的生成方法了。授权token无非是包含特定保密信息的密文。上面提到了密文对应的明文的基本元素。主要有SecretID,用户ID,应用ID。

      一方面,电子商务开放平台在第三方支付应用请求fetchToken的时候,会验证SecretID对应的授权Token是否失效。另一方面,第三方应用拿到密文以后就会用CA证书里面的公钥解开密文,如果能解开,说明密文来自于真正的电子商务平台。接下来比对订单信息。其中SecretID就是唯一标识这笔交易的随机码。如果付款地址里面的SecretID跟密文里面的SecretID一致,说明订单是可靠的。下面对Token的生成过程做简要介绍。

      目前,非对称密钥算法中,RSA是最常见的。密钥产生过程如下:

  1. 首先随机生成两个质数P 和 Q;
  2. 接着把P和Q相乘得到:N=P*Q;
  3. 然后随机生成一个数字keyE,保证2<=keyE<=Φ(N)-1,并且keyE和Φ(N)的最大公约数为1,即GCD(keyE, Φ(N))=1;其中Φ(N)称为N的欧拉函数,值为: Φ(N)=(P-1)*(Q-1)
  4. 最后求解上述最大公约数函数得到keyD,公式是keyD=keyE-1mod (N),其中keyE-1为keyD的逆元;

      因此(N,keyE)是公钥,(N,keyD)是私钥。可以用RSA算法对明文加密或对密文进行解密。对于明文M,用公钥(N,keyE)加密可得到密文C。对于密文C,用私钥(N,keyD)解密可得到明文M。同时,也可以用于签名的情况。   

      本文的授权Token是不需要第三方支付解密,第三方支付得到授权Token以后,返回电子商务平台作为授权凭证。即,对于授权信息明文,用公钥(N,keyE)加密得到授权密文Token;对于授权密文Token,用私钥(N,keyD)解密得到原始的授权信息明文。

      在实际实现过程中,RSA有很多编程语言实现,这里就不详细展开。基本实现方法跟上面一致。至于RSA对于电子商务支付集成的安全性分析会在下一章详细描述。

      这一节主要针对卖家Token,上面分析了,买家token是通过SercetID生成,每次订单支付都是一次授权,而且是10分钟内失效,因此不用考虑过期的维护问题。时效安全性分析将在下一节详细分析。

      根据OAuth协议的概念,token就是允许第三方支付公司提供一个令牌就能获取用户在电子商务平台中的数据,途径就是开放平台API。而用户并不需要提供他在电子商务网站上的用户名和密码给第三方支付公司。这个token让API以用户的名义调用。在返回API具体功能结果前,开放平台会检查这个token的有效性,比如是否过期或者被收回授权了并且验证API请求头的合法性,也就是App Key和Developer Key的合法性。

      在电子商务平台和第三方支付过程中,电子商务平台上卖家的一些信息也需要显示在第三方支付页面上,比如发货人信息等,这些比订单信息更保密的信息需要卖家的授权才能拿到。因此卖家需要做一次OAuth操作来让第三方支付公司得到卖家的一段时间内的授权。

卖家Token的有效期维护

      一般情况下,第三方支付会从卖家登录电子商务网站的时候拿到新的卖家token。这个token一般会在未来的18个月内有效,除非因为一些安全嫌疑,卖家在电子商务网站上主动收回授权。

      Token一般允许2KB长度,而且是基于64位的存储在数据库里面,字符合法性限制如下:

  • a to z, A to Z, 0 to 9
  • 星号,斜杠,加好 ( * / +)

      第三方支付应用汇在至少7天前通知用户token即将失效,如果用户迟迟不更新token导致token失效,那么第三方支付将不能替卖家完成交易。

      如果用户修改了他/她的用户名和密码,这个时候当前的token是不受影响的。第三方支付还是能够正常调用API,即使用户的用户名和密码改了,因此这是一个授权和认证分离的优势。

      在SOAP格式的调用中,7天警告会被返回,字段是HardExpirationWarning。这个字段会在提前7天的API请求的返回包头中。

      在HardExpirationWarning元素中时间是基于GMT标准时间的。但是由于Axis自动把这个时间转化成了本地时间。因此第三方支付需要按照自己客户端的位置来判断。

      Token失效更新的步骤如图4-5所示:

      Token失效相关的错误码定义为如下:

  • Token失效的日期即将到来:也就是上面说的提前7天通知,这里定义的错误码是932。
  • 用户主动收回token权限:用户在电子商务平台中我的账户站点设置第三方授权中选择了收回某第三方支付的授权。这里定义的错误码是16110。
  • 电子商务公司自身收回token的授权:如果XXX 公司检测到一些不正常的账户的安全问题,授权的token会被立即收回,这里定义的授权码是17470。

方案评估

      OAuth的解决方案主要思想就是用token代替用户名和密码,然后对资源的授权进行细化。从而达到认证和授权的分离。

      OAuth 协议( RFC5849 标准)文档上也提出了很多安全考量,其中包括签名方法,客户端授权强度,客户端伪冒的可能性,Access Token保密性,授权码在RedirectUrl中伪造的风险等。

      首先从四个理论层次方面对本文中的基于OAUTH的电子商务和第三方支付集成的解决方案的安全性,包括对签名方法,客户端请求的保密性,授权码在redirectUrl中伪造的风险和授权码SerectID的时效性这四个方面进行详细分析和评估。

      然后从本文开头提出的两个问题方面对本文的解决方案评估分析,来说明本文提出的集成解决方案是否解决了这些问题。

      最后,从实际运行效果方面评估本文的解决方案,并对比传统解决方案,从响应时间和处理失败率两个方面评估和体现本文解决方案的优越性,展示本文解决方案的现实运行状况。

授权Token的签名方法

      授权token利用RSA-SHA1签名的验证客户端请求的方法并不使用相同的密钥来验证授权Token的签名,也就意味着客户端授权的过程中完全依赖于私钥。

      上一章提到了授权token的RSA方式的实现方法。而RSA是公认的比较优秀的非对称算法,这也已经被业界广泛认可,这里简要阐明一下RSA的安全性,并结合电子商务授权Token的实际情况选择分析密钥长度。

      RSA算法已经广泛应用于加密和数字签名中,由于它易于理解和使用,它是到目前为止研究和应用最官方的公钥算法,从它诞生到现在,经历了各种各样攻击的考验。研究发现RSA算法的安全性依赖于密钥的长度。密钥长度对应于保密年限的关系如表5-1所示:

                                           表5-1 RSA密钥长度与保密年限关系表

保密级别

对称密钥长度(bit)

RSA密钥长度(bit)

保密年限

80

80

1024

2010

112

112

2048

2030

128

128

3072

2040

192

192

7680

2080

256

256

15360

2120

 

      RSA的安全性取决于大数分解的复杂度,理论上模数n 越大越安全,但是另一方面如果密钥长度过长,因为RSA是分组加密算法,因此运算代价高,速度慢,与传统的对称密钥DES来说慢了几个数量级,因此对于电子商务支付来说也需要综合考虑一下。

      目前,非对称加密算法1024位的密钥强度已经相当于对称加密算法80位密钥的强度。以当前的软硬件水平,破解1024位的RSA加密密文,需要一套10亿美金的系统使用若干十年的时间,即使在如今云计算的大背景下,短时间内还是很难被破解的。

      但是,电子商务支付过程首要考虑的是安全性问题,而且对于电子商务支付流程来说,增加适当的密钥等级是必要的。

      本文中,电子商务平台用了2048比特的RSA密钥长度,因此在保证速度的前提下,安全性得到了保证。这也是SET(Secure Electronic Transaction)协议中对CA密钥长度的要求。

客户端请求的保密性

      在OAuth提供了一种机制来保证请求的完整性的时候,并没有保证请求的机密性。因此除非系统提供了额外的措施,偷听者能够访问请求的所有内容。因此服务器需要非常小心的处理这些请求是否来自于真正的客户端。因此客户端和服务器之间请求要尽可能的介绍敏感信息的传输。

      本文解决方案中,fetchToken过程中唯一暴露在请求中的信息就是SecretID,对于SecretID的安全性会在下面进行详细分析。

      另一方面,本文解决方案中采用了HTTPS协议,SSL的传输方式一定程度上保证了请求通道的安全性。

      SSL是业内普遍采用的客户端请求保密性手段。本文解决方案也利用了SSL的安全性一定程度上保证了数据的保密性。

      安全套接层协议SSL(Secure Sockets Layer)是网景(Netscape)公司在推出网页浏览器的同时提出的协议。目前版本是3.0,它被广泛用于浏览器和服务器之间的身份认证和加密数据传输。SSL安全协议建立在TCP/IP协议之上,而又独立于应用层协议(HTTP,FTP等),专门为其它应用层协议提供专业的安全服务。例如基于SSL的HTTP应用协议就是经常使用的HTTPS应用协议。

      根据安全协议的主要两个任务,SSL这种安全协议可分为两个阶段:握手阶段和数据传输阶段。握手阶段完成认证过程,数据传输过程保证保密性。如果握手过程中一方发现无法支持另一方选择的加密算法来实现保密性,或者发现数据被篡改,这时通讯方就发出警告,通讯中断,重新协商建立连接。如下图SSL协议结构所下图5-1所示:

      SSL作为一种普遍使用的一种通道加密技术,已经被大部分的网站所支持,因此相对来说SecretID还是保密的,另一方面,由于SecretID本身并不要求保密性,因此本文中的解决方案满足了客户端保密的要求。

授权码在RedirectURL中伪造的风险

         解决方案中的授权码使用了一个叫做SerectID的随机值,这个字符串本身对保密性要求不高,但是对于伪造的要求非常高,因为一个支付交易往往对外标识为一个secretID,要想完成这笔支付交易,必须保证正确的SecretID,如果SecretID被伪造,等于说整个支付交易被伪造。

      SecretID在实现中往往采用UUID来实现,因此这里只要分析UUID重复的几率。

      UUID是全球统一标识符的缩写(Universally Unique Identifier),通常是能保证名字空间到全球范围内生成的标识符唯一。通常具有以下特点:

  • 必须经由计算机的特别算法生成,依赖于某些全球唯一要素

      全球唯一要素通常会通过网卡的MAC地址,或者当前日期和时间戳,或者随机数,时钟时序等,而这些要素的复杂性意味着只能由计算机生成

  • 标识符序列规律不可识别性

      虽然UUID由全球唯一要素经过特殊算法生成,日期和时间戳虽然有大小区别,但是特殊算法混淆的算法已经无法通过任何字面规律来猜测出它与什么东西有关联。

  • 碰撞几率极低

      虽然UUID由计算机生成,长度有限,但是碰撞几率也就是生成两个相同UUID的几率非常低。当然碰撞几率是在一定范围内的,这与它的版本有关。

      一方面,UUID是16个字节长的数字,通常以36个16进制的字符串表示,比如:3F2504E0-4F89-11D3-9A0C-0305E82C3301。UUID具有很多个版本,每个版本的算法不同,应用范围也不同。

      另一方面,由于UUID是由一串16字节长度的数字所构成,因此理论上UUID的总的可能性为216*8=2128个数,换算一下,约等于3.4028 * 1038。也就是说若每纳秒(一纳秒等于0.000001毫秒)产生1百万个UUID数值,也要花50亿年才会将一半的UUID用完。

      UUID的标准型式是十六进制的,一共包含32个数字,分为五段,每段长度形式为8-4-4-4-12,比如:110x341f-x47b-f22a-j15a-393948482123。

      本文解决方案是用JAVA语言实现的,在其随机产生的UUID的128个比特中,通过生日悖论,可计算出两个UUID拥有相同值的几率大约是:

      以下是以计算出n笔UUID后产生碰撞的机率如下表5-2所示:

               表5-2 UUID碰撞几率表

次数N

碰撞几率

236

0.0000000000000004 (4 x 10-16)

241

0.0000000000004 (4 x 10-13)

246

0.0000000004 (4 x 10-10)

      从上面表格可以看出,UUID发生碰撞的概率非常低,如果对比被陨石击中的机率的情况,已知一个人每天被陨石击中的几率估计为六万亿之1,也就是说几率大约是0.0000000000016 (1.6 x 10-12)。也就是说,每毫秒产生一百万笔UUID,每小时产生3.6万亿的UUID,一百年后只产生一次重复的几率也只有二分之一。因此产生重复UUID并造成错误的情况非常低,是故大可不必考虑此问题。

SecretID的时效性分析

      第三章中提到,对信息资源授权是跟具体资源绑定,同时讨论了资源的粒度如何划分的问题,因为订单支付信息是基于用户的,也是基于第三方支付应用的,同时还是基于每个订单交易号的。所以这里可以有两种授权方式:

  • 用户对该第三方支付方式下的每个订单进行授权
  • 用户对该第三方支付方式下的最新一笔订单进行授权

      可以看到前者可以实现对更多资源的同时授权,需要授权token跟订单号绑定。而后者只是对最后一笔订单授权,因此授权token只要跟应用ID绑定就可以了,因为知道应用ID就知道该应用下这个用户的最新一笔订单。

      因此本文采用了更新操作,即用户对该第三方支付方式下的最新一笔订单进行授权。

      但是,即使是最新一笔订单进行授权,这个授权也不是永久有效的。因此,这里需要设定一个超时时间,这就是为什么在授权表中设置一个HARD_EXPIRE_TIME的原因。目前,实际运行中,这个时间设置为当前时间后10分钟内。这里做了一个统计,一般用户都能在10分钟内完成支付交易。否则,如果支付交易操作时间超过了10分钟,即使用户secretID被复制,比如用户在上午请求了这个SecretID,但是忘了完成支付,等到下午想继续完成支付,这个时候fetchToken会失败,因为SecretID已经无效,即使第三方应用cache了授权token,因为授权token也已经失效,因此相应的getOrderDetails也不能成功,因为token是无效的,进而支付也无法完成。

      本文开头提出,在现有的电子商务支付集成中,主要存在两个问题,描述如下:

  1. 每次系统集成的方式基本上都不同,都需要重新开发,研发成本高;
  2. 一旦被攻击,就是全局的影响,对于海量的电子商务交易来说,不仅性能有影响,而且认证和授权不能达到更细的粒度,例如不能实现单个买家对单个卖家的临时授权等。

      接下来,通过分析基于OAuth的电子商务支付集成方案,来对于这两个问题是否得到了解决情况进行评估。

      对于第一个问题,在现有的电子商务支付过程中,由于系统与系统之间接口的不确定性,每次集成的方式往往都不同,这些不同随着技术的发展慢慢的在改进。一开始由于语言的不同,导致很多平台与平台之间不能方便的互通,比如JAVA语言的平台就很难与.NET平台方便快捷的交互和信息共享,因此往往只能通过文件或者数据流这些操作系统间的标准形式实现互联。

      后来,由于Web Service的出现,很好的解决了不同语言之间的系统集成的问题,因为Web Service基于接口数据结构而不基于特定语言,同时又是标准的HTTP网络协议,因此系统集成上不再是问题。

      再后来,数据结构本身的不统一或者数据处理流程的不同意也造成了系统集成难以集成过程千人千面。所以每次集成开发总免不了大动干戈,数据适配,安全认证方式都不一样,因此开发成本很高。

      运用了OAuth和开放平台以后,如第三章的图3-3所示,对于电子商务开放平台将数据标准化,OAuth授权方式将安全认证标准化。这样,对于电子商务平台来说,几乎不需要重新开发,只需要简单注册新的第三方支付公司到电子商务平台就能实现集成信息交互。而对于第三方支付公司来说,由于都是标准协议和流程,对于OAuth协议,很多语言都已经很好的支持了。因此集成过程并不复杂。所以,这个开发成本问题在本文的集成解决方案中得到了很好的解决。

      本文解决方案中设计了分别针对于授权的fetchToken接口和对于资源获取的getXXXTransactions接口。对于资源的获取和授权分别处理,清晰的接口职责,在技术上实现了数据的标准化,因此技术集成统一,开发简单,开发周期短。一次开发,多次使用。

      比如对于RuName的注册机制使得在新加入第三方支付公司的项目中,电子商务平台不再需要任何开发,只需要注册一个电子商务合法的应用ID和名字,通过配置来增加新的第三方应用。而RuName本身也是第三方应用自己可以完成的,就像用户注册一样。

      另一方面,由于整个流程上是OAuth的原理,因此对于新的应用开发者来说也是非常容易理解的。比如对于授权码和授权Token,以及授权Token换取资源等概念。

      另外一个问题是关于授权粒度的问题,对于一个系统与系统之间授权来说,传统的方式是一个系统给另外一个系统分配协商密钥或者颁发证书,然后系统之间的通讯就按照这个证书或者密钥进行授权,而这种授权都是全局的授权。也就是说,凡是来自对方系统的经过证书签名的都是可靠的。

       而这很难满足电子商务支付集成的需要,因为一个电子商务平台上并不是所有的数据都可以授权给第三方支付公司的,比如,一个第三方公司只接受某个国家的订单支付请求,或者只接收特定用户群的订单支付请求,那么电子商务平台就不必授权所有其他的数据给这个第三方支付公司了。这本身也是数据安全方面的考量。一旦这个第三方支付公司出现不安全,那么真个电子商务平台上的数据就有可能不安全,是全局的。

      另一方面,由于是全局的认证授权方式要求交换密钥,因此密钥定期更换问题也是需要考虑的问题,而定期更换密钥需要人员维护。这在某种程度上导致了应用系统难以维护的问题。

      而本文基于OAuth的解决方案很好的解决了这个问题。由于OAuth授权是基于资源的,而资源是可以划分为细粒度的,比如本文的资源都是买卖双方的订单信息,在授权的过程中,电子商务平台按照买家的意思临时授权给第三方支付公司一个token,第三方支付公司只能按照这个token获取指定订单信息,至于其他的订单信息是无权访问的。而这个授权过程就是就是上一章中描述的过程。这个过程很好的控制了资源的授权的细粒度的问题。因此,本文的解决方案很好的实现了资源授权细粒度的问题,解决了授权安全的问题。

      在本文解决方案中,SecretID首先是根据AppID定位的,因此只能通过当前的第三方应用发送SecretID来获取授权Token,从而获取相应资源。而对于其它第三方应用来说,当前的SecretID根本就不能使用,即使被截取,也无法通过fetchToken的验证来换取授权Token.因此不同的第三方应用不能共享授权,授权粒度达到了应用级别。

      其次,SecretID根据UserID定位的,因此对于当前第三方应用来说,只能通过当前的SecretID获取相应用户资源的授权Token,而无法获取其他用户资源的授权Token。而授权Token本身在getXXXTransaction被调用资源的时候,如果请求的资源超出了该授权Token的范围,那么同样拿不到授权以外的资源。因此授权粒度达到了用户级别。

      再者,SecretID针对于支付交易设计,本身具有有效期,在第三方应用根据获取授权Token以后,该授权Token的生存时间开始计时,如果在规定时间内没有去资源服务器获取资源,那么过了有效期以后(本文设计的是10分钟),授权Token就失效了。需要重新生成SecretID来授权。因此授权粒度达到了交易级别。

      根据后台日志统计,贝宝的三个API的平均耗时分别是:setExpressCheckout是500到1000毫秒,getExpressCheckout是500到1000毫秒,doExpressCheckout最慢,大概在5000毫秒左右。而在节假日等大访问量的情况下,更会造成很多超时,有相当多的doExpressCheckout操作会在10秒以上。

      在快速结帐模式下,每个操作都必须有数据交互,大量数据的交互必然带来集成开发效率和数据传输效率。

      然而在运用OAuth的方式以后,新的支付集成方式完全不同于与传统的快速结帐模式。在电子商务网站结帐平台上,完全脱离了支付平台的交互。只有在真正需要第三方支付参与的情况下,才进行交互。而且交互是通过OAuth在电子商务开放平台上进行的。

      根据线上日志统计,采用了OAuth的解决方案以后,初始化订单支付操作只需要100毫秒以内,验证订单支付操作需要50毫秒左右,最后的确认订单支付操作也不过是1000毫秒左右。而在节假日等大访问量的情况下,也不会出现系统与系统之间交互会出现的超时问题,每一步操作最坏情况下也在可控范围内,根据统计在10%处理时间增加,而这些都是可以通过云平台扩展服务器节点的方式优化。传统方式处理速度和OAuth方式处理速度的比较如下表5-3所示:

                                      表5-3 OAuth方式处理速度和传统方式处理速度比较表

95%交易统计

传统方式(贝宝)处理时间(毫秒)

OAuth方式处理时间(毫秒)

初始化订单支付

668.42

66.86

验证订单支付

613.85

46.27

确认订单支付

5180.74

1038.45

      上述表格数据得出,OAuth的方式处理时间有明显提升,因为OAuth大大降低了系统集成的复杂度。当然对于传统方式,由于交易量巨大,因此处理时间难免有一些表现不好,但是,从上面数据看,对于处理方式的改变,处理时间上还是有非常大的改善的。

      另外一方面,根据内部日志统计, 贝宝的三个API的平均失败率分别是:setExpressCheckout是0.3%左右,getExpressCheckout是0.25%,doExpressCheckout失败率最高,大概在4%左右。而在节假日等大访问量的情况下,更会造成很多超时失败。传统方式处理失败率和OAuth方式处理失败率的比较如下表5-4所示:

                            表5-4 OAuth方式处理失败率和传统方式处理失败率比较表

95%交易统计

传统方式(贝宝)处理失败率

OAuth方式处理失败率

初始化订单支付

0.30%

~0%

验证订单支付

0.25%

~0%

确认订单支付

3.69%

~0%

      从上面表数据看到,运用OAuth以后,日志显示的操作失败率显著下降,初始化订单支付操作失败率几乎为零,验证订单支付操作需要,最后的确认订单支付操作也不过是1000毫秒左右。而在节假日等大访问量的情况下,也不会出现系统与系统之间交互会出现的超时问题。

      所有这些优势,都是因为所有的操作都是在电子商务和开放平台资源服务器之间的交互,而这些交互是系统内部交互,共享的是同一个数据库,因此互相授权相对简单。

      由于支付在电子商务平台中要求非常高,项目实施过程相对要非常细致,从需求分析,详细设计和架构实现,都是一个不断完善的过程。这期间,参与项目的架构师,产品经理,工程师、第三方支付供应商来自世界各地,他们的专家也付出了艰辛的努力,才使项目得以顺利运行。

      该方案上线以后,交易过程实现零投诉。对于后续的第三方支付公司来说,只要按照类似的方案,根据国际标准的OAuth实现就能实现跟电子商务平台的支付对接,对于电子商务公司来说无需任何的开发,只要简单的配置开通就可以了。

      方案中还有一些需要改进的地方,作者认为应该围绕以下方面展开:

  • 每次信息查询过程始终需要fetchToken来实现,在网络不好的情况下,两次请求容易造成失败,需要用户重试,用户体验需要提高。因此,是否可以通过缓存accessToken的方式使得一定时间内同样的交易无需重复fetchToken或者通过卖家token来获取,因为订单信息也同样属于卖家。
  • ReviseCheckoutStatus这个支付结果通知过程。由于各个第三方支付公司对于交易的状态的不一致性,比如有些第三方支付公司对信用卡预授权成功即标志着支付成功,而有些第三方支付公司对信用卡支付需要在入账以后才能标志成功。以及第三方支付公司是国际公司,各个国家的法律制度的不同,造成支付状态和手续费的差异都是的ReviseCheckoutStatus 接口异常复杂。因此,后续会用新的方式来处理,不如统一为消息通知平台,各种用例用不同的适配器来代替等,以提高处理效率,降低维护复杂度。

      总之,建立一个即要安全可靠,又要便于开发维护的高效的电子商务支付集成方案不止OAUTH这一种,但是本文的基于OAUTH的方案到目前为止,无疑是一种可行的优秀的方案。



相关文章:

  • 支付系统监控实践
  • 改进你的网页
  • 在开发测试中使用HBaseMiniCluster
  • 使用JProfiler进行性能调优
  • DBA的新领域:调试Oracle(进阶篇)
  • Comet框架Pushlets的集成
  • 如何定制一个基于REST Service的ODBC驱动程序
  • Maven依赖版本冲突报告
  • Maven中的扁平化POM
  • 你好,HBase
  • Maven Build Tracking
  • 分布式文件系统概述
  • 调试Oracle 之一 基础篇
  • 基于Apache Mesos 构建高可靠,高可用的Jenkins CI
  • Kepler性能分析之M2E调优
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • 03Go 类型总结
  • android 一些 utils
  • canvas 高仿 Apple Watch 表盘
  • ECMAScript入门(七)--Module语法
  • ES6系列(二)变量的解构赋值
  • Go 语言编译器的 //go: 详解
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • Laravel 菜鸟晋级之路
  • Phpstorm怎样批量删除空行?
  • 编写高质量JavaScript代码之并发
  • 聚类分析——Kmeans
  • 收藏好这篇,别再只说“数据劫持”了
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 以太坊客户端Geth命令参数详解
  • 翻译 | The Principles of OOD 面向对象设计原则
  • ​如何在iOS手机上查看应用日志
  • (2)STM32单片机上位机
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (附源码)python旅游推荐系统 毕业设计 250623
  • .NET BackgroundWorker
  • .net core Swagger 过滤部分Api
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .net 程序 换成 java,NET程序员如何转行为J2EE之java基础上(9)
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • @staticmethod和@classmethod的作用与区别
  • [.NET]桃源网络硬盘 v7.4
  • []新浪博客如何插入代码(其他博客应该也可以)
  • [100天算法】-实现 strStr()(day 52)
  • [bug总结]: Feign调用GET请求找不到请求体实体类
  • [Django 0-1] Core.Handlers 模块
  • [GN] Vue3快速上手1
  • [HTML]Web前端开发技术28(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页
  • [iOS]-NSTimer与循环引用的理解
  • [Java基础]—JDBC
  • [leetcode] Multiply Strings
  • [PHP]pearProject协作系统 v2.8.14 前后端
  • [python] `sys.settrace` 跟踪函数
  • [saiku] olap数据源管理