每个骨干同步请求发送令牌

时间:2013-01-07 20:59:29

标签: javascript api backbone.js coffeescript

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

  1. 活跃
  2. 拥有访问资源的权限
  3. 在Backbone中设置此功能的最简单方法是什么?我猜测唯一的方法是覆盖Backbone.sync,但代码会是什么样子? CoffeeScript首选。

    修改

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

4 个答案:

答案 0 :(得分:18)

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应用程序中的一个函数。

答案 1 :(得分:11)

你可以这样做:

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 */ )

答案 2 :(得分:1)

身份验证是应用的责任。

对于Backbone应用程序,auth逻辑应位于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功能是一种简单的替代方法。 / p>

使用我们之前定义的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!');
  });

答案 3 :(得分:-4)

Backbone.$.ajaxSetup({
   headers: { 'sid': 'blabla' }
});