首页 文章

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

提问于
浏览
107

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

对于Web服务客户端,客户端服务后面有 no end user . 请求由计划的任务,事件或一些其他计算机操作启动 . 连接服务列表事先已知(显然,我猜) . How should I authenticate these requests coming from other (web) services? 我希望身份验证过程尽可能简单地为这些服务实现,但不是以安全为代价 . 这样的场景的标准和最佳实践是什么?

我能想到的选项(或已被建议给我):

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

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

  • 我可以在以前的选项中添加IP地址检查 . 这将使执行虚假请求变得更加困难 .

  • 客户端证书 . 设置我自己的证书颁发机构,创建根证书,并为客户端服务创建客户端证书 . 但是,有几个问题浮现在脑海中:a)如何在没有证书的情况下允许用户进行身份验证,以及b)从客户端服务的角度来看,这种情况有多复杂?

  • 其他 - 那里必须有其他解决方案吗?

我的服务将在Java上运行,但我故意遗漏了有关它将构建的具体框架的信息,因为我对基本原则更感兴趣,而不是对实现细节更感兴趣 - 我认为最好的解决方案是无论底层框架如何,都可以实现 . 但是,我对这个主题缺乏经验,所以对实际实现的具体提示和示例(例如有用的第三方库,文章等)也将非常受欢迎 .

9 回答

  • 3

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

  • 3

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

  • 10

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

    我认为客户证书解决方案的A点难以解决 . 你只需要使用一个分支 . if (client side certificat) { check it } else { http basic auth } 我'm no java expert and I'从未使用它来做客户端证书 . 然而,一个快速的谷歌引导我们this tutorial,它看起来就在你的小巷 .

    尽管所有这些都是“最好的”讨论,但我要指出的是,还有另一种理念,即“代码越少,聪明程度越低越好” . (我个人持有这种理念) . 客户端证书解决方案听起来像很多代码 .

    我知道您对OAuth表达了疑问,但OAuth2提案确实包含了一个名为“bearer tokens”的问题解决方案,该解决方案必须与SSL结合使用 . 我认为,为了简单起见,我选择硬编码的用户/密码(每个应用程序一个,以便它们可以单独撤销)或非常相似的承载令牌 .

  • 30

    在阅读了你的问题之后,我会说,生成特殊令牌以执行所需的请求 . 此令牌将在特定时间生效(假设在一天内) .

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

    (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
    

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

    希望有所帮助

    :)

  • -3

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

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

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

    • 从你的描述来看,这真的听起来你可能会感兴趣OAuth2我到目前为止的经历,从我的那种令人困惑的,有点出血的边缘 . 有实现,但他们're few and far between. In Java, I understand that it has been integrated into Spring3' s security模块 . (他们的tutorial编写得非常好 . )我一直在等待Restlet中是否会有扩展,但到目前为止,虽然它仍未完全合并 .

  • 33

    我相信这个方法:

    • 第一个请求,客户端发送id / passcode

    • 唯一标记的Exchange ID / pass

    • 在每个后续请求上验证令牌,直到它过期

    无论您如何实施以及其他具体技术细节,都是非常标准的 .

    如果您真的想要推动信封,也许您可以将客户端的https密钥视为临时无效状态,直到验证凭据,限制信息(如果它们从未如此),并在验证时根据到期时授予访问权限 .

    希望这可以帮助

  • 3

    就客户端证书方法而言,在允许没有客户端证书的用户的情况下实现起来并不是非常困难 .

    如果您确实创建了自己的自签名证书颁发机构,并向每个客户端服务颁发了客户端证书,那么您可以轻松地对这些服务进行身份验证 .

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

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

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

  • 0

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

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

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

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

    https://jwt.io/introduction/

  • 1

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

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

    • Map 中将此 sessionId 与实际会话一起存储 . 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 (或其他方式);

    • 使用 sessionId 从 Map 中回溯 HttpSession ;

    • 如果会话处于活动状态,则验证该会话的请求;

    • 发回响应或错误消息 .

相关问题