0

我有一个 rails api 设置,使用设计用于 auth,backbone.js 用于前端。我能够让用户注册并登录,但我无法向 api 发出后续请求。我需要在签名后的每个请求中附加一个身份验证令牌和一个电子邮件地址,以便发出进一步的请求,但我不知道该怎么做。

我的应用程序的流程是这样的

  1. 向 /sign_in 发送获取请求以使用自定义主干同步设置 csrf 标头
  2. 使用凭据向 /sign_in 发送发布请求并取回身份验证令牌
  3. 随每个后续请求发送令牌和电子邮件

随每个请求发送令牌和电子邮件是我遇到的问题。如何将其附加到骨干网的请求中?

我应该像使用 csrf 令牌一样将它添加到 http 标头吗?

这是我到目前为止的设置。

带过滤器的 API 控制器

class ApiController < ApplicationController
  respond_to :json
  before_filter :authenticate_user_from_token!
  # This is Devise's authentication
  before_filter :authenticate_api_v1_user!

  private

  def authenticate_user_from_token!
    user_email = params[:email].presence
    user = user_email && User.find_by_email(user_email)

    if user && Devise.secure_compare(user.authentication_token, params[:token])
      sign_in user, store: false
    end
  end
end

会话控制器

module Api
  module V1
    class SessionsController < Devise::SessionsController

  skip_before_filter :authenticate_user!, only: :create
  after_action :set_csrf_header, only: [:new, :create, :destroy]

  def new
    render nothing: true
  end

  def create
    user = User.find_for_database_authentication(:email => params[:email])

    if user && user.valid_password?(params[:password])
      user.ensure_authentication_token  # make sure the user has a token generated
      render :json => { :user => user }, :status => :created
    else
      return invalid_login_attempt
    end
  end

  def destroy
    # expire auth token
    user = User.where(:authentication_token => params[:authentication_token]).first
    user.reset_authentication_token!
    render :json => { :message => ["Session deleted."] },  :success => true, :status => :ok
  end

  private

  def invalid_login_attempt
    warden.custom_failure!
    render :json => { :errors => ["Invalid email or password."] },  :success => false, :status => :unauthorized
  end

  def set_csrf_header
    response.headers['X-CSRF-Token'] = form_authenticity_token
  end

  def form_authenticity_token
    session[:_csrf_token] ||= SecureRandom.base64(32)
  end

end
end
end

覆盖backbone.sync以设置csrf标头

require([
'backbone',
'router',
'app',
'jquery.mobile',
'google',
'models/session_model'
], function(Backbone, Router, AppView, JQMobile, google) {
    window.Droppin = window.Droppin || {};

// overide backbone.sync
Backbone._sync = Backbone.sync;

Backbone.sync = function(method, model, options) {
  if (!options.noCSRF) {
    var beforeSend = options.beforeSend;

    // Set X-CSRF-Token HTTP header
    options.beforeSend = function(xhr) {
      var token = Droppin.csrfToken;  
      if (token) xhr.setRequestHeader('X-CSRF-Token', token);

      // this will include session information in the requests
      xhr.withCredentials = true;

      if (beforeSend) return beforeSend.apply(this, arguments);
    };
  }

  var complete = options.complete;
  options.complete = function(jqXHR, textStatus) {

     // If response includes CSRF token we need to remember it
     var token = jqXHR.getResponseHeader('X-CSRF-Token') 
     if (token) Droppin.csrfToken = token;

     model.trigger('sync:end');
     if (complete) complete(jqXHR, textStatus);
  };

  // Serialize data, optionally using paramRoot
  if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
    options.contentType = 'application/json';
    data = JSON.stringify(options.attrs || model.toJSON(options));
    if (model.paramRoot) {
      data = {};
      data[model.paramRoot] = model.toJSON(options);
    } else {
      data = model.toJSON();
    }
    options.data = JSON.stringify(data);
  }

  return Backbone._sync(method, model, options);
};


Backbone.history.start();
new AppView();
new Router();
});
4

1 回答 1

0

您可以全局覆盖所有 jquery ajax 请求以在标头中包含某些内容。

于 2014-08-31T20:38:38.303 回答