Как пропустить проверку подлинности при использовании ключа API?

Я использую Devise для своего приложения и хотел бы создать глобальный ключ API, который может получить доступ к данным JSON любой учетной записи без необходимости входа в систему.

Например, скажем, мой API-ключ 1234, и у меня есть два пользователя, которые создали два разных ресторана.

  • Пользователь 1 - Ресторан 1 (/рестораны/1)
  • Пользователь 2 - Ресторан 2 (/рестораны/2)

И я открываю совершенно новый браузер и нигде не вошел, и я перехожу в свой URL .../restaurants/2.json?api_key=1234, я должен иметь доступ к данным JSON этого ресторана без необходимости входа в систему как User 2

Каков наилучший способ сделать это?

Я следил за Railscast # 352 Защита API, поэтому я могу получить доступ к материалам JSON, передав ключ API, но я должны войти в систему, чтобы увидеть что-нибудь.

Изменить 1: Использование CanCan

Я должен упомянуть, что я также использую CanCan для ролей, но не уверен, что это сыграет какую-либо роль (каламбур не предназначен) в этой ситуации.

Редактировать 2: Имплементирование с версией API

Я следил за Railscast # 350 и # 352, которые рассказывают вам, как создавать REST API Versioning и как его защищать с помощью API-ключа.

Здесь мои контроллеры /api/v 1/restaurants/restaurants_controller.rb выглядят так:

module Api
  module V1
    class RestaurantsController < ApplicationController
      before_filter :restrict_access

      respond_to :json

      def index
        respond_with Restaurant.all
      end

      def show
        respond_with Restaurant.find(params[:id])
      end

    private

      def restrict_access
        api_key = ApiKey.find_by_access_token(params[:api_key])
        head :unauthorized unless api_key        
      end
    end
  end
end

И мой application_controller.rb все еще имеет код before_filter :authenticate_user!.

Решение

Я сначала выполнил Railscast # 350 в REST API Versioning и переместил все мои вызовы API JSON на /apps/api/v1/...

Затем, следуя приведенному ниже решению Стив Йоргенсена, убедитесь, что мой API-модуль унаследован от ActionController::Base вместо ApplicationController, чтобы он обошел код Devise before_filter :authenticate_user! внутри ApplicationController.

Итак, мой код Edit 2, когда выглядит так:

module Api
  module V1
    class RestaurantsController < ApplicationController
    ...

к

module Api
  module V1
    #Replace 'ApplicationController' with 'ActionController::Base'
    class RestaurantsController < ActionController::Base
    ...

Ответ 1

Один из вариантов, о котором никто не упомянул, состоит в том, чтобы иметь полностью отдельный набор контроллеров для API, которые не наследуются от ApplicationController.

Я видел шаблон, используемый в контроллерах API в файлах, таких как /app/controllers/api/v1/somethings.rb, и доступен через такие маршруты, как /api/v1/somethings. Каждый из конкретных контроллеров API наследует от базового API-контроллера, который наследует от ActionController::Base, поэтому не включает ни один из фильтров, определенных в ApplicationController.

Ответ 2

Я знаю, что прошло некоторое время с тех пор, как это было задано, но подумал, что я бы выбрал еще один вариант, который я использовал в прошлом

class Api::ApplicationController < ApplicationController

skip_before_filter :authenticate_user!

Это предполагает, что у вас есть контроллер приложения в вашем каталоге API, на который наследуются все ваши контроллеры api. Если нет, вы можете просто пропустить пропуск в каждом контроллере.

Ответ 3

Вы можете сделать это с помощью before_filter в своем контроллере.

В настоящее время у вас, вероятно, есть что-то вроде:

class SomeController < ApplicationController
  before_filter :authenticate_user!
end

Вместо того, чтобы называть это, вы можете определить другой метод (в идеале в ApplicationController)

class ApplicationController < ActionController::Base
  before_filter :authenticate_or_token

  private
  def authenticate_or_token
    if params[:api_key] == 1234
      @current_user = User.new(:admin => true, :any => "other", :required => "fields")
      return current_user
    end
    authenticate_user!
  end

Я бы рекомендовал использовать более надежный метод аутентификации, такой как OAuth, но это должно работать для простой аутентификации на основе 1 ключа.

Ответ 4

Альтернативой Газлеру было бы использовать исключение:

class ApplicationController < ActionController::Base
  before_filter :authenticate_user!, except: :some_json_method

  def some_json_method
    render :nothing unless params[:api_key] == '1234'

    render :json
  end
end

Таким образом, вы не открываете все приложение для держателя ключа (в зависимости от ваших потребностей, вам это нужно или нет). Если вам нужно использовать несколько методов для ключа, возможно, вы также можете использовать что-то вроде:

class ApplicationController < ActionController::Base
  JSON_METHODS = [method_1, method2]
  before_filter :authenticate_user!, except: JSON_METHODS
  before_filter :authenticate_token, only: JSON_METHODS

  private
  def authenticate_token
    params[:api_key] == '1234'
  end
end