Rails api和原生移动应用程序身份验证

33
我知道关于这个话题有很多信息,但是我找不到最新的信息。我看到了像与Rails和Android身份验证相关的这个主题,但是我发现TokenAuthenticatable现在从devise中删除了
我的问题很简单:是否有一种好的方法可以使用Rails 4从本机Android和iPhone应用程序对用户进行身份验证?是否有人知道提供解决方案的好教程或文章?
Adam Waite添加悬赏:
我刚刚在这个问题上开了500元的赏金,因为我找不到正确的实践方法,将iOS应用程序中的用户身份验证到Rails API。这就是我考虑做的事情,但我不知道它是否安全?!:
让我们假设我们有一个User记录。用户已经注册了一个帐户,这创建了一个具有email列和password_digest列的数据库中的User记录。
当用户登录时,我希望该用户一直保持已验证状态,直到明确退出移动应用程序。
我想我们需要基于令牌的身份验证。当创建“用户”时,我可能会创建一个ApiKey记录,并将其保存为“用户”记录上的关联。
当用户登录/注册时,响应将包含一个API令牌(类似于SecureRandom.hex),它将保存在iOS密钥链中,并与所有后续请求一起使用,通过传递标题验证用户并使用以下内容进行验证:
before_filter :restrict_access

private

def restrict_access
authenticate_or_request_with_http_token do |token, options|
  ApiKey.exists?(access_token: token)
end

这是否安全?我需要在每个请求中刷新token并将其包含在响应中吗?

我还有什么其他选项?Facebook、Twitter和Pinterest等网站是如何处理的?

我知道OAuth2.0,但那不是用于授权外部应用程序吗?

是否有可以管理所有这些的gem?

抱歉,我完全不确定。

最佳答案可获得500点奖励。


1
我终于成功了我的任务,现在我拥有了一个Rails后端和一个能够注册、登录和注销的安卓应用,而且不需要每次重新认证。很快我会做一些示例。它运行得非常好,但我不知道它是否安全,我不擅长处理这方面的问题... 所以我在等待回复。 - agonist_
我在iOS上也可以做同样的事情,但像你一样,我不知道它是否容易被黑客攻击! - Adam Waite
1
也许我对这个问题的回答会有帮助。 - Ashitaka
1
我正在做的是使用Troy提到的apiauth gem修改https://github.com/danahartweg/authenticatable_rest_api应用程序中的令牌生成。该应用程序具有Ashitaka提到的所有步骤。 - LightBox
@agonist_ 你能解释一下你是怎么做到的吗? - Alberto Alegria
4个回答

14

这是我研究的一个解决方案要点,欢迎编辑、纠正、驳斥等。

SessionsController < ApplicationController
  skip_before_filter :authenticate_user, :only => [:create]
  def create
    user = User.where("username = ? OR email = ?", params[:username_or_email], params[:username_or_email]).first
    if user && user.authenticate(params[:password])
      api_key = user.find_api_key
      if !api_key.secret_key || api_key.is_expired?
        api_key.set_expiry_date
        api_key.generate_secret_key
      end
      api_key.save
      render json: api_key, status: 201     
    else
      status: 401
    end
  end

注意ApiAuth.authentic?方法和request对象。请求必须在客户端使用HMAC算法进行签名。

ApplicationController < ActionController::Base
  respond_to :json
  force_ssl
  protect_from_forgery with: :null_session 
  before_filter :authenticate_user
  private
  def authenticate_user
    if authenticate_user_from_secret_key
      return true
    else
      head :unauthorized 
    end
  end
  def authenticate_user_from_secret_key
    userid = ApiAuth.access_id(request)
    currentuser = userid && User.find_by_id(userid)
    if ApiAuth.authentic?(request, currentuser.find_api_key.secret_key)
      return true
    else
      return false
    end
    false
  end

用户创建/注册

UsersController < ApplicationController
  skip_before_filter :authenticate_user, :only => [:create]
  def create
      user = User.create(user_params)
      if !user.new_record?
        render json: user.find_apit_key, status: 201

      else
       # error
      end
  end

API密钥模型。与#352 Railscast中的API密钥模型相似,唯一的区别在于ApiAuth密钥生成。

class ApiKey < ActiveRecord::Base
  before_create :generate_secret_key, :set_expiry_date
  belongs_to :user   
  def generate_secret_key
    begin
    self.secret_key = ApiAuth.generate_secret_key
    end while self.class.exists?(secret_key: secret_key)
  end

用户模型。

class User < ActiveRecord::Base
  has_secure_password
  before_save :ensure_api_key 
  has_many :api_keys 
  def find_api_key
   self.api_keys.active.ios.first_or_create
  end

在客户端,必须使用HMAC算法对请求进行签名。

代码来源:
[SHA1 HMAC密钥生成/身份验证]https://github.com/mgomes/api_auth
[控制器和模型]https://github.com/danahartweg/authenticatable_rest_api


6
我是一个API开发者,曾遇到过这个问题。你可以通过令牌和自定义授权来解决,但我会告诉你我们的应用程序是如何处理的,它为六位数字级别的用户提供服务。
至少对于iOS设备而言,设备将为您处理会话,这意味着如果iOS应用程序上的用户使用参数向/users/sign_in 发送POST请求,
user: { 
  password: 'mypassword',
  email: 'testuser@example.com',
  remember_me: true # optional
}

iOS设备会为您安全地持久存储该会话。
现在,如果您想使用OAuth 2路线,我实际上维护了一个针对Rails 4的宝石,名为OAuth 2可提供性,我添加了一个非常酷的功能,允许用户通过“授权”屏幕,因为显然如果您开发软件,则不需要用户确认他们信任您。
如果您决定使用OAuth 2,您将需要使用所谓的隐式访问令牌。 这是长而枯燥的OAuth2规范

Rails 4项目可以在github上找到 https://github.com/bwheeler96/devise-oauth2-provider-rails4

如果您不在Rails 4上,可以使用原始宝石 https://github.com/socialcast/devise_oauth2_providable

顺便提一下,这个gem还需要改进,如果有人想要帮助改进它,请不要客气地fork 这个仓库


5
你走在正确的道路上,但用户的令牌应该只用于标识哪个用户发出请求。由于像你所推测的那样,在每个请求中更改令牌,黑客可以拦截数据流获取令牌,然后在随后的请求中“成为”该用户,因此你仍需要某种形式的身份验证。
通过在每个请求中更改令牌,你消除了拦截问题,但一旦有人拦截了令牌,他们就可以进一步利用系统,甚至修改响应。解决这个问题的方法之一是使用HMAC(Amazon Web Services使用的算法)。它提供了一个唯一的请求签名(哈希),对于每个请求都是唯一的,不需要更改密钥,并且不能预测未来的请求。
Rails有一个Ruby gem实现了服务器端的HMAC签名请求和生成请求,当进行服务器到服务器通信时也可以生成请求。对于像你这样的客户端到服务器的请求,你需要在iOS或Android端生成签名并在服务器上进行身份验证。
考虑使用 ApiAuth gem在服务器端执行工作。在iOS客户端上,考虑使用 HBHMAC库生成签名。查看ApiAuth的具体实现,因为它向数据添加时间戳以防止重放攻击,所以您可能需要在将其传递给HBHMAC之前向数据添加字段。
总之,使用HMAC身份验证将通过利用单向哈希算法避免中间人攻击和重放攻击,从而防止攻击者生成真实请求,即使他们能够拦截有效请求。

看起来不错,感谢您的回答。在接受之前会等待其他的意见,谢谢。 - Adam Waite

2

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