12

我在我的应用程序上使用 Devise,并希望创建一个全局 API 密钥,无需登录即可访问任何人帐户的 JSON 数据。

例如,假设我的 API Key 是1234,我有两个用户创建了两个不同的餐厅。

  • 用户 1 - 餐厅 1 (/restaurants/1)
  • 用户 2 - 餐厅 2 (/restaurants/2)

我打开了一个全新的浏览器并且没有登录任何东西,然后我传入了我的 URL .../restaurants/2.json?api_key=1234,我应该能够访问该餐厅的 JSON 数据,而无需以用户 2身份登录

最好的方法是什么?

我遵循了Railscast #352 Securing an API,因此我可以通过传入 API 密钥来访问 JSON 内容,但我必须登录才能看到任何内容。

编辑 1:使用 CanCan

我应该提到我也在使用 CanCan 作为角色,但不确定在这种情况下它是否会扮演任何角色(双关语不是有意的)。

编辑 2:使用 API 版本控制实现

我遵循了 Railscast #350 和 #352,它们教你如何创建 REST API 版本控制以及如何使用 API 密钥保护它。

这是我的控制器/api/v1/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 on REST API Versioning并将所有 JSON API 调用移至/apps/api/v1/...

然后,按照下面史蒂夫乔根森的解决方案,确保我的 API 模块继承自ActionController::Base而不是,ApplicationController这样它就绕过before_filter :authenticate_user!ApplicationController.

所以,我的编辑 2 代码看起来像这样:

module Api
  module V1
    class RestaurantsController < ApplicationController
    ...

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

4 回答 4

15

我知道自从有人问这个问题以来已经有一段时间了,但我想我会再提供一个我过去使用过的选项

class Api::ApplicationController < ApplicationController

skip_before_filter :authenticate_user!

这假设您的 API 目录中有一个应用程序控制器,您的所有 api 控制器都从该控制器继承。如果没有,您可以将跳过放在每个控制器中。

于 2014-03-03T20:39:05.457 回答
12

您可以before_filter在 Controller 中执行此操作。

目前,您可能有类似的东西:

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 键的身份验证。

于 2012-08-02T20:43:59.497 回答
7

没有人提到的一个选项是为 API 提供一组完全独立的控制器,这些控制器不继承自ApplicationController.

我已经看到了 API 控制器存在于文件中的模式,/app/controllers/api/v1/somethings.rb并且可以通过/api/v1/somethings. 每个特定的 API 控制器都从继承自 的基本 API 控制器继承ActionController::Base,因此不包括在 上定义的任何过滤器ApplicationController

于 2012-08-03T06:40:50.943 回答
5

Gazler 的替代方法是使用 except:

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
于 2012-08-03T06:15:35.897 回答