RESTful身份验证的含义是什么?它是如何工作的?我无法在Google上找到一个很好的概述.我唯一的理解是你在URL中传递会话密钥(remeberal),但这可能是非常错误的.
如何在RESTful客户端 - 服务器架构中处理身份验证是一个有争议的问题.
通常,可以通过以下方式在SOA over HTTP世界中实现:
通过HTTPS进行HTTP基本身份验证;
Cookie和会话管理;
HTTP标头中的令牌(例如OAuth 2.0 + JWT);
使用其他签名参数查询身份验证
您必须调整甚至更好地混合这些技术,以最好地匹配您的软件架构.
每种身份验证方案都有自己的PRO和CON,具体取决于安全策略和软件体系结构的用途.
通过HTTPS进行HTTP基本身份验证
大多数Web服务都使用基于标准HTTPS协议的第一个解决方案.
GET /spec.html HTTP/1.1 Host: www.example.org Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
它很容易实现,默认情况下在所有浏览器上都可用,但是有一些已知的缺点,比如浏览器上显示的糟糕的身份验证窗口,它会持续存在(这里没有类似LogOut的功能),一些服务器端额外的CPU消耗,以及将用户名和密码(通过HTTPS)传输到服务器的事实(在键盘输入期间让密码仅保留在客户端,并在服务器上存储为安全散列应该更安全) .
我们可能会使用摘要式身份验证,但它也需要HTTPS,因为它容易受到MiM或Replay攻击,并且特定于HTTP.
通过Cookies会话
说实话,在服务器上管理的会话并非真正无状态.
一种可能性是维护cookie内容中的所有数据.而且,在设计上,该cookie将被服务器端的处理(客户端,事实上,甚至不试图解释这个cookie数据:它只是手回服务器上的每个连续的要求).但是这个cookie数据是应用程序状态数据,因此客户端应该在纯粹的无状态世界中管理它,而不是服务器.
GET /spec.html HTTP/1.1 Host: www.example.org Cookie: theme=light; sessionToken=abc123
cookie技术本身是HTTP链接的,所以它不是真正的RESTful,它应该是独立于协议的,恕我直言.它易受MiM或Replay攻击.
通过令牌(OAuth2)授予
另一种方法是在HTTP标头中放置一个令牌,以便对请求进行身份验证.例如,这就是OAuth 2.0的功能.请参阅RFC 6749:
GET /resource/1 HTTP/1.1 Host: example.com Authorization: Bearer mF_9.B5f-4.1JqM
简而言之,这与cookie非常相似,并且遇到同样的问题:不是无状态,依赖于HTTP传输细节,并且受到许多安全漏洞的影响 - 包括MiM和Replay - 所以只能通过HTTPS使用.通常,JWT用作令牌.
查询验证
查询身份验证包括通过URI上的一些其他参数对每个RESTful请求进行签名.请参阅此参考文章.
它在本文中定义如下:
必须通过使用私有凭证作为签名标记对以小写字母顺序排序的查询参数进行签名来验证所有REST查询.应在URL编码查询字符串之前进行签名.
这种技术可能与无状态架构更兼容,也可以通过轻型会话管理(使用内存中会话而不是数据库持久性)来实现.
例如,以下是上面链接中的通用URI示例:
GET /object?apiKey=Qwerty2010
应该这样传播:
GET /object?timestamp=1261496500&apiKey=Qwerty2010&signature=abcdef0123456789
正在签名的字符串是/object?apikey=Qwerty2010×tamp=1261496500
,并且签名是使用API密钥的私有组件的该字符串的SHA256哈希.
服务器端数据缓存始终可用.例如,在我们的框架中,我们在SQL级别而不是在URI级别缓存响应.因此,添加此额外参数不会破坏缓存机制.
有关基于JSON和REST的客户端 - 服务器ORM/SOA/MVC框架中的RESTful身份验证的一些详细信息,请参阅此文章.既然我们允许通信不仅通过HTTP/1.1,还命名管道或GDI消息(本地),我们试图实现一个真正的RESTful的认证模式,而不是依赖于HTTP特殊性(如头部或饼干).
稍后注意:在URI中添加签名可能被视为不良做法(因为例如它将出现在http服务器日志中),因此必须减轻它,例如通过适当的TTL来避免重放.但是,如果您的http日志遭到破坏,您肯定会遇到更大的安全问题.
在实践中,即将到来的OAuth 2.0的MAC令牌认证可能是关于"由令牌授予"当前方案的巨大改进.但这仍然是一项正在进行的工作,并且与HTTP传输有关.
结论
值得推断的是,REST不仅基于HTTP,即使在实践中,它也主要通过HTTP实现.REST可以使用其他通信层.因此,无论谷歌如何回答,RESTful身份验证都不仅仅是HTTP身份验证的同义词.它根本不应该使用HTTP机制,但应从通信层中抽象出来.如果您使用HTTP通信,由于Let's Encrypt计划,没有理由不使用正确的HTTPS,这是任何身份验证方案所必需的.
我怀疑那些热心地喊"HTTP身份验证"的人是否尝试过用REST创建一个基于浏览器的应用程序(而不是机器到机器的Web服务)(没有冒犯的意图 - 我只是觉得他们没有遇到过复杂的问题) .
我在RESTful服务上使用HTTP身份验证时发现的问题是生成可在浏览器中查看的HTML页面:
用户通常会得到一个丑陋的浏览器制作的登录框,这对用户不友好.你不能添加密码检索,帮助框等.
以不同的名称注销或登录是一个问题 - 浏览器将继续向站点发送身份验证信息,直到您关闭窗口
超时很难
铲球这些逐点非常有见地的文章是在这里,但是这导致了很多特定浏览器的JavaScript两轮牛车,变通办法变通办法,等等的.因此,它也不是向前兼容的,因此在发布新浏览器时需要不断维护.我不认为干净清晰的设计,而且我觉得这是一项额外的工作和头痛,所以我可以热情地向我的朋友展示我的REST徽章.
我相信cookie是解决方案.但等等,饼干是邪恶的,不是吗?不,他们不是,饼干经常使用的方式是邪恶的.Cookie本身只是一条客户端信息,就像浏览器在浏览时会跟踪的HTTP身份验证信息一样.这条客户端信息在每次请求时都会发送到服务器,就像HTTP身份验证信息一样.在概念上,唯一的区别是所述内容这块客户端状态中的可以由确定服务器作为其响应的一部分.
通过使用以下规则使会话成为RESTful资源:
一个会话映射一个关键用户ID(也可能是最后行动时间戳超时)
如果存在会话,则表示该密钥有效.
登录意味着POST到/ sessions,新密钥被设置为cookie
注销意味着删除/ sessions/{key}(使用重载的POST,记住,我们是浏览器,HTML 5还有很长的路要走)
通过在每个请求时将密钥作为cookie发送并检查会话是否存在且有效来完成身份验证
现在,与HTTP身份验证的唯一区别在于,身份验证密钥由服务器生成并发送给不断发送回来的客户端,而不是客户端从输入的凭据计算它.
converter42补充说,当使用https(我们应该)时,重要的是cookie将设置其安全标志,以便永远不会通过非安全连接发送身份验证信息.好极了,自己没见过.
我认为这是一个运行良好的充分解决方案,但我必须承认,我不足以识别此方案中的潜在漏洞 - 我所知道的是,数百个非RESTful Web应用程序使用的基本相同登录协议(PHP中的$ _SESSION,Java EE中的HttpSession等).cookie头内容仅用于寻址服务器端资源,就像接受语言可能用于访问翻译资源一样,等等.我觉得它是一样的,但也许其他人不一样?你们觉得怎么样?
这里的好人已经对这个主题说了足够的话.但这是我的2美分.
有两种互动模式:
人机对话(HTM)
机器对机器(MTM)
机器是公共分母,表示为REST API,演员/客户端是人或机器.
现在,在一个真正的RESTful架构中,无状态的概念意味着必须为每个请求提供所有相关的应用程序状态(意味着客户端状态).相关的是,REST API需要处理请求并提供适当的响应.
当我们在人机对应用环境中考虑这一点时,Skrebbel在上面指出的"基于浏览器",这意味着在浏览器中运行的(web)应用程序需要在每个请求中发送其状态和相关信息它使后端REST API.
考虑一下:您有一个REST API的数据/信息平台公开资产.也许您有一个处理所有数据立方体的自助BI平台.但是您希望您的(人类)客户通过(1)Web应用程序,(2)移动应用程序和(3)某些第三方应用程序访问此用户.最后,即使是MTM链也可以通向HTM - 对.因此,人类用户仍然处于信息链的顶端.
在前两种情况下,您有一个人机交互的案例,这个信息实际上是由人类用户消费的.在最后一种情况下,您有一个使用REST API的机器程序.
身份验证的概念适用于所有人.您将如何设计此类以便以统一,安全的方式访问REST API?我看到这个的方式,有两种方式:
方式1:
首先没有登录.每个请求都执行登录
客户端向每个请求发送其标识参数+请求特定参数
REST API接受它们,转身,ping用户存储(无论是什么)并确认auth
如果auth建立,请为请求提供服务; 否则,使用适当的HTTP状态代码拒绝
对目录中所有REST API的每个请求重复上述操作
方法2:
客户端以auth请求开始
登录REST API将处理所有此类请求
它接受auth参数(API密钥,uid/pwd或您选择的任何内容)并验证用户存储(LDAP,AD或MySQL DB等)的身份验证
如果已验证,则创建一个身份验证令牌并将其交还给客户端/呼叫者
然后,调用者将此auth令牌+请求特定参数与每个后续请求一起发送到其他业务REST API,直到注销或直到租约到期为止
显然,在Way-2中,REST API需要一种方法来识别和信任令牌有效.Login API执行了auth验证,因此目录中的其他REST API需要信任"valet密钥".
当然,这意味着需要在REST API之间存储和共享身份验证密钥/令牌.这个共享的,可信任的令牌存储库可以是本地/联合的,允许来自其他组织的REST API相互信任.
但我离题了.
关键是,需要维护和共享"状态"(关于客户端的身份验证状态),以便所有REST API都可以创建一个信任圈.如果我们不这样做,即Way-1,我们必须接受必须对进入的任何/所有请求执行认证行为.
执行身份验证是一个资源密集型过程.想象一下,针对每个传入请求,针对您的用户存储执行SQL查询以检查uid/pwd匹配.或者,加密并执行哈希匹配(AWS样式).从架构上讲,我怀疑每个REST API都需要使用常见的后端登录服务来执行此操作.因为,如果你不这样做,那么你到处乱丢auth代码.一团糟.
层次越多,延迟越多.
现在,采用Way-1并应用于HTM.如果您必须发送uid/pwd/hash或其他任何请求,您的(人类)用户是否真的关心?不,只要你不打扰她每秒抛出auth /登录页面.如果你这样做,祝客户好运.因此,您要做的是将登录信息存储在客户端的某个位置,在浏览器中,在开头,并在每次发出请求时发送.对于(人类)用户,她已经登录,并且"会话"可用.但实际上,她会根据每个请求进行身份验证.
与Way-2相同.您的(人类)用户永远不会注意到.所以没有伤害.
如果我们将Way-1应用于MTM怎么办?在这种情况下,由于它是一台机器,我们可以通过要求它提交每个请求的认证信息来解决这个问题.没人在乎!在MTM上执行Way-2不会引起任何特殊反应; 它是一台该死的机器.它可以少关心!
真的,问题是什么适合你的需要.无国籍状态需要付出代价.付出代价继续前进.如果你想成为一个纯粹主义者,那么也为此付出代价,继续前进.
最后,哲学并不重要.真正重要的是信息发现,演示和消费体验.如果人们喜欢你的API,你就完成了自己的工作.
这是一个真正完全RESTful的身份验证解决方案:
在身份验证服务器上创建公钥/私钥对.
将公钥分发给所有服务器.
当客户端进行身份验证时:
3.1.发出包含以下内容的令牌:
到期时间
用户名(可选)
用户IP(可选)
密码哈希(可选)
3.2.使用私钥加密令牌.
3.3.将加密的令牌发送回用户.
当用户访问任何API时,他们还必须传递其身份验证令牌.
服务器可以通过使用auth服务器的公钥解密来验证令牌是否有效.
这是无状态/ RESTful身份验证.
请注意,如果包含密码哈希,则用户还将发送未加密的密码以及身份验证令牌.服务器可以通过比较哈希来验证密码是否与用于创建身份验证令牌的密码匹配.使用HTTPS之类的安全连接是必要的.客户端的Javascript可以处理获取用户密码并将其存储在客户端,可以存储在内存中,也可以存储在cookie中,可能使用服务器的公钥进行加密.
说实话,我在这里看到了很好的答案,但是让我感到困扰的是当有人将整个无状态概念推向一个极端的教条时.它让我想起那些只想拥抱纯OO的老Smalltalk粉丝,如果某些东西不是一个对象,那么你做错了.给我一个休息时间.
RESTful方法应该让您的生活更轻松,减少会话的开销和成本,尝试遵循它,因为这是明智的做法,但是你遵循一个学科(任何学科/指南)到极端的那一刻不再提供预期的好处,那么你做错了.今天一些最好的语言同时具有函数式编程和面向对象.
如果您解决问题的最简单方法是将身份验证密钥存储在cookie中并在HTTP标头上发送,那么请执行此操作,只是不要滥用它.请记住,如果会话变得沉重而且很大,如果所有会话都包含一个包含密钥的短字符串,则会有什么大不了的事情?
我愿意接受评论中的更正,但我只是没有看到(到目前为止)让我们的生活变得悲惨的一点,只是避免在我们的服务器中保留一个大的哈希词典.
首先,RESTful Web服务是STATELESS(或换句话说,SESSIONLESS).因此,RESTful服务没有也不应该包含会话或cookie的概念.在RESTful服务中进行身份验证或授权的方法是使用RFC 2616 HTTP规范中定义的HTTP Authorization标头.每个请求都应包含HTTP Authorization标头,并且请求应通过HTTP(SSL)连接发送.这是在HTTP RESTful Web服务中进行身份验证和验证请求授权的正确方法.我已经为Cisco Systems的Cisco PRIME Performance Manager应用程序实现了RESTful Web服务.作为该Web服务的一部分,我也实现了身份验证/授权.
鲁本斯戈麦斯.
它肯定不是关于"会话密钥",因为它通常用于指代在REST的所有约束内执行的无会话身份验证.每个请求都是自我描述的,携带足够的信息来自行授权请求,而不需要任何服务器端应用程序状态.
解决此问题的最简单方法是从RFC 2617中的 HTTP内置身份验证机制开始.
@skrebel(http://www.berenddeboer.net/rest/authentication.html)提到的"非常富有洞察力"的文章讨论了一种令人费解但却非常破碎的身份验证方法.
您可以尝试访问该页面(仅对经过身份验证的用户可以查看)http://www.berenddeboer.net/rest/site/authenticated.html,无需任何登录凭据.
(对不起,我无法对答案发表评论.)
我会说REST和身份验证根本不混合.REST意味着无状态,但"经过身份验证"是一种状态.你不能将它们放在同一层.如果你是一个RESTful的拥护者并对国家不满,那么你必须使用HTTPS(即将安全问题留给另一层).
我认为restful身份验证涉及将身份验证令牌作为参数传递给请求.例如api使用apikeys.我不相信使用cookies或http auth符合条件.
下面提到的方法实质上是OAuth2.0的 "资源所有者密码凭证"授权类型.这是一种简单的启动和运行方式.但是,通过这种方法,组织中的每个应用程序最终都会拥有自己的身份验证和授权机制.推荐的方法是"授权代码"授权类型.另外,在我之前的回答中,我推荐使用浏览器localStorage来存储身份验证令牌.但是,我开始相信cookie是用于此目的的正确选择.我在StackOverflow答案中详细说明了我的理由,授权代码授予类型实现方法,安全注意事项等.
我认为以下方法可用于REST服务身份验证:
创建登录RESTful API以接受用于身份验证的用户名和密码.使用HTTP POST方法防止缓存和SSL在传输过程中的安全性成功验证后,API返回两个JWT - 一个访问令牌(更短的有效期,比如30分钟)和一个刷新令牌(更长的有效期,比如24小时)
客户端(基于Web的UI)将JWT存储在本地存储中,并且在每个后续API调用中,在"授权:承载#access令牌"头中传递访问令牌
API通过验证签名和到期日期来检查令牌的有效性.如果令牌有效,请检查用户(它将JWT中的"子"声明解释为用户名)是否可以通过缓存查找访问API.如果用户有权访问API,请执行业务逻辑
如果令牌过期,则API返回HTTP响应代码400
客户端在接收400/401时,使用"Authorization:Bearer #refresh token"标头中的刷新令牌调用另一个REST API以获取新的访问令牌.
在使用刷新令牌接收呼叫时,通过检查签名和到期日期来检查刷新令牌是否有效.如果刷新令牌有效,则从DB刷新用户的访问权限缓存并返回新的访问令牌和刷新令牌.如果刷新令牌无效,则返回HTTP响应代码400
如果返回新的访问令牌和刷新令牌,请转到步骤2.如果返回HTTP响应代码400,则客户端假定刷新令牌已过期并要求用户输入用户名和密码
要注销,请清除本地存储
通过这种方法,我们每隔30分钟就会执行昂贵的加载缓存操作,其中包含用户特定的访问权限详细信息.因此,如果撤消访问权限或授予新访问权限,则需要30分钟才能反映或注销,然后登录.
这是做到这一点的方法:使用OAuth 2.0进行登录.
只要支持OAuth,您就可以使用Google以外的其他身份验证方法.