【问题标题】:Session design of an Application server API with multiple client platforms具有多个客户端平台的应用程序服务器 API 的会话设计
【发布时间】:2017-02-09 05:19:01
【问题描述】:

我想构建一个支持多种平台的应用程序:桌面应用程序 (Mac/PC)、Web(angularJS 前端)和原生移动应用程序。

所以我正在考虑一个应用服务器,为上述平台提供内部 API。我对如何支持登录/注销有某些假设。如果我的想法是错误的,如果有人可以发表评论,我会很高兴。

  1. 对于桌面和移动应用程序,“登录”功能将使用内部 API 传递凭据,并作为回报接收永久令牌。桌面/移动应用程序将存储令牌并将其用于对应用程序服务器的任何后续请求。从桌面/移动应用“注销”后,令牌将在服务器端被丢弃,而在前端应用端被遗忘。
  2. 对于 Web 界面,Angular 应用程序会将登录后提供的令牌保留为 cookie,并将其加载并与对应用程序服务器的任何请求一起使用。

这是一种常见的模式吗?

【问题讨论】:

  • 这些 API 是什么类型的接口? RESTful Web 服务?
  • 是的,RESTful 服务

标签: javascript angularjs node.js web-services rest


【解决方案1】:

是的,它与您所描述的有特别的偏差。阅读OAuth2 身份验证舞蹈,因为它可能涉及第 3 方使用应用程序服务器对客户端(桌面/Web/本地移动)进行身份验证。

例如,您可能希望使用 Facebook 作为您的身份提供者,您的应用程序服务器将是服务提供者,尝试登录的用户将被定向到 FB,在那里他将获得用于与之通信的令牌您的服务。

当您使用它时,请查看 JWT too,因为它为令牌身份验证提供了额外的安全级别。

此外,Auth0 即使您不打算使用它们,也可以在 various platforms 上提供有关身份验证的很好的文档。

【讨论】:

    【解决方案2】:

    我建议你看看我在此处为与身份验证相关的问题所做的答案:Authorization method for REST API utilising Active Directory

    问题是关于活动目录的,但我关注的是 OAuth 以及如何使用 thinktecture 或 oauth2orize 等工具实现自定义 OAuth 身份验证服务器。

    一旦您拥有 Oauth 身份验证服务器(开箱即用的 ADFS、.net 中的自定义 thinktecture、nodeJs 中的自定义 oauth2orize),您可以在其上插入任何内容,因为它是标准身份验证协议。例如,Xamarin 可以对任何 oauth2 服务器进行身份验证:https://components.xamarin.com/gettingstarted/xamarin.auth。正如我在上一个答案中提到的,任何 Web 服务器技术或 javascript 技术都可以处理 OAuth2。

    不要犹豫,提出任何问题以获取有关这些主题的更多详细信息。

    【讨论】:

      【解决方案3】:

      您的基本结构是正确的,但是使用 OAuth2,您将永远不会永远存储访问令牌。访问令牌通常是一个不透明的字符串,它授予对您的 API 的访问权限,将其存储在 cookie 或本地存储中很好,但是从服务器发出永不过期的令牌是非常不可取的(MITM 攻击可能会永远劫持您的身份) .

      为解决此问题,OAuth2 实施通常会在访问令牌旁边分配刷新令牌。刷新令牌通常比访问令牌具有更长的到期时间(在访问令牌的到期时间和我会说的一个月之间的任何时间)。刷新令牌类似于临时用户密码 - 它们不会直接授予对您 API 的任何访问权限,但是用户可以通过调用您的 OAuth2 刷新 api 向您的系统授权,并获取新的访问权限并使用新的到期时间刷新令牌.这使您的应用程序有机会定期重新验证用户声明(可能他们的访问权限/角色已更改,他们需要更新声明)。


      JWT 令牌

      访问令牌可能是您存储在服务器上的不透明字符串,但我强烈建议您使用 JWT 令牌。与不透明(无意义)令牌相比,JWT 令牌有 2 个主要优点:

      1.客户索赔

      在您的客户端应用程序授权后,您需要做的第一件事是查找各种内容来构建您的 UI。 JWT 令牌的美妙之处在于,它们将您的所有用户声明(包括您的应用程序自定义用户声明)作为 JSON 对象有效负载存储在一个编码字符串中,可以通过首先在 . 上拆分令牌来在客户端解码,这会破坏将其转换为[ header, payload, sig ] base 64 编码字符串。然后,您可以对负载字符串进行 base 64 解码并通过 JSON.parse 运行它,这将生成您的声明键值对:

      const access_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ'
      const claims = JSON.parse(atob(access_token.split('.')[1]))
      console.info(claims)

      这允许您的客户端应用程序从访问令牌中原子地解码用户声明,而不是使用访问令牌去查找有关用户的信息的传统模型。使用 JWT,您将立即知道用户的名字、姓氏、用户 ID 以及您希望粘贴在 JWT 令牌中的任何其他数据。

      2。无会话

      要使用您的授权 API,您的客户端应用程序将向端点发出请求并发送 'Authorization': 'Bearer access_token'(其中 access_token 是您的访问令牌)。在传统应用程序中,必须在服务器端查找访问令牌以验证服务器是否授予它。关于 JWT 令牌的另一个很棒的一点是,当它们被发布时,有一个服务器端的秘密用于对它们进行签名。当服务器需要验证它们时,它只需使用服务器端的密钥对其进行签名,如果通过,服务器将根据解码令牌的声明授予 API 请求。无需将它们存储在服务器端,从而使您的架构更加简单。服务器无需针对每个授权的 API 请求与数据库进行通信。您将绕过许多问题,例如跨网络场同步访问令牌或完全存储它们(但您仍需要将刷新令牌存储在链接到用户的表中)。


      Cookie 与本地存储

      人们对 cookie 的一个很常见的误解是应该避免它们,因为它们会让您面临CSRF 攻击,但通常情况并非如此。发送会话 cookie 和基于这些 cookie 水合会话的 Web 表单等系统对 CSRF 攻击是开放的,但如果您正在构建单页面应用程序并且所有安全检查点都在您的 API 层,那么您的端点将检查Authorization您的不记名令牌的标头,而不是 cookie。如果您正在执行服务器端渲染并在那里使用 cookie 值,您应该了解 CSRF 威胁并实施预防方法。如果您使用 cookie,您应该确保它们设置了 HttpOnly 标志并且确实设置了 Secure 标志以保护您免受 MITM 威胁。


      由于您使用的是 node(或至少是 angular),我现在将插入一个我编写的库 - jwt-autorefresh。这个库的要点很简单,给它你的应用程序刷新机制(向你的刷新 api 发出 http 请求并随后将结果存储在 cookie 中的客户端代码)以及你想让令牌刷新之前的秒数它们的到期时间,它将处理您的客户端应用程序上的自动调度刷新。在内部,它会解码您的 JWT 令牌并查看其 exp 声明(到期时间)以确定它需要多长时间才能安排您的刷新。它具有诸如在刷新时间中添加少量抖动的功能,以便所有客户端实例不会同时尝试刷新。

      【讨论】:

        【解决方案4】:

        是的,这就是我现在为我的项目所做的。这是一种常见的模式。

        应用架构:

        [Web Applications]     <-HTTPS/TLS-> [Restful Web API]
        
        [PC Applications]      <-HTTPS/TLS-> [Restful Web API]
        
        [Mobile Applications]  <-HTTPS/TLS-> [Restful Web API]
                                             [Restful Web API] <-Authenticate-> [over LDAP or SQL Database]
                                             [Restful Web API] <-File Stream-> [File system/file server]
        

        注意事项:

        有一些注意事项会影响你的设计:

        1. 管理令牌生命周期。

          • 确保您的令牌有一个较短的过期日期(必须在 30 分钟或更短的时间内)。如果令牌已过期(或即将过期),那么您必须刷新您的令牌(有关详细信息,请查看 OAUTH2 中的refresh token)。
          • 确保您的令牌在注销后被丢弃。
          • 如果最终用户报告有人未经他们的许可使用他们的帐户。确保您可以终止小偷的令牌。(这在企业应用程序中很重要)
        2. 保护您的令牌

          • 避免将令牌存储在 cookie 中。如果可以,请使用本地存储。
          • 使用 HTTPS 而不是 HTTP。
          • 每个平台都必须使用单独的令牌:如果您计划将您的 API 公开给其他开发人员/平台/供应商,这将很有帮助。
          • 确保记录所有已发行令牌的历史记录(何时以及由谁发行)。假设您的应用程序受到攻击,您不知道令牌来自哪里。它将帮助您回答:Were your private key stolen by attacker?!
        3. 保护您的静态资源(*.pdf、图像、*.doc、...)

          • 您无法以正常方式保护您的静态资源(发送授权 HTTP 标头)。然后你必须通过 Cookie 发送它(我不鼓励这样做)。因此,您必须为静态资源生成安全令牌,该令牌必须在 5 分钟或更短时间内有效

        【讨论】:

        • 有许多好的实现考虑因素可能需要将令牌存储在 cookie 中。如果您没有将 cookie 值用于服务器端的自动会话水合,那么您将不会受到 XSRF 攻击。在这种情况下,它只是用作客户端上的存储容器。
        • @cchamberlain 是的,我同意这一点。在cookie 中存储token 有许多良好且安全的实现(但它打破了RESTful 最佳实践)。 Cookie & 自动会话很好。 Cookie还不错,我们会长期坚持cookies。但在这种情况下,我们为RESTful API 存储一个Token,它是无状态的/cookie-less,最好将您的令牌存储在内部存储中。当然,这取决于!使用 Cookie,你会考虑:XSRF & XSS 攻击。但是,您只需使用LocalStorage 处理 XSS,您的生活会更轻松:P
        • 我不确定令牌存储容器与 REST 有什么关系。 REST 是关于通过路由识别资源和通过 HTTP 动词进行操作。
        • @cchamberlain 是的,存储容器与 REST 无关。 Web Service 通过 HTTP Authorization Header => 授权您的请求,因此您无需担心 XSRF。毫无疑问,我同意你的解决方案。但是每个 HTTP 请求都会在 cookie 中包含我的 Token 的想法让我感到不安全。我的意见:如果它没有给您带来任何好处,请不要这样做。如果你的浏览器不支持 localStorage,那么 cookie 是最好的选择 :)
        【解决方案5】:

        @cchamberlain 也已经回答了...但是,您可以参考 [此处](使用 Active Directory 的 REST API 的授权方法),我认为这将帮助您解决问题,

        谢谢!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-11
          • 2022-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多