RESTful身份验证意味着什么?它是如何工作的?我在谷歌上找不到好的概览。我唯一的理解是在URL中传递会话密钥(记住),但这可能是错误的。
这当然不是关于“会话密钥”,因为它通常用于指在REST的所有约束条件下执行的无会话身份验证。每个请求都是自我描述的,携带足够的信息,可以在没有任何服务器端应用程序状态的情况下自行授权请求。
最简单的方法是从RFC 2617中的HTTP内置认证机制开始。
以我的理解来回答这个问题。。。
一种使用REST的身份验证系统,因此您不需要实际跟踪或管理系统中的用户。这是通过使用HTTP方法POST、GET、PUT、DELETE完成的。我们采用这4种方法,并将它们视为数据库交互的CREATE、READ、UPDATE、DELETE(但在web上,我们使用POST和GET,因为这是锚标记当前支持的)。因此,将POST和GET视为我们的CREATE/READ/UPDATE/DELETE(CRUD),然后我们就可以在web应用程序中设计路由,从而推断出我们正在实现的CRUD操作。
例如,在RubyonRails应用程序中,我们可以构建我们的web应用程序,以便如果登录的用户访问http://store.com/account/logout则该页面的GET可以被视为用户试图注销。在我们的rails控制器中,我们将在中构建一个操作,将用户注销并将其发送回主页。
登录页面上的GET将生成一个表单。登录页上的POST将被视为登录尝试,并获取POST数据并使用它进行登录。
对我来说,这是一种使用映射到其数据库含义的HTTP方法的实践,然后构建一个身份验证系统,您不需要传递任何会话id或跟踪会话。
我还在学习——如果你发现我说的任何错误,请纠正我,如果你了解更多,请将其张贴在这里。谢谢
我怀疑那些热情高喊“HTTP认证”的人是否曾尝试过用REST制作基于浏览器的应用程序(而不是机器对机器的web服务)(无意冒犯-我只是认为他们从未面临过复杂的问题)。
我在RESTful服务上使用HTTP身份验证时发现的问题是:
用户通常会收到一个难看的浏览器制作的登录框,这对用户非常不友好。您不能添加密码检索、帮助框等。注销或以其他名称登录是一个问题-浏览器将一直向站点发送身份验证信息,直到您关闭窗口超时很困难
这里有一篇非常有见地的文章,逐点解决了这些问题,但这导致了许多特定于浏览器的javascript黑客、变通方法等。因此,它也不向前兼容,因此在发布新浏览器时需要不断维护。我不认为这是一个干净清晰的设计,而且我觉得这是一项额外的工作,很头疼,因为这样我才能热情地向朋友展示我的REST徽章。
我认为饼干是解决方案。但是等等,饼干是邪恶的,不是吗?不,他们不是,饼干经常被使用的方式是邪恶的。cookie本身只是一条客户端信息,就像您浏览时浏览器会跟踪的HTTP身份验证信息一样。这段客户端信息在每次请求时都会被发送到服务器,就像HTTP身份验证信息一样。从概念上讲,唯一的区别是,这段客户端状态的内容可以由服务器作为其响应的一部分来确定。
通过使用以下规则使会话成为RESTful资源:
会话将密钥映射到用户id(可能还有超时的最后一个操作时间戳)如果会话存在,则意味着密钥有效。登录意味着发布到/会话,新密钥设置为cookie注销意味着删除/会话/{key}(要记住,我们是一个浏览器,HTML5还有很长的路要走)通过在每次请求时将密钥作为cookie发送并检查会话是否存在和有效来完成身份验证
现在,与HTTP身份验证的唯一区别是,身份验证密钥由服务器生成,并发送给客户端,客户端继续发送该密钥,而不是客户端根据输入的凭据计算该密钥。
converter42补充道,当使用https(我们应该这样做)时,cookie必须设置其安全标志,这样身份验证信息就不会通过非安全连接发送。说得好,我自己都没看到。
我觉得这是一个足够好的解决方案,但我必须承认,我还不够安全专家来识别这个方案中的潜在漏洞-我所知道的是,数百个非RESTful web应用程序使用基本相同的登录协议(PHP中的$_SESSION,Java EE中的HttpSession等),就像接受语言可以用于访问翻译资源等。我觉得这是一样的,但也许其他人不是这样?你们觉得呢,伙计们?
如何在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特有的。
通过Cookie进行会话
老实说,在服务器上管理的会话并不是真正无状态的。
一种可能是维护cookie内容中的所有数据。而且,根据设计,cookie是在服务器端处理的(事实上,客户端甚至不尝试解释cookie数据:它只是在每次连续请求时将其返回给服务器)。但是这个cookie数据是应用程序状态数据,所以客户端应该在一个纯无状态的世界中管理它,而不是服务器。
GET /spec.html HTTP/1.1
Host: www.example.org
Cookie: theme=light; sessionToken=abc123
cookie技术本身是HTTP链接的,因此它不是真正的RESTful,应该是独立于协议的,IMHO。它容易受到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编码查询字符串。
这种技术可能与无状态体系结构更为兼容,也可以通过轻会话管理(使用内存会话而不是DB持久性)来实现。
例如,这里有一个来自上面链接的通用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特定性(如标头或cookie)。
稍后注意:在URI中添加签名可能被视为不好的做法(例如,因为它会出现在http服务器日志中),因此必须进行缓解,例如通过适当的TTL来避免重播。但如果你的http日志被破坏,你肯定会有更大的安全问题。
在实践中,即将推出的OAuth 2.0 MAC令牌认证可能是对“令牌授权”当前方案的巨大改进。但这仍然是一项正在进行的工作,与HTTP传输有关。
结论
值得总结的是,REST不仅是基于HTTP的,即使在实践中,它也主要通过HTTP实现。REST可以使用其他通信层。因此,RESTful身份验证不仅仅是HTTP身份验证的同义词,无论谷歌如何回答。它甚至根本不应该使用HTTP机制,而是应该从通信层抽象出来。如果您使用HTTP通信,由于Let's Encrypt倡议,没有理由不使用适当的HTTPS,这是除了任何身份验证方案之外所必需的。
@skrevel(http://www.berenddeboer.net/rest/authentication.html )讨论了一种复杂但真正失败的身份验证方法。
您可以尝试访问该页面(该页面应仅对经过身份验证的用户可见)http://www.berenddeboer.net/rest/site/authenticated.html没有任何登录凭据。
(很抱歉,我无法对答案发表评论。)
我会说REST和身份验证根本不混合。REST表示无状态,但“已验证”是一种状态。不能在同一层同时使用它们。如果你是一个RESTful的拥护者,并且不赞成状态,那么你必须使用HTTPS(即将安全问题留给另一层)。
首先也是最重要的,RESTful web服务是无状态的(或者换句话说,无会话的)。因此,RESTful服务没有也不应该包含会话或cookie的概念。在RESTful服务中进行身份验证或授权的方法是使用RFC 2616 HTTP规范中定义的HTTP授权头。每个请求都应该包含HTTP授权头,并且请求应该通过HTTPs(SSL)连接发送。这是在HTTPRESTful web服务中进行身份验证和验证请求授权的正确方法。我已经为Cisco Systems的Cisco PRIME Performance Manager应用程序实现了RESTful web服务。作为web服务的一部分,我还实现了身份验证/授权。
老实说,我在这里看到了很好的答案,但让我有点困扰的是,当有人将整个无状态概念推向极端,使其变得教条化时。这让我想起了那些只想拥抱纯OO的Smalltalk老粉丝,如果某个东西不是对象,那么你就错了。放过我
RESTful方法应该让你的生活更轻松,减少会议的开销和成本,尽量遵循它,因为这是一件明智的事情,但一旦你遵循一个纪律(任何纪律/准则)到了极致,它不再提供其预期的好处,那么你就错了。当今一些最好的语言同时具备函数式编程和面向对象。
如果解决问题的最简单方法是将身份验证密钥存储在cookie中并通过HTTP标头发送,那么就这样做吧,不要滥用它。请记住,当会话变得沉重和庞大时,会话是不好的,如果所有会话都由一个包含密钥的短字符串组成,那么有什么大不了的?
我愿意接受评论中的更正,但我只是认为(到目前为止)避免在我们的服务器中保存一本大字典的哈希值,这没有什么意义。
这里是一个真正且完全的RESTful身份验证解决方案:
在身份验证服务器上创建公钥/私钥对。将公钥分发给所有服务器。当客户端进行身份验证时:3.1.发行包含以下内容的代币:到期时间用户名(可选)用户IP(可选)密码散列(可选)3.2.使用私钥加密令牌。3.3.将加密令牌发送回用户。当用户访问任何API时,还必须传递其身份验证令牌。服务器可以通过使用身份验证服务器的公钥解密令牌来验证令牌是否有效。
这是无状态/RESTful身份验证。
注意,如果包含密码哈希,则用户还将发送未加密的密码以及身份验证令牌。服务器可以通过比较散列来验证密码是否与用于创建身份验证令牌的密码匹配。需要使用HTTPS之类的安全连接。客户端的Javascript可以处理获取用户的密码并将其存储在客户端,可以存储在内存中,也可以存储在cookie中,可能使用服务器的公钥进行加密。
使用公钥基础结构(其中密钥的注册涉及适当的绑定)可确保公钥以确保不可抵赖的方式绑定到分配给其的个人
看见http://en.wikipedia.org/wiki/Public_key_infrastructure . 如果您遵循正确的PKI标准,可以识别并锁定不正确使用被盗密钥的人或代理。如果要求代理使用证书,则绑定变得非常紧密。一个聪明且行动迅速的小偷可以逃跑,但他们会留下更多的面包屑。
关于这个话题,这里的好心人已经说得够多了。但这是我的2美分。
有两种交互模式:
人对机器(HTM)机器对机器(MTM)
机器是共同的分母,表示为RESTAPI,参与者/客户端要么是人,要么是机器。
现在,在真正的RESTful架构中,无状态的概念意味着所有相关的应用程序状态(意味着客户端状态)都必须随每个请求一起提供。通过相关,这意味着RESTAPI处理请求和提供适当响应所需的一切。
当我们在人对机器应用程序的上下文中考虑这一点时,正如Skrebel在上面指出的那样,“基于浏览器”,这意味着在浏览器中运行的(web)应用程序将需要将其状态和相关信息及其向后端REST API发出的每个请求一起发送。
考虑一下:您有一个数据/信息平台公开的RESTAPI资产。也许您有一个自助BI平台来处理所有数据立方体。但您希望您的(人类)客户通过(1)web应用程序、(2)移动应用程序和(3)某些第三方应用程序访问此应用程序。最后,即使是MTM链也会导致HTM-right。因此,人类用户仍然处于信息链的顶端。
在前两个案例中,您有一个人机交互的案例,信息实际上是由人类用户使用的。在最后一种情况下,您有一个使用RESTAPI的机器程序。
身份验证的概念适用于所有领域。您将如何设计它,以便以统一、安全的方式访问REST API?在我看来,有两种方式:
途径1:
首先,没有登录。每个请求都执行登录客户端发送其标识参数+特定请求每个请求的参数REST API接收它们,转身,ping用户存储(无论是什么)并确认授权如果建立了身份验证,则为请求提供服务;否则,拒绝具有适当的HTTP状态代码对所有REST API中的每个请求重复上述步骤目录
路线-2:
客户端以身份验证请求开始登录REST API将处理所有此类请求它接受auth参数(API密钥、uid/pwd或任何您select),并根据用户存储(LDAP、AD或MySQL DB等)验证身份验证如果已验证,则创建一个身份验证令牌并将其交还给客户/呼叫方然后,调用者将此身份验证令牌+请求特定参数与对其他业务REST API的每个后续请求,直到注销或租约到期
显然,在Way-2中,RESTAPI需要一种方法来识别和信任令牌的有效性。Login API执行了身份验证,因此,目录中的其他REST API需要信任“代客密钥”。
当然,这意味着需要在RESTAPI之间存储和共享身份验证密钥/令牌。这个共享的、可信的令牌存储库可以是本地/联合的,允许其他组织的REST API相互信任。
但我跑题了。
关键是,需要维护和共享一个“状态”(关于客户端的身份验证状态),以便所有RESTAPI都能创建一个信任圈。如果我们不这样做,即Way-1,我们必须接受必须对任何/所有传入的请求执行身份验证。
执行身份验证是一个资源密集型过程。想象一下,针对每个传入的请求,对用户存储执行SQL查询,以检查uid/pwd匹配。或者,加密并执行哈希匹配(AWS风格)。在架构上,我怀疑每个RESTAPI都需要使用一个通用的后端登录服务来执行此操作。因为,如果你不这样做,那么你就会到处乱扔授权码。一团糟。
所以层数越多,延迟就越大。
现在,选择Way-1并向HTM申请。您的(人类)用户真的关心您是否必须在每个请求中发送uid/pwd/hash或其他内容吗?不,只要你不打扰她,每秒都会抛出auth/login页面。如果你这样做的话,祝你有客户。所以,你要做的是将登录信息存储在客户端的某个位置,在浏览器中,一开始就存储,并随每个请求一起发送。对于(人类)用户,她已经登录,并且“会话”可用。但事实上,她的每一个请求都经过了认证。
与路线2相同。你的(人类)用户永远不会注意到。所以没有造成伤害。
如果我们将Way-1应用于MTM怎么办?在这种情况下,由于它是一台机器,我们可以要求它在每次请求时都提交身份验证信息,从而让这个家伙见鬼去。没人在乎!在MTM上执行Way-2不会引起任何特殊反应;这是一台该死的机器。它可以不在乎!
所以,问题是什么适合你的需要。无国籍需要付出代价。付出代价,继续前进。如果你想成为一个纯粹主义者,那么也要为此付出代价,然后继续前进。
最终,哲学无关紧要。真正重要的是信息发现、展示和消费体验。如果人们喜欢你的API,你就做好了自己的工作。
2019年2月16日更新
下面前面提到的方法本质上是OAuth2.0的“资源所有者密码凭据”授予类型。这是一种简单的起床和跑步方式。然而,使用这种方法,组织中的每个应用程序最终都将拥有自己的身份验证和授权机制。建议采用“授权代码”授权类型。此外,在下面我先前的回答中,我建议使用浏览器localStorage来存储身份验证令牌。然而,我开始相信饼干是实现这一目的的正确选择。在StackOverflow的回答中,我详细介绍了我的原因、授权代码授予类型实现方法、安全考虑等。
我认为以下方法可用于REST服务身份验证:
创建一个登录RESTful API以接受用户名和密码进行身份验证。使用HTTP POST方法在传输过程中防止缓存和SSL安全成功认证后,API返回两个JWT-一个访问令牌(有效期更短,例如30分钟)和一个刷新令牌(有效性更长,例如24小时)客户端(一个基于web的UI)将JWT存储在本地存储中,并且在随后的每个API调用中都会传递“Authorization:Bearer#access token”标头中的访问令牌API通过验证签名和到期日期来检查令牌的有效性。如果令牌有效,请检查用户(它将JWT中的“sub”声明解释为用户名)是否可以通过缓存查找访问API。如果用户被授权访问API,则执行业务逻辑如果令牌过期,API将返回HTTP响应代码400客户端在收到400/401时,调用另一个REST API,并在“Authorization:Bearer#refresh token”标头中使用刷新令牌来获取新的访问令牌。收到带有刷新令牌的呼叫时,通过检查签名和到期日期来检查刷新令牌是否有效。如果刷新令牌有效,则从DB刷新用户的访问权限缓存,并返回新的访问令牌和刷新令牌。如果刷新令牌无效,则返回HTTP响应代码400如果返回了新的访问令牌和刷新令牌,请转到步骤2。如果返回HTTP响应代码400,则客户端假定刷新令牌已过期,并向用户请求用户名和密码要注销,请清除本地存储
使用这种方法,我们每30分钟就要执行一次昂贵的操作,即加载具有用户特定访问权限详细信息的缓存。因此,如果取消了访问或授予了新的访问权限,则需要30分钟才能反映或注销,然后登录。
用于保护任何web应用程序的有效提示
如果你想保护你的应用程序,那么你应该首先使用HTTPS而不是HTTP,这样可以确保你和用户之间建立一个安全的通道,防止嗅探来回发送给用户的数据,并有助于对交换的数据保密。
您可以使用JWT(JSON Web令牌)来保护RESTful API,与服务器端会话相比,这有很多好处,好处主要是:
1-更具可扩展性,因为您的API服务器不必为每个用户维护会话(当您有多个会话时,这可能是一个很大的负担)
2-JWT是独立的,具有定义用户角色的声明,例如,他可以访问的内容,并在日期和到期日发布(之后JWT将无效)
3-更易于跨负载平衡器处理&如果您有多个API服务器,因为您不必共享会话数据,也不必配置服务器将会话路由到同一服务器,只要JWT请求命中任何服务器,就可以对其进行身份验证和授权
4-减少了数据库的压力,也不必为每个请求不断存储和检索会话id和数据
5-如果您使用强密钥签署JWT,JWT不会被篡改,因此您可以信任随请求发送的JWT中的声明,而无需检查用户会话&无论他是否获得授权,您只需检查JWT,然后您就可以知道该用户可以做什么。
许多库提供了在大多数编程语言中创建和验证JWT的简单方法,例如:在node.js中,最流行的是jsonwebtoken
由于REST API通常旨在使服务器保持无状态,因此JWT与这一概念更为兼容,因为每个请求都使用自包含的授权令牌(JWT)发送,而服务器无需跟踪用户会话,而会话使服务器保持有状态,从而记住用户及其角色,然而,会话也被广泛使用并有其优点,如果需要,可以搜索。
需要注意的一点是,您必须使用HTTPS将JWT安全地交付给客户机,并将其保存在安全的地方(例如,本地存储)。
您可以通过此链接了解有关JWT的更多信息
推荐文章
- 跨REST微服务的事务?
- “刷新令牌”的目的是什么?
- 调用webrequest, POST参数
- 摘要认证和基本认证的区别是什么?
- 如何在Spring RestTemplate请求上设置“接受:”头?
- 什么是端点?
- 配置系统初始化失败
- REST API最佳实践:查询字符串中的参数vs请求体中的参数
- Java中SOAP和rest式web服务的主要区别
- 如何为Java创建REST客户端?
- file_get_contents(): SSL operation failed with code 1, failed to enable crypto
- 在Subversion中,我可以是我的登录名以外的用户吗?
- 在HttpClient和WebClient之间进行选择
- 执行没有实体主体的HTTP POST被认为是不好的做法吗?
- JavaScript/jQuery下载文件通过POST与JSON数据