Как пропустить проверку подлинности при использовании ключа 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