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

79
我正在编写一个Rails 4应用程序,将为尚未开发的移动应用程序公开API。用户将使用移动应用程序中的电子邮件和密码进行身份验证。
虽然我找到了很多关于这个主题的信息,但很难分辨哪些是过时或不太优化的。我阅读了关于HTTP基本认证的内容,但它似乎不太安全,还有基于令牌的HTTP认证,但我不确定如何将其与常规的电子邮件和密码身份验证相结合(顺便说一句,我正在使用Devise)。
我只想知道目前实现这种身份验证的最佳实践是什么,这样我就可以确保走上正确的路线。

1
不了解具体情况,我的想法是使用 OAuth2。可以使用 doorkeeper 或 oauth2 宝石。 - Joe Essey
4个回答

49
从安全角度来看,重要的一点是交换用户的电子邮件和密码一次性地换取一个令牌,然后在随后的请求中使用该令牌。这是因为:
  1. 您不希望客户端应用程序负责保存用户的密码,在那里可能会因为错误或攻击而泄漏;以及
  2. 服务器发布的令牌为您(和您的用户)提供了必要的功能,如有必要,可以使令牌过期,例如锁定被盗设备或阻止行为不当的API客户端。
有很多方法可以实现这个目标,具体复杂程度不同。
以下是一个非常新的教程,详细介绍了在Rails中创建基于令牌身份验证的API(不使用Devise,但仍然相关于理解概念):https://labs.kollegorna.se/blog/2015/04/build-an-api-now/

谢谢!这正是我需要理解的。 - Roma149
除了设备令牌,您还可以使用设备标识符。设备标识符可以帮助您正确识别设备,这可能有助于防止其他设备访问您的网站。此答案中的设备标识符可以帮助您更好地理解。 - duykhoa

5

另一个选项是将下面的模块包含在你的设备 MODEL 中,并将 auth_token 添加到你的表中。

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
...

编辑:修正了破坏代码的错字


此外,请查看Rails的has_secured_password功能。 - Shiva
我相信token_authenticable已经被弃用了。 - Kelsey Hannan

4

@Roma149 这更多是个人偏好,但大多数新手使用Devise,因为这是我认为最容易的选择。OAuth2也是一个不错的选择。 更重要的是,你可以随时访问The Ruby Toolbox

那里有很多关于gems的好信息,他们甚至会告诉你gem的年龄和受欢迎程度。这还将使你区分出社区现在真正关注的gems或已经过时的gems。

请记住,在Ruby和Ruby On Rails中,不仅仅是哪个gem更好,而是什么最适合你的项目!


3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接