Rails API : 实现身份验证的最佳方式?

Rails API : Best way to implement authentication?

我正在编写一个 Rails 4 应用程序,该应用程序将为尚未开发的移动应用程序公开 API。用户将使用来自移动应用程序的电子邮件和密码进行身份验证。

虽然我发现了很多关于该主题的信息。很难辨别什么是过时的或非最佳的。我读过 HTTP Basic Auth,它似​​乎不太安全,以及基于 HTTP Token 的 Auth,但我不确定如何将其与常规电子邮件和密码身份验证结合起来(我正在使用 Devise by方式)。

我只是想知道目前关于如何实现它的最佳实践是什么,所以我一定会走正确的路。

@Roma149 这更多是个人喜好,但大多数刚开始使用 Devise 的人都使用它,因为它是最简单的 IMO。 OAuth2 也是一个不错的选择。 作为更重要的说明,您可以随时转到 The Ruby Toolbox

那里有很多关于 gem 的有用信息,他们甚至会告诉您 gem 的年龄和受欢迎程度。这也将使您能够区分 gem 社区现在真正热衷于什么或什么已经过时了。

请记住 Ruby 和 Ruby 在 Rails 中并不总是更好 gem 明智,而是最适合您的项目!

从安全的角度来看,重要的一点是将用户的电子邮件和密码交换一次令牌,然后使用该令牌进行后续请求。这是因为:

  1. 您不希望客户端应用程序负责保存用户的密码,因为错误或攻击可能会导致密码泄露;和
  2. 服务器颁发的令牌使您(和您的用户)能够在必要时使令牌过期,例如锁定被盗设备或阻止行为不端的 API 客户端。

有多种不同复杂程度的方法可以实现这一点。

这是一个最新的教程,其中包含使用基于令牌的身份验证在 Rails 中创建 API 的详尽演练(不使用 Devise,但仍然与理解概念相关) : https://labs.kollegorna.se/blog/2015/04/build-an-api-now/

另一种选择是将下面的模块包含在您的设计模型中,并为您添加 auth_token table。

app/models/concerns/token_authenticable.rb

module TokenAuthenticatable
  extend ActiveSupport::Concern

  included do
    before_save :ensure_auth_token
  end

  module ClassMethods
    def find_by_token(token)
      find_by(auth_token: token)
    end
  end

  def ensure_auth_token
    self.auth_token = generate_auth_token if auth_token.blank?
  end

  private

  def generate_auth_token
    loop do
      token = Devise.friendly_token
      break token unless self.class.exists?(auth_token: token)
    end
  end
end

app/controllers/api/v1/login_controller.rb

...
 def login_user(params)
    if params[:authentication]
      @user = User.find_by(auth_token: params[:authentication])
      if @user.nil?
        render json: err('login user by token failed', ERR_USER_NOT_FOUND), status: :not_found
        event('login_user_by_auth_failed', 'token', params[:authentication])
        return
      else
        render status: :ok, json: @user
        return
      end
    else
      user = user.find_by(email: params[:email])
      if user.nil?
        event('login_user_failed_not_found', 'user_email', params[:email])
        render json: err("login user not found #{params[:email]}", ERR_USER_NOT_FOUND), status: :not_found
        return
      end
      if user.access_locked?
        event('login_user_blocked', 'user_id', user.id)
        render json: err("login user account is locked : #{user.id}", ERR_USER_LOCKED), status: :unauthorized
        return
      end
      unless user.try(:valid_password?, params[:password])
        event("login_user_password_does_not_match #{user.id}", 'user_id',  user.id)
        render json: err('login user password does not match', ERR_PASSWORD_NOT_MATCH), status: :unauthorized
        return
      end
      event('login_user_succeeded', 'user_id', user.id)
      @user= user
      if @user.save
        response.headers['authentication'] = @user.auth_token
        render status: :ok, json: @user
        return
      else
        render json: @user.errors, status: :unprocessable_entity
        return
      end
    end
  end
...

编辑:更正了代码破译错误

Tiddle gem 为 API-only Ruby Rails 应用程序中的令牌身份验证提供设计策略。它的主要特点是支持每个用户使用多个令牌

https://github.com/adamniedzielski/tiddle