我正在设计一个RESTful Web服务,需要用户访问,还需要其他Web服务和应用程序访问。所有传入的请求都需要进行身份验证。所有通信都通过HTTPS进行。用户身份验证将基于身份验证令牌工作,通过将用户名和密码(通过SSL连接)发布到服务提供的 / session 资源获取。
对于Web服务客户端,客户端服务后面有无最终用户。请求由计划任务,事件或其他一些计算机操作启动。连接服务列表事先已知(显然,我猜)。 我应该如何验证来自其他(网络)服务的这些请求?我希望身份验证过程尽可能简单地实现这些服务,但不是以安全为代价。对于这样的场景,标准和最佳实践是什么?
我能想到的选项(或已被建议给我):
让客户服务使用“假”用户名和密码,并以与用户相同的方式对其进行身份验证。我不喜欢这个选项 - 感觉不对。
为客户端服务分配永久应用程序ID,也可能是应用程序密钥。据我所知,这与用户名+密码相同。使用此ID和密钥,我可以验证每个请求,或创建身份验证令牌以验证进一步的请求。无论哪种方式,我都不喜欢这个选项,因为任何能够获取应用程序ID和密钥的人都可以冒充客户端。
我可以在以前的选项中添加IP地址检查。这会使执行虚假请求变得更加困难。
客户证书。设置我自己的证书颁发机构,创建根证书,并为客户端服务创建客户端证书。但是,有几个问题浮现在脑海中:a)如何在没有证书的情况下允许用户进行身份验证,以及b)从客户端服务的角度来看,这种情况有多复杂?
别的 - 还有其他解决方案吗?
我的服务将在Java上运行,但我故意遗漏了有关它将构建的具体框架的信息,因为我对基本原则更感兴趣,而不是对实现细节更感兴趣 - 我认为最好的解决方案无论底层框架如何,都可以实现这一点。但是,我对这个主题缺乏经验,所以对实际实现的具体提示和示例(如有用的第三方库,文章等)也将非常感激。
答案 0 :(得分:35)
在阅读完您的问题后,我会说,生成特殊令牌以执行所需的请求。此令牌将在特定时间生效(假设在一天内)。
以下是生成身份验证令牌的示例:
(day * 10) + (month * 100) + (year (last 2 digits) * 1000)
例如: 2011年6月3日
(3 * 10) + (6 * 100) + (11 * 1000) =
30 + 600 + 11000 = 11630
然后与用户密码连接,例如“my4wesomeP4ssword!”
11630my4wesomeP4ssword!
然后执行该字符串的MD5:
05a9d022d621b64096160683f3afe804
您何时致电请求,请始终使用此令牌
https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata
这个令牌每天都是独一无二的,所以我想这种保护足以保证你的服务。
希望帮助
:)
答案 1 :(得分:32)
此问题的任何解决方案归结为共享秘密。我也不喜欢硬编码的用户名和密码选项,但它确实具有非常简单的优点。客户端证书也不错,但它真的有很大的不同吗?服务器上有一个证书,客户端上有一个证书。它的主要优点是蛮力更难。希望你有其他保护措施可以防止这种情况发生。
我认为客户证书解决方案的A点难以解决。你只需要使用一个分支。 if (client side certificat) { check it } else { http basic auth }
我不是Java专家,我从来没有使用它来做客户端证书。然而,快速谷歌引导我们this tutorial,它看起来就在你的小巷里。
尽管所有这些“最好的”讨论,但我要指出的是,还有另一种理念,即“代码越少,聪明程度越低越好”。 (我个人持这种理念)。客户端证书解决方案听起来像很多代码。
我知道您对OAuth表示了疑问,但OAuth2提案确实包含了一个名为“bearer tokens”的问题解决方案,该解决方案必须与SSL结合使用。我认为,为了简单起见,我选择硬编码的用户/密码(每个应用程序一个,以便它们可以单独撤销)或非常相似的承载令牌。
答案 2 :(得分:11)
您可以采取几种不同的方法。
RESTful纯粹主义者希望您使用BASIC身份验证,并在每个请求上发送凭据。他们的理由是没有人存储任何州。
客户端服务可以存储cookie,该cookie维护会话ID。我个人认为这不像我听到的一些纯粹主义者那样令人反感 - 一遍又一遍地进行身份验证可能会很昂贵。不过,听起来你并不是太喜欢这个想法。
根据你的描述,你可能会感兴趣OAuth2到目前为止,我的经验,从我所看到的,是它有点令人困惑,有点出血。那里有实现,但它们很少而且很远。在Java中,我知道它已经集成到Spring3的security模块中。 (他们的tutorial编写得非常好。)我一直在等待Restlet中是否会有扩展,但到目前为止,虽然它已被提出,并且可能在孵化器中,但它仍然存在尚未完全合并。
答案 3 :(得分:3)
我相信这个方法:
非常标准,无论您如何实施以及其他具体技术细节。
如果您真的想要推动信封,也许您可以将客户端的https密钥视为临时无效状态,直到验证凭据,限制信息(如果它们从未有过),并在验证时根据到期时授予访问权限
希望这有帮助
答案 4 :(得分:3)
就客户端证书方法而言,在允许没有客户端证书的用户的情况下实现起来并不是非常困难。
如果您确实创建了自己的自签名证书颁发机构,并向每个客户端服务颁发了客户端证书,那么您可以轻松地对这些服务进行身份验证。
根据您使用的Web服务器,应该有一种方法来指定将接受客户端证书的客户端身份验证,但不需要客户端身份验证。例如,在Tomcat中指定https连接器时,可以设置'clientAuth = want',而不是'true'或'false'。然后,您将确保将自签名CA证书添加到您的信任库(默认情况下,您正在使用的JRE中的cacerts文件,除非您在Web服务器配置中指定了另一个文件),因此唯一可信的证书将是由你的自签名CA.
在服务器端,如果您能够从请求中检索客户端证书(非空),则只允许访问您希望保护的服务,如果您更喜欢任何额外的安全性,则通过任何DN检查。对于没有客户端证书的用户,他们仍然可以访问您的服务,但只会在请求中没有证书。
在我看来,这是最“安全”的方式,但它肯定有其学习曲线和开销,因此可能不一定是满足您需求的最佳解决方案。
答案 5 :(得分:3)
5。还有别的 - 那里必须有其他解决方案吗?
你是对的,有!它被称为JWT(JSON Web Tokens)。
JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任。 JWT可以使用秘密(使用HMAC算法)或使用RSA的公钥/私钥对进行签名。
我强烈建议您研究JWT。与替代解决方案相比,它们是解决问题的简单方法。
答案 6 :(得分:1)
您可以在服务器上创建会话,并在每次REST呼叫的客户端和服务器之间共享sessionId
。
首先验证REST请求:/authenticate
。使用sessionId: ABCDXXXXXXXXXXXXXX
;
将此sessionId
与Map
一起存储在实际会话中。 Map.put(sessionid, session)
或使用SessionListener
为您创建和销毁密钥;
public void sessionCreated(HttpSessionEvent arg0) {
// add session to a static Map
}
public void sessionDestroyed(HttpSessionEvent arg0) {
// Remove session from static map
}
获取每个REST调用的sessionid,例如URL?jsessionid=ABCDXXXXXXXXXXXXXX
(或其他方式);
HttpSession
; sessionId
答案 7 :(得分:0)
我会使用应用程序将用户重定向到具有应用程序ID参数的站点,一旦用户批准该请求,就会生成另一个应用程序用于身份验证的唯一令牌。这样,其他应用程序不处理用户凭据,并且用户可以添加,删除和管理其他应用程序。 Foursquare和其他一些网站以这种方式进行身份验证,并且很容易像其他应用程序一样实现。
答案 8 :(得分:-3)
除了身份验证,我建议你考虑一下大局。考虑在没有任何身份验证的情况下制作后端RESTful服务;然后在最终用户和后端服务之间放置一些非常简单的身份验证所需的中间层服务。