我目前正在为.net开发一个REST库,我想听听一些关于我的开放点的看法:REST和身份验证.
以下是与库一起使用的RESTful接口的示例:
[RestRoot("/user")] public interface IUserInterface { [RestPut("/")] void Add(User user); [RestGet("/")] int[] List(); [RestGet("/get/{id}")] User Get(int id); [RestDelete("/delete/{id}")] void Delete(int id); }
然后,服务器代码只实现接口,客户端可以通过工厂获得相同的接口.或者,如果客户端未使用库,则标准HTTP请求也可以.
我知道有一些主要方法可以使用HTTP Basic Auth或向需要经过身份验证的用户的请求发送令牌.
第一种方法(HTTP Basic Auth)有以下问题(部分是Web浏览器特定的):
密码随每个请求一起传输 - 即使使用SSL,这也会产生某种"不良感觉".
由于密码是使用请求标头传输的,因此本地攻击者很容易查看传输的标头以获取密码.
密码在浏览器内存中可用.
没有标准的方法来使用户"会话"到期.
使用浏览器登录会中断页面的外观.
第二种方法的问题更侧重于实现和库使用:
需要身份验证的每个请求URI都必须具有令牌的参数,这只是非常重复.
如果每个方法实现需要检查令牌是否有效,则需要编写更多代码.
该界面将变得不那么具体如[RestGet("/get/{id}")]
对[RestGet("/get/{id}/{token}")]
.
放置令牌的位置:在URI的末尾?根之后?别的地方?
我的想法是将令牌作为参数传递给URL http:/server/user/get/1234?token=token_id
.
另一种可能性是将参数作为HTTP头发送,但这会使普通HTTP客户端的使用变得复杂.
令牌将作为每个请求的自定义HTTP标头("X-Session-Id")传递回客户端.
然后,这可以完全从接口中抽象出来,并且任何需要身份验证的实现都可以询问令牌(如果给定)属于哪个用户.
你认为这会过多地违反REST还是你有更好的想法?
我倾向于认为身份验证详细信息属于标头,而不是URI.如果您依赖于URI上的令牌,那么应用程序中的每个URI都需要进行编码以包含令牌.它还会对缓存产生负面影响.具有不断变化的令牌的资源将不再能够被缓存.资源相关信息属于URI,而不是与证书相关的应用程序相关数据.
您似乎必须将Web浏览器定位为客户端?如果是这样,您可以调查使用HTTP摘要访问身份验证或向客户端发出自己的SSL证书来唯一标识和验证它们.另外,我认为会话cookie不一定是坏事.特别是在不得不处理浏览器时.只要您隔离cookie处理代码并使应用程序的其余部分不依赖它,您就可以了.关键是只在会话中存储用户的身份,没有别的.不要滥用服务器端会话状态.
如果您的目标客户端不是浏览器,那么您可以采取多种方法.我很幸运使用了亚马逊的S3身份验证机制.
当然,这一切都是非常主观的.纯粹和遵循REST的信件有时可能是不切实际的.只要您最小化并隔离此类行为,应用程序的核心仍然可以是RESTful.我强烈建议将RESTful Web服务作为REST信息和方法的重要来源.
我同意workmad3,如果需要维护会话生存时间,则应创建会话资源.使用用户凭据(基本身份验证或正文内容中的凭据)在该资源上发布将返回唯一会话ID.在/ session/{id}上删除将注销用户.
如果要控制会话到期时间.创建新会话(会话资源上的帖子)时,服务器将在响应上设置cookie(使用标准的set-cookie标头).cookie将包含到期时间.cookie字符串应该在服务器上加密,因此只有服务器可以打开该cookie.对服务器的每个后续请求都将在cookie头中发送会话cookie.(如果您的客户是浏览器,它将自动为您完成).服务器需要为每个请求"更新"cookie,即创建具有新到期时间的新cookie(延长会话超时).当用户在会话资源上调用delete时,请记住清除cookie.
如果您希望应用程序更安全,则可以将客户端IP存储在cookie本身中,因此当请求到达时,服务器可以验证它是从"原始"客户端发送的.但请记住,当涉及代理时,此解决方案可能会出现问题,因为服务器可能会"看到"所有请求来自同一客户端.