Rails:路由前进行身份验证

时间:2014-07-11 21:29:48

标签: ruby-on-rails ruby-on-rails-4

什么。

我正在尝试在我的Rails API上实现基于令牌的基本身份验证。它适用于现有路线,但这里有一个问题:

当未经身份验证的用户访问不存在的路由时,它会显示404 - Not found页面,而不会显示401 - Unauthorized。如何在 验证路由之前让Rails检查身份验证

这是我的application_controller.rb

class Api::V1::ApiController < ApplicationController
  # Main controller inherited by all API controllers

  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :null_session

  # Enforce site-wide authentication
  before_action :authenticate

  def authenticate
    authenticate_token || render_unauthorized
  end

  def authenticate_token
    # Can we find a user with the authentication token used?
    authenticate_with_http_basic do |u, p|
      # Only search active, private keys
      @api_key = ApiKey.active.find_by(api_key: u, is_secret: true)
      @user = @api_key.user if @api_key

      return @api_key
    end
  end

  def render_unauthorized
    # Displays the Unauthorized message
    render json: JSON.pretty_generate({ 
      error: { 
        type: "unauthorized",
        message: "This page cannot be accessed without a valid API key."
        } 
      }), status: 401
  end
end

为什么

这样想:有人阻止你到办公室询问方向。你是否要求他们先提供一些身份证明,或者你只是向他们展示自己的方式?

如果是公职人员,我只是向他们展示道路......但如果我们在中央情报局受限制的特殊项目部门,我不在乎你要去哪里( even < / strike> 特别是如果你告诉我你正在寻找Office 404,我知道它不存在):我想先看一些ID。

编辑:使用基本身份验证

我最初提到了基于令牌的身份验证,但它实际上是Basic Auth(带有“username”=“token”)。

4 个答案:

答案 0 :(得分:5)

使用内置的rails认证,你无法实现你想要的,因为@ rich-peck解释道。 Rails首先评估您的路由,然后传递对控制器的请求。

您有两种选择。首先,做一下Stripe的人正在做的事情。将身份验证委派给应用服务器前面的Web服务器(在他们的例子中为nginx)。

$ curl -I https://api.stripe.com/this/route/doesnt/exist
HTTP/1.1 401 Unauthorized
Server: nginx
Date: Fri, 08 Aug 2014 21:21:49 GMT
Content-Type: application/json;charset=utf-8
Www-Authenticate: Basic realm="Stripe"
Cache-Control: no-cache, no-store
Stripe-Version: 2014-08-04
Strict-Transport-Security: max-age=31556926; includeSubDomains

或者,如果您希望/需要将其保存在ruby-land中,您可以使用机架中间件,它将在rails加载您的路线之前进行身份验证。您可以尝试https://github.com/iain/rack-token_auth

$ bin/rake middleware
...snip a bunch of middleware...
use Rack::TokenAuth
run Dummy::App::Application.routes

更新:使用HTTP基本身份验证

在这种情况下,您根本不需要其他库,因为rack通过Rack::Auth::Basic中间件内置了对http basic auth的支持,例如

config.middleware.use Rack::Auth::Basic do |username, password|
  username == "bob" && password == "secret"
end

更新:我可以将身份验证应用于API命名空间吗?

这取决于。由于机架中间件在rails加载路由,控制器等之前运行,因此它不知道您的命名空间。尽管如此,如果您的命名空间控制器也在URL级别进行了命名空间,例如全部在/api下,您可以检查request.path是否应用身份验证。

您可以使用此行为为装饰 Rack::Auth::Basic创建新的机架middlware,例如

class MyAPIAuth < Rack::Auth::Basic
  def call(env)
    request = Rack::Request.new(env)

    if request.path =~ /\A\/api\/?/
      super
    else
      @app.call(env)
    end
  end
end

# change your configuration to use your middleware
config.middleware.use MyAPIAuth do |username, password|
  username == "bob" && password == "secret"
end

答案 1 :(得分:4)

您需要做的就是在路线末尾附加一条全能路线.rb:

YourApp::Application.routes.draw do
  # Standard routes
  # ...

  # Catch-all
  get '*other', to: 'dummy#show'
end

然后让一个虚拟控制器在show动作中渲染404 - Not Found。如果有人访问不存在的路由,它将由dummy处理,它将在before filter中首先执行身份验证,如果失败则会呈现401 - Unauthorized

答案 2 :(得分:2)

我认为你已经超越了MVC

的想法

enter image description here

当您向Rails发送请求时,路由机制意味着获取请求的URL&amp;发送到特定区域(controller)。身份验证在控制器内部发生 - 允许您以模块化方式处理路由流量

我认为您在将用户发送到相应控制器之前进行身份验证的问题违反了MVC原则 - 您的身份验证(ping数据库)必须在之前发生到达控制器/ model


<强>验证

你签出了this Railscast吗?

enter image description here

它显示了如何在控制器中使用令牌身份验证 - 允许您保护API

答案 3 :(得分:0)

我假设你的Rails 3.X.X就像我一样,并且收到同样的错误(他们使用Rails 4进行Code School课程)。以下是您需要改变的内容:

before_action:authenticate 必须更改为 before_filter:authenticate

User.find_by(auth_token:token) 必须更改为 User.find_by_auth_token(令牌)