119

我正在设计一个需要用户访问的 RESTful Web 服务,以及其他 Web 服务和应用程序。所有传入的请求都需要进行身份验证。所有通信都通过 HTTPS 进行。用户身份验证将基于身份验证令牌工作,该令牌通过将用户名和密码(通过 SSL 连接)发布到服务提供的/session资源来获取。

对于 Web 服务客户端,客户端服务背后没有最终用户。请求由计划任务、事件或一些其他计算机操作发起。连接服务列表是事先知道的(显然,我猜)。我应该如何验证来自其他(网络)服务的这些请求?我希望为这些服务实现身份验证过程尽可能简单,但不以牺牲安全为代价。对于这样的场景,标准和最佳实践是什么?

我能想到(或向我建议)的选项:

  1. 让客户端服务使用“假”用户名和密码,并以与用户相同的方式对其进行身份验证。我不喜欢这个选项——只是感觉不对。

  2. 为客户端服务分配一个永久的应用程序 ID,也可能是一个应用程序密钥。据我了解,这与拥有用户名+密码相同。使用这个 id 和密钥,我可以对每个请求进行身份验证,或者创建一个身份验证令牌来对进一步的请求进行身份验证。无论哪种方式,我都不喜欢这个选项,因为任何可以获取应用程序 ID 和密钥的人都可以冒充客户端。

  3. 我可以在上一个选项中添加 IP 地址检查。这将使执行虚假请求变得更加困难。

  4. 客户证书。设置我自己的证书颁发机构,创建根证书,并为客户端服务创建客户端证书。不过,我想到了几个问题:a) 我如何仍然允许用户在没有证书的情况下进行身份验证,以及 b) 从客户端服务的角度来看,这种场景实施起来有多复杂?

  5. 别的东西 - 那里必须有其他解决方案吗?

我的服务将在 Java 上运行,但我故意遗漏了有关它将建立在什么特定框架上的信息,因为我对基本原理更感兴趣,而不是对实现细节感兴趣——我认为最好的解决方案是无论底层框架如何,都可以实现。但是,我对这个主题有点缺乏经验,所以关于实际实现的具体提示和示例(例如有用的第三方库、文章等)也将不胜感激。

4

9 回答 9

36

阅读您的问题后,我会说,生成特殊令牌来执行所需的请求。该令牌将存在于特定时间(假设在一天内)。

这是生成身份验证令牌的示例:

(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

这个令牌每天都是独一无二的,所以我想这种保护足以始终保护你的服务。

希望有所帮助

:)

于 2011-06-03T09:06:03.143 回答
34

这个问题的任何解决方案都归结为一个共享的秘密。我也不喜欢硬编码的用户名和密码选项,但它确实具有非常简单的好处。客户证书也不错,但真的有很大不同吗?服务器上有一个证书,客户端上有一个证书。它的主要优点是更难暴力破解。希望您有其他保护措施来防止这种情况发生。

我认为您对客户端证书解决方案的 A 点并不难解决。您只需使用一个分支。if (client side certificat) { check it } else { http basic auth }我不是 Java 专家,我从来没有使用它来做客户端证书。然而,一个快速的谷歌将我们引导到本教程,它看起来正合你的胃口。

尽管有所有这些“什么是最好的”讨论,但让我指出还有另一种哲学说:“更少的代码,越少的聪明越好。” (我个人持有这种哲学)。客户端证书解决方案听起来像很多代码。

我知道您表达了有关 OAuth 的问题,但 OAuth2 提案确实包含一个解决您的问题的方法,称为“不记名令牌”,它必须与 SSL 结合使用。我认为,为了简单起见,我会选择硬编码的用户/通行证(每个应用程序一个,以便可以单独撤销它们)或非常相似的不记名令牌。

于 2011-06-02T16:31:50.537 回答
11

您可以采取几种不同的方法。

  1. RESTful 纯粹主义者会希望您使用 BASIC 身份验证,并在每个请求上发送凭据。他们的理由是没有人存储任何状态。

  2. 客户端服务可以存储一个 cookie,它维护一个会话 ID。我个人并不觉得这像我听到的一些纯粹主义者那样令人反感——一遍又一遍地进行身份验证可能会很昂贵。不过,听起来你不太喜欢这个主意。

  3. 根据您的描述,听起来您确实可能对OAuth2感兴趣。从我所见,到目前为止,我的经验是它有点令人困惑,而且有点前沿。那里有实现,但它们很少而且相距甚远。在Java中,据我了解它已集成到Spring3的安全模块中。(他们的教程写得很好。)我一直在等着看是否会在Restlet中进行扩展,但到目前为止,虽然它已经被提出,并且可能在孵化器中,但它仍然没有被完全合并。

于 2011-05-26T05:40:26.947 回答
3

我相信这种方法:

  1. 第一个请求,客户端发送 id/passcode
  2. 交换唯一令牌的 ID/通行证
  3. 在每个后续请求上验证令牌,直到它过期

是相当标准的,不管你如何实现和其他具体的技术细节。

如果您真的想挑战极限,也许您可​​以将客户端的 https 密钥视为暂时无效状态,直到凭据被验证,如果它们从未验证过,则限制信息,并在验证时再次根据到期时间授予访问权限。

希望这可以帮助

于 2011-05-31T22:04:54.323 回答
3

就客户端证书方法而言,实施起来并不难,同时仍然允许没有客户端证书的用户进入。

如果您确实创建了自己的自签名证书颁发机构,并向每个客户端服务颁发客户端证书,那么您将有一种简单的方法来验证这些服务。

根据您使用的 Web 服务器,应该有一种方法来指定将接受客户端证书但不需要的客户端身份验证。例如,在 Tomcat 中指定 https 连接器时,您可以设置“clientAuth=want”,而不是“true”或“false”。然后,您将确保将您的自签名 CA 证书添加到您的信任库(默认情况下,您正在使用的 JRE 中的 cacerts 文件,除非您在 Web 服务器配置中指定了另一个文件),因此唯一受信任的证书将是那些从您的自签名 CA。

在服务器端,如果您能够从请求中检索客户端证书(非空),您将只允许访问您希望保护的服务,并且如果您更喜欢任何额外的安全性,则通过任何 DN 检查。对于没有客户端证书的用户,他们仍然可以访问您的服务,但请求中根本没有证书。

在我看来,这是最“安全”的方式,但它肯定有其学习曲线和开销,因此不一定是满足您需求的最佳解决方案。

于 2011-06-02T21:37:42.093 回答
3

5. 其他的——肯定有其他的解决方案吗?

你是对的,有!它被称为 JWT(JSON Web Tokens)。

JSON Web Token (JWT) 是一个开放标准 (RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息作为 JSON 对象。此信息可以验证和信任,因为它是数字签名的。JWT 可以使用密钥(使用 HMAC 算法)或使用 RSA 的公钥/私钥对进行签名。

我强烈建议研究 JWT。与替代解决方案相比,它们是解决问题的更简单的解决方案。

https://jwt.io/introduction/

于 2016-05-13T14:08:43.140 回答
1

您可以在服务器上创建会话,并sessionId在每次 REST 调用时在客户端和服务器之间共享。

  1. 首先验证 REST 请求:/authenticate. sessionId: ABCDXXXXXXXXXXXXXX使用;返回响应(根据您的客户端格式)

  2. 将其sessionIdMap实际会话一起存储。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
    }
    
  3. 通过每个 REST 调用获取 sessionid,例如URL?jsessionid=ABCDXXXXXXXXXXXXXX(或其他方式);

  4. 使用;HttpSession从地图中检索sessionId
  5. 如果会话处于活动状态,则验证对该会话的请求;
  6. 发回响应或错误消息。
于 2012-10-05T22:57:46.017 回答
0

一旦用户批准请求,我会使用应用程序将用户重定向到您的站点并使用应用程序 id 参数生成一个唯一令牌,该令牌由其他应用程序用于身份验证。这样,其他应用程序不会处理用户凭据,并且其他应用程序可以由用户添加、删除和管理。Foursquare 和其他一些网站以这种方式进行身份验证,并且很容易作为其他应用程序实现。

于 2011-05-26T05:34:28.200 回答
-3

除了身份验证,我建议您考虑大局。考虑让您的后端 RESTful 服务无需任何身份验证;然后在最终用户和后端服务之间放置一些非常简单的身份验证所需的中间层服务。

于 2011-05-26T13:07:09.913 回答