REST无状态约束

无状态约束

  在Roy Fielding的论文中,其为REST添加了一个无状态约束:

 

We next add a constraint to the client-server interaction: communication must be stateless in nature … such that each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client.

 

  从上面的陈述中可以看到,在一个REST系统中,用户的状态会随着请求在客户端和服务端之间来回传递。这也便是REST这个缩写中ST(State Transfer)的来历。

  为REST系统添加这个约束有什么好处呢?主要还是基于集群扩展性的考虑。如果REST服务中记录了用户相关的状态,那么在集群中,这些用户相关的状态就需要及时地在集群中的各个服务器之间同步。对用户状态的同步将会是一个非常棘手的问题:当一个用户的相关状态在一个服务器上发生了更改,那么在什么时候,什么情况下对这些状态进行同步?如果该状态同步是同步进行的,那么同时刷新多个服务器上的用户状态将导致对用户请求的处理变得异常缓慢。如果该同步是异步的,那么用户在发送下一个请求时,其它服务器将可能由于用户状态不同步的原因无法正确地处理用户的请求。除此之外,如果集群进行了不停机的横向扩展,那么用户状态的同步需要如何完成?这些实际上都是非常难以处理的问题。

  但是现有的很多较为流行的技术及规范实际上都没有限制用户的请求是无状态的。相信您知道,一个技术或规范实际上都拥有一个生态圈。在该生态圈之内的各技术之间可以较好地契合在一起。尤其是,有些技术实际上就会以该生态圈中的核心技术或规范所建立的假设之上来实现自己的功能。如果希望禁止该假设,那么让某些技术工作起来就是非常困难的事情了。

  就以搭建基于HTTP的REST服务为例。在HTTP中,一个重要的功能就是Cookie和Session的使用(RFC6265)。该功能会在服务器里保留一个状态。因此在一个基于HTTP的REST系统中,我们常常需要避免使用这些在服务器里面保留状态的技术。但是某些技术,如用户的登陆,实际上常常需要在服务器中添加一个状态。

  所以在stackoverflow中,我们常常会看到有人问:我现在使用了这样一种解决方案。这样实现是不是RESTful?此时一些人就会说,这不是RESTful。但是pure RESTful和almost RESTful之间的区别主要还是在于一个是理论,一个是工程。在工程中,轻微地违反了一个准则并不一定代表这个解决方案一无是处。而是要看遵守该准则和轻微地违反了该准则之后工作量的大小以及后期的维护成本:之所以提出一系列准则,那是因为遵守该准则拥有一定的好处。如果对该准则的轻微违反可以减少大量的工作量,而且遵守准则的好处并没有消失,或者是通过另一样技术可以快速地重新获得该好处,那么对准则的轻微违反是值得的。

 

Authentication

  其实在上一节中,我们已经提出了无状态约束给REST实现带来的麻烦:用户的状态是需要全部保存在客户端的。当用户需要执行某个操作的时候,其需要将所有的执行该请求所需要的信息添加到请求中。该请求将可能被REST服务集群中的任意服务器处理,而不需要担心该服务器中是否存有用户相关的状态。

  但是在现有的各种基于HTTP的Web服务中,我们常常使用会话来管理用户状态,至少是用户的登陆状态。因此,REST系统的无状态约束实际上并不是一个对传统用户登录功能友好的约束:在传统登陆过程中,其本身就是通过用户所提供的用户名和密码等在服务端创建一个用户的登陆状态,而REST的无状态约束为了横向扩展性却不想要这种状态。而这也就是为基于HTTP的REST服务添加身份验证功能的困难之处。

  为了解决该问题,最为经典也最符合REST规范的实现是在每次发送请求的时候都将用户的用户名和密码都发送给服务器。而服务器将根据请求中的用户名和密码调用登陆服务,以从该服务中得到用户所对应的Identity和其所具有的权限。接下来,在REST服务中根据用户的权限来访问资源。

  这里有一个问题就是登陆的性能。随着系统当前的加密算法越来越复杂,登陆已经不再是一个轻量级的操作。因此用户所发送的每次请求都要求一次登陆对于整个系统而言就是一个巨大的瓶颈。

  在当前,解决该问题的方法主要是一个独立的缓存系统,如整个集群唯一的登陆服务器。但是缓存系统本身所存储的仍然是用户的登陆状态。因此该解决方案将仍然轻微地违反了REST的无状态约束。

  还有一个类似的方法是通过添加一个代理来完成的。该代理会完成用户的登陆并获得该用户所拥有的权限。接下来,该代理会将与状态有关的信息从请求中删除,并添加用户的权限信息。在经过了这种处理之后,这些请求就可以转发到其后的各个服务器上了。转发目的地所在的服务器则会假设所有传入的请求都是合法的并直接对这些请求进行处理。

  可以看到,无论是一个独立的登陆服务器还是为整个集群添加一个代理,系统中都将有一个地方保留了用户的登陆状态。这实际上和在集群中对会话集中进行管理并没有什么不同。也就是说,我们所尝试的通过禁止使用会话来达成完全的无状态并不现实。因此在一个基于HTTP的REST服务中,为登陆功能使用集中管理的会话是合理的。

  既然我们放松了对REST系统的无状态约束,那么一个REST系统所可以使用的登陆机制将主要分为以下两种:

  1.   基于HTTPS的Basic Access Authentication

其好处是其易于实现,而且主流的浏览器都提供了对该功能的支持。但是由于登陆窗口都是由浏览器所提供的,因此其与产品外观有很大不同。除此之外,浏览器都没有提供登出的功能,也没有提供找回密码等功能。

  2.   基于Cookie及Session的管理

在使用Cookie来管理用户的注册状态的时候,其实际上就是将服务端所返回的Cookie在每次发送请求的时候添加到请求中。虽然说这个Cookie并非存储了用户应用的状态,但是其实际存储了用户的登陆状态。因此客户端的角度来讲,由服务端管理的Session并不符合REST所倡导的无状态的要求。

  可以说,上面的两种方法各有优劣。可能第二种方法从客户端的角度看来并不是RESTful的,但是其优势则在于很多类库都直接提供了对该功能的支持,从而简化了会话管理服务器的实现。

  在这里顺便提一句,如果项目足够大,将一些SSO产品集成到服务中也是不错的选择。

 

摘抄自:http://www.cnblogs.com/loveis715/p/4669091.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值