草庐IT

javascript - 具有多个客户端平台的应用程序服务器 API 的 session 设计

coder 2023-05-29 原文

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

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

  • 对于桌面和移动应用程序,“登录”功能将使用内部 API 来传达凭据,作为返回,将收到一个永久 token 。桌面/移动应用程序将存储 token 并将其用于对应用程序服务器的任何后续请求。从桌面/移动应用程序“注销”后, token 将在服务器端丢弃,而在前端应用程序端被遗忘。
  • 对于 Web 界面,angular 应用程序将在登录后将提供的 token 保留为 cookie,并将加载它并将其用于向应用程序服务器发出的任何请求。

  • 这是一种常见的模式吗?

    最佳答案

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

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

    JWT 代币

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

    1. 客户 claim

    在您的客户端应用程序授权后,您需要做的第一件事是查找各种内容来构建您的 UI。 JWT token 的美妙之处在于它们将您的所有用户声明(包括您的应用程序自定义用户声明)作为 JSON 对象有效负载存储在编码字符串中,该字符串可以通过首先在 . 上拆分 token 来在客户端进行解码。 ,将其分解为 [ 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)


    这允许您的客户端应用程序以原子方式解码用户声明,只不过是访问 token ,而不是使用访问 token 查找有关用户信息的传统模型。使用 JWT,您将立即知道用户的名字、姓氏、用户 ID 以及您想保留在 JWT token 中的任何其他数据。

    2. 无 session

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

    Cookie 与本地存储

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

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

    关于javascript - 具有多个客户端平台的应用程序服务器 API 的 session 设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39794732/

    有关javascript - 具有多个客户端平台的应用程序服务器 API 的 session 设计的更多相关文章

    1. ruby-on-rails - Rails 3 中的多个路由文件 - 2

      Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

    2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

      我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

    3. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

      我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

    4. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

      我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

    5. ruby - 多个属性的 update_column 方法 - 2

      我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

    6. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

      我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

    7. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

      我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

    8. ruby - 使用多个数组创建计数 - 2

      我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']

    9. ruby-on-rails - before_filter 运行多个方法 - 2

      是否有可能:before_filter:authenticate_user!||:authenticate_admin! 最佳答案 before_filter:do_authenticationdefdo_authenticationauthenticate_user!||authenticate_admin!end 关于ruby-on-rails-before_filter运行多个方法,我们在StackOverflow上找到一个类似的问题: https://

    10. ruby-on-rails - Rails 3.1 中具有相同形式的多个模型? - 2

      我正在使用Rails3.1并在一个论坛上工作。我有一个名为Topic的模型,每个模型都有许多Post。当用户创建新主题时,他们也应该创建第一个Post。但是,我不确定如何以相同的形式执行此操作。这是我的代码:classTopic:destroyaccepts_nested_attributes_for:postsvalidates_presence_of:titleendclassPost...但这似乎不起作用。有什么想法吗?谢谢! 最佳答案 @Pablo的回答似乎有你需要的一切。但更具体地说...首先改变你View中的这一行对此#

    随机推荐