RESTful Web 服务——如何验证来自其他服务的请求?

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

在 Web 服务客户端的情况下,客户端服务后面有 没有终端用户。这些请求是由预定的任务、事件或其他计算机操作发起的。连接服务的列表是预先知道的(显然,我猜)。我希望这些服务的身份验证过程尽可能简单,但不能以牺牲安全为代价。这种情况下的标准和最佳实践是什么?

我能想到的(或者已经有人向我建议过的)选项:

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

  2. 为客户端服务分配一个永久的应用程序 ID,可能还有一个应用程序密钥。据我所知,这和用户名 + 密码是一样的。使用这个 id 和密钥,我可以对每个请求进行身份验证,或者创建一个身份验证令牌来对进一步的请求进行身份验证。无论哪种方式,我都不喜欢这个选项,因为任何能够获得应用程序 ID 和密钥的人都可以模拟客户机。

  3. I could add an IP address check to previous option. This would make it harder to perform fake requests.

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

  5. 一定还有其他的解决方案吧

我的服务将运行在 Java 上,但是我故意遗漏了关于它将建立在什么样的具体框架上的信息,因为我对基本原则更感兴趣,而不是对实现细节那么感兴趣——我假设这方面的最佳解决方案将是可能实现的,而不管底层框架是什么。然而,我对这个主题有点缺乏经验,因此关于实际实现的具体提示和示例(例如有用的第三方库、文章等)也会受到很大的欢迎。

122351 次浏览

I would use have an application redirect a user to your site with an application id parameter, once the user approves the request generate a unique token that is used by the other app for authentication. This way the other applications are not handling user credentials and other applications can be added, removed and managed by users. Foursquare and a few other sites authenticate this way and its very easy to implement as the other application.

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

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

  2. 客户端服务可以存储一个 cookie,它维护一个会话 ID。我个人并不像我听到的一些纯粹主义者那样觉得这是一种冒犯——一遍又一遍的验证是很昂贵的。听起来你好像不太喜欢这个主意。

  3. 从你的描述来看,听起来你真的可能对 OAuth2感兴趣。到目前为止,我的经验是,从我所看到的来看,它有点令人困惑,有点前沿。现在已经有了一些实现,但是数量很少。在 Java 中,我知道它已经被集成到 Spring3的 保安模块中了。(他们的 教程写得很好。)我一直在等待 Restlet是否会有一个扩展,但到目前为止,虽然它已经被提出,并可能在孵化器,它仍然没有完全纳入。

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

我相信这种方法:

  1. 第一个请求,客户端发送 id/passcode
  2. Exchange id/pass for unique token
  3. 在每个后续请求上验证令牌,直到令牌过期

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

If you really want to push the envelope, perhaps you could regard the client's https key in a temporarily invalid state until the credentials are validated, limit information if they never are, and grant access when they are validated, based again on expiration.

希望这个能帮上忙

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

我不认为您对客户端证书解决方案的观点 A 是难以解决的。你只要用树枝就行了。我不是 Java 专家,也从来没有用它做过客户端证书。然而,一个快速的谷歌引导我们到 本教程看起来正合你的口味。

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

我知道您对 OAuth 提出了一些问题,但是 OAuth2提案确实包含了一个名为“ 无记名代币”的解决方案,该方案必须与 SSL 一起使用。我认为,为了简单起见,我会选择硬编码的用户/通行证(每个应用程序一个,这样它们可以单独撤销)或非常相似的持有者令牌。

就客户端证书方法而言,在允许没有客户端证书的用户进入系统的同时,实现起来并不十分困难。

If you did in fact create your own self-signed Certification Authority, and issued client certs to each client service, you would have an easy way of authenticating those services.

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

在服务器端,只有在能够从请求中检索客户端证书(非 null)时才允许访问希望保护的服务,并且如果希望获得额外的安全性,则通过任何 DN 检查。对于没有客户端证书的用户,他们仍然可以访问您的服务,但是在请求中将不会出现任何证书。

在我看来,这是最“安全”的方式,但它肯定有其学习曲线和开销,所以可能不一定是最好的解决方案,您的需要。

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

下面是一个生成身份验证令牌的示例:

(day * 10) + (month * 100) + (year (last 2 digits) * 1000)

例如: 2011年6月3日

(3 * 10) + (6 * 100) + (11 * 1000) =
30 + 600 + 11000 = 11630

然后与用户密码连接,例如“ my4wesomeP4sswat!”

11630my4wesomeP4ssword!

然后执行该字符串的 MD5:

05a9d022d621b64096160683f3afe804

当您调用请求时,总是使用这个标记,

https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata

这个信物每天都是独一无二的,所以我想这种保护已经足够保护你的服务了。

希望有用

:)

You can create Session on server and share sessionId in between client and server with each REST call.

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

  2. 将此 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
    }
    
  3. Get sessionid with every REST call, like URL?jsessionid=ABCDXXXXXXXXXXXXXX (or other way);

  4. Retrive HttpSession from map using sessionId;
  5. Validate request for that session if session is active;
  6. Send back response or error message.

5. 还有别的办法——一定还有别的办法?

You're right, there is! And it is called JWT (JSON Web Tokens).

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

我强烈推荐使用 JWT。与其他解决方案相比,它们是一种更简单的解决方案。

Https://jwt.io/introduction/