【问题标题】:Send token with every backbone sync request随每个骨干网同步请求发送令牌
【发布时间】:2012-12-21 15:40:41
【问题描述】:

我的 PHP api 要求我的前端 Backbone 应用程序的每个请求都提交一个用户令牌,以确保用户...

  1. 处于活动状态
  2. 有权访问资源

在 Backbone 中进行设置的最简单方法是什么?我猜唯一的方法是覆盖 Backbone.sync,但代码会是什么样子?首选 CoffeeScript。

编辑

还有两件事
1. 如果我收到403: Access Forbidden Error,我想将用户重定向到/login
2. 当应用程序启动时,我从 localStorage 中提取包含令牌的用户模型
3. 我有一个 baseModel 和 baseCollection,所有模型/集合都来自

【问题讨论】:

  • 服务器如何获得访问令牌?在 URL 中,作为 HTTP 标头,还是在请求正文/JSON 中?我在这里回答了类似的问题:stackoverflow.com/questions/13763280/…
  • generic $_REQUEST['token'] - 看看你的答案,我已经有 $.ajaxPrefilter 覆盖了基本 url - 如果我添加令牌作为数据参数会起作用吗?
  • 我不知道$_REQUEST['token'] 是什么 - 我猜是在同一个存储桶中包含正文、标题、会话等的一些 PHP 包罗万象?如果是这样,只需将其添加到 jQuery ajax 设置中的 headers 对象即可。但不确定。

标签: javascript api backbone.js coffeescript


【解决方案1】:

身份验证是应用程序的责任。

对于 Backbone 应用,身份验证逻辑应在 Backbone 代码中,并且应不惜一切代价避免更改全局 jQuery 的 ajax 行为。

ajaxSetupajaxSend 的缺点

来自ajaxSetup 上的 jQuery 文档:

注意:此处指定的设置将影响对$.ajax的所有调用或 基于 Ajax 的衍生产品,例如 $.get()。这可能会导致不受欢迎的 行为,因为其他调用者(例如,插件)可能期待 正常的默认设置。出于这个原因,我们强烈推荐 反对使用此 API。相反,在 调用或定义一个简单的插件来做到这一点。

ajaxSend 与上面提到的问题相同。与ajaxSetup 相比,它的唯一优势是每次都调用一个函数,这比传递给ajaxSetup 的基于对象的选项更灵活。

最安全的方式,AuthModelAuthCollection

将身份验证逻辑放入基础模型和集合中。这是范围最广的解决方案。

在这里,您可以使用现有的BaseModel,但我仍然倾向于将BaseModelAuthModel 分开,因为您可能希望创建一个使用您的基本模型但也使用不同的自定义模型例如外部 API。

由于模型和集合的新 sync 函数相似,但两者可能有不同的父实现,因此我制作了一个简单的函数生成器。

/**
 * Generates a new sync function which adds the token to the request header 
 * and handles a redirect on error.
 * @param  {Function} syncFn the parent `sync` function to call.
 * @return {Function}  a new version of sync which implements the auth logic.
 */
var authSyncFunction = function(syncFn) {
    return function(method, model, options) {
        options = options || {};

        var beforeSend = options.beforeSend,
            error = options.error;

        _.extend(options, {
            // Add auth headers
            beforeSend: function(xhr) {
                xhr.setRequestHeader('Authorization', "Bearer " + yourTokenHere);
                if (beforeSend) return beforeSend.apply(this, arguments);
            },

            // handle unauthorized error (401)
            error: function(xhr, textStatus, errorThrown) {
                if (error) error.call(options.context, xhr, textStatus, errorThrown);
                if (xhr.status === 401) {
                    Backbone.history.navigate('login');
                }
            }
        });

        return syncFn.call(this, method, model, options);
    };
};

在模型和集合上都使用生成器。

var AuthModel = BaseModel.extend({
    sync: authSyncFunction(BaseModel.prototype.sync)
});

var AuthCollection = BaseCollection.extend({
    sync: authSyncFunction(BaseCollection.prototype.sync)
});

然后您就可以在您确定需要身份验证的模型和集合上使用它们了。由于您已经在使用基本模型和集合,只需将BaseModel.extend 更改为AuthModel.extend

虽然我知道您要求对 403 Forbidden 响应进行重定向,但我认为它应该在 401 Unauthorized 上。见403 Forbidden vs 401 Unauthorized HTTP responses

覆盖 Backbone 的 sync

如果您此时不想更改所有模型和集合,但仍想遵循良好做法并避免更改全局 ajax 设置,则覆盖 Backbone.sync 函数是一个简单的替代方案。

使用我们之前定义的sync 生成器:

Backbone.sync = authSyncFunction(Backbone.sync);

管理本地存储和身份验证

要管理本地存储中的数据,请查看Backbone-session

这是一个很好的 Backbone 模型实现,它与本地存储而不是 REST API 同步。它还提供了一个很好的界面来管理身份验证。

// Extend from Session to implement your API's behaviour
var Account = Session.extend({
  signIn: function () {},
  signOut: function () {},
  getAuthStatus: function () {}
});

// Using the custom Account implementation
var session = new Account();
session.fetch()
  .then(session.getAuthStatus)
  .then(function () {
    console.log('Logged in as %s', session.get('name'));
  })
  .fail(function () {
    console.log('Not yet logged in!');
  });

【讨论】:

    【解决方案2】:

    Backbone 使用 jQuery 的 $.ajax,因此您可以使用 $.ajaxSetup 来“为未来的 Ajax 请求设置默认值”:

    $.ajaxSetup({
       headers: {
         "accept": "application/json",
         "token": YOUR_TOKEN
       }
    });
    

    更新:对这个想法的改进(感谢@Glen)是使用$.ajaxSend 在每次将令牌设置在请求的标头中之前检查是否存在:

    $(document).ajaxSend(function(event, request) {
       var token = App.getAuthToken();
       if (token) {
          request.setRequestHeader("token", token);
       }
    });
    

    其中 App.getAuthToken() 是 Backbone 应用程序中的一个函数。

    【讨论】:

    • jQuery 文档不推荐使用$.ajaxSetup。请参阅此处:api.jquery.com/jQuery.ajaxSetup 另一种方法是使用 $.ajaxSend 添加适当的标题。
    • 还可以查看有关 OAuth 2.0 的演讲以及他们如何使用 $.ajaxSend 在每个请求中传递令牌:youtu.be/khnmMv4_RCE?t=13m40s
    • $.ajaxSend 应根据As of jQuery 1.9, all the handlers for the jQuery global Ajax events, including those added with the .ajaxSend() method, must be attached to document. Link 改为$(document).ajaxSend
    • ajaxSendajaxSetup 在这种情况下应避免使用,以支持特定于应用程序的解决方案。
    【解决方案3】:
    Backbone.$.ajaxSetup({
       headers: { 'sid': 'blabla' }
    });
    

    【讨论】:

      【解决方案4】:

      你可以这样做:

      var _sync = Backbone.sync;
      Backbone.sync = function(method, model, options) {
      
          if( model && (method === 'create' || method === 'update' || method === 'patch') ) {
              options.contentType = 'application/json';
              options.data = JSON.stringify(options.attrs || model.toJSON());
          }
      
          _.extend( options.data, {
              "access_token": "some-token"
          });
      
          return _sync.call( this, method, model, options );
      }
      

      只需监听 fetch/save 方法的失败事件即可将用户重定向到/login

      model.fetch().fail( /* redirect */ )
      

      【讨论】:

      猜你喜欢
      • 2020-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-29
      • 2017-10-19
      • 1970-01-01
      • 2012-09-14
      • 1970-01-01
      相关资源
      最近更新 更多