用户认证与葡萄和设计 [英] User Authentication with Grape and Devise

查看:238
本文介绍了用户认证与葡萄和设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有困难的理解,并正确实施 用户验证中的API。换句话说,我有严重的问题,了解葡萄API与前端框架,如Backbone.js的,AngularJS或Ember.js的整合。

I have difficulties to understand and also properly implement User Authentication in APIs. In other words, I have serious problem to understand the integration of Grape API with front-end frameworks such as Backbone.js, AngularJS or Ember.js.

我试图转动所有不同的方法和读了很多关于这一点,但谷歌返回我真正坏的资源,在我看来,像有这个主题真的没有好文章 - Rails和用户认证与设计和前端框架

I'm trying to pivot all different approaches and read a lot about that, but Google returns me truly bad resources and it seems to me, like there is no really good article on this topic - Rails and User authentication with Devise and front-end frameworks.

我将描述我目前的支点,我希望你能提供我一些反馈我的实现,也许我指向正确的方向。

I will describe my current pivot and I hope you can provide me some feedback on my implementation and maybe point me to the right direction.

当前实施

我有后台的 Rails的REST API 与下面的的Gemfile (我会故意缩短所有文件code)

I have backend Rails REST API with following Gemfile(I will purposely shorten all file code)

gem 'rails', '4.1.6'
gem 'mongoid', '~> 4.0.0'
gem 'devise'
gem 'grape'
gem 'rack-cors', :require => 'rack/cors'

我的当前实现仅具有以下路线的API( 的routes.rb

My current implementation has only APIs with following Routes(routes.rb):

api_base      /api        API::Base
     GET        /:version/posts(.:format)
     GET        /:version/posts/:id(.:format)
     POST       /:version/posts(.:format)
     DELETE     /:version/posts/:id(.:format)
     POST       /:version/users/authenticate(.:format)
     POST       /:version/users/register(.:format)
     DELETE     /:version/users/logout(.:format)

我创建了下面的模型 user.rb

I created have following model user.rb

class User
  include Mongoid::Document
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  field :email,              type: String, default: ""
  field :encrypted_password, type: String, default: ""

  field :authentication_token,  type: String

  before_save :ensure_authentication_token!

  def ensure_authentication_token!
    self.authentication_token ||= generate_authentication_token
  end

  private

  def generate_authentication_token
    loop do
      token = Devise.friendly_token
      break token unless User.where(authentication_token: token).first
    end
  end   
end

在我的控制器我创建以下文件夹结构: controllers-> API - > V1 的和我创建以下共享模块验证( authentication.rb

In my controllers I created following folder structure: controllers->api->v1 and I have created following shared module Authentication (authentication.rb)

module API
  module V1
    module Authentication
      extend ActiveSupport::Concern

      included do
        before do
           error!("401 Unauthorized", 401) unless authenticated?
         end

         helpers do
           def warden
             env['warden']
           end

           def authenticated?
             return true if warden.authenticated?
             params[:access_token] && @user = User.find_by(authentication_token: params[:access_token])
           end

           def current_user
             warden.user || @user
           end
         end
       end
     end
   end
end

所以每次当我想确保,我的资源将身份验证令牌被调用,我可以简单地调用补充一点:包括API :: V1 ::验证的葡萄资源:

module API
  module V1
    class Posts < Grape::API
      include API::V1::Defaults
      include API::V1::Authentication

现在我有另一个葡萄资源被称为用户(users.rb的)这里我实现了认证,注册和注销的方法。(我想我在这里混苹果梨,我就提取登录/注销过程到另一个葡萄资源 - 会话)

Now I have another Grape resource called Users(users.rb) and here I implement methods for authentication, registration and logout.(I think that I mix here apples with pears, and I should extract the login/logout process to another Grape resource - Session).

module API
  module V1
    class Users < Grape::API
      include API::V1::Defaults

      resources :users do
        desc "Authenticate user and return user object, access token"
        params do
           requires :email, :type => String, :desc => "User email"
           requires :password, :type => String, :desc => "User password"
         end
         post 'authenticate' do
           email = params[:email]
           password = params[:password]

           if email.nil? or password.nil?
             error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
             return
           end

           user = User.find_by(email: email.downcase)
           if user.nil?
              error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
              return
           end

           if !user.valid_password?(password)
              error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
              return
           else
             user.ensure_authentication_token!
             user.save
             status(201){status: 'ok', token: user.authentication_token }
           end
         end

         desc "Register user and return user object, access token"
         params do
            requires :first_name, :type => String, :desc => "First Name"
            requires :last_name, :type => String, :desc => "Last Name"
            requires :email, :type => String, :desc => "Email"
            requires :password, :type => String, :desc => "Password"
          end
          post 'register' do
            user = User.new(
              first_name: params[:first_name],
              last_name:  params[:last_name],
              password:   params[:password],
              email:      params[:email]
            )

            if user.valid?
              user.save
              return user
            else
              error!({:error_code => 404, :error_message => "Invalid email or password."}, 401)
            end
          end

          desc "Logout user and return user object, access token"
           params do
              requires :token, :type => String, :desc => "Authenticaiton Token"
            end
            delete 'logout' do

              user = User.find_by(authentication_token: params[:token])

              if !user.nil?
                user.remove_authentication_token!
                status(200)
                {
                  status: 'ok',
                  token: user.authentication_token
                }
              else
                error!({:error_code => 404, :error_message => "Invalid token."}, 401)
              end
            end
      end
    end
  end
end

我意识到我present这里每吨code,并且它可能没有什么意义,但是这是我目前有,我可以使用 authentication_token 反对我的API调用这是由模块保护验证

I realize that I present here a ton of code and it might not make sense, but this is what I currently have and I'm able to use the authentication_token for calls against my API which are protected by module Authentication.

我觉得这个解决方案并不好,但我真的在寻找更简单的方式如何实现通过API的用户认证。我有我下面列出几个问题。

I feel like this solution is not good, but I really looking for easier way how to achieve user authentication through APIs. I have several questions which I listed below.

问题


  1. 请您觉得这种实现是很危险的,如果是这样,为什么? - 我认为,这是因为一个令牌的使用情况。有没有一种方法如何改善这种情况?我也看到有单独的模型令牌有到期时间等实现,但我认为这几乎是像重新发明轮子,因为这个目的,我可以实现的OAuth2。我想有更轻的解决方案。

  2. 这是很好的做法,为验证创建新的模块,并将其包含到仅在需要的地方的资源?

  3. 请您了解关于这个主题的很好的教程 - 实施
    导轨+设计+葡萄?此外,你知道任何好的
    开源Rails项目,这是这种方式实现?

  4. 如何用不同的方法实现它哪个更安全?

  1. Do you think this kind of implementation is dangerous, if so, why? - I think that it is, because of the usage of one token. Is there a way how to improve this pattern? I've also seen implementation with separate model Token which has expiration time, etc. But I think this is almost like reinventing wheel, because for this purpose I can implement OAuth2. I would like to have lighter solution.
  2. It is good practice to create new module for Authentication and include it only into resources where it is needed?
  3. Do you know about any good tutorial on this topic - implementing Rails + Devise + Grape? Additionally, do you know about any good open-source Rails project, which is implemented this way?
  4. How can I implement it with different approach which is more safer?

我这么长的帖子道歉,但我希望更多的人有同样的问题,这可能会帮助我找到我的问题更多的答案。

I apologize for such a long post, but I hope that more people has the same problem and it might help me to find more answers on my questions.

推荐答案

添加token_authenticable制定模块(适用于色器件版本&LT; = 3.2)

Add token_authenticable to devise modules (works with devise versions <=3.2)

在user.rb加:token_authenticatable制定的模块列表,它应该是这个样子如下:

In user.rb add :token_authenticatable to the list of devise modules, it should look something like below:

class User < ActiveRecord::Base
# ..code..
  devise :database_authenticatable,
    :token_authenticatable,
    :invitable,
    :registerable,
    :recoverable,
    :rememberable,
    :trackable,
    :validatable

  attr_accessible :name, :email, :authentication_token

  before_save :ensure_authentication_token
# ..code..
end

在生成令牌验证自己(如果色器件版本> 3.2)

Generate Authentication token on your own (If devise version > 3.2)

class User < ActiveRecord::Base
# ..code..
  devise :database_authenticatable,
    :invitable,
    :registerable,
    :recoverable,
    :rememberable,
    :trackable,
    :validatable

  attr_accessible :name, :email, :authentication_token

  before_save :ensure_authentication_token

  def ensure_authentication_token
    self.authentication_token ||= generate_authentication_token
  end

  private

  def generate_authentication_token
    loop do
      token = Devise.friendly_token
      break token unless User.where(authentication_token: token).first
    end
  end

添加迁移authentiction令牌

Add migration for authentiction token

rails g migration add_auth_token_to_users
      invoke  active_record
      create    db/migrate/20141101204628_add_auth_token_to_users.rb

编辑迁移文件中加入:authentication_token列给用户

Edit migration file to add :authentication_token column to users

class AddAuthTokenToUsers < ActiveRecord::Migration
  def self.up
    change_table :users do |t|
      t.string :authentication_token
    end

    add_index  :users, :authentication_token, :unique => true
  end

  def self.down
    remove_column :users, :authentication_token
  end
end

运行迁移

耙分贝:迁移

生成现有用户令牌

我们需要调用保存的用户,以确保认证令牌每个实例是为每个用户present。

We need to call save on every instance of user that will ensure authentication token is present for each user.

User.all.each(安培;:保存)

使用安全葡萄API身份验证令牌

Secure Grape API using auth token

您需要低于code加入到API ::根在为了增加基于令牌的认证。如果你还没有意识API ::根的话,请用葡萄阅读构建RESTful API

You need to add below code to the API::Root in-order to add token based authentication. If you are unware of API::Root then please read Building RESTful API using Grape

在下面的例子中,我们根据两个方案认证用户 - 如果用户登录到Web应用程序,然后使用相同的会话 - 如果会议不提供身份验证令牌传递然后基于令牌

In below example, We are authenticating user based on two scenarios – If user is logged on to the web app then use the same session – If session is not available and auth token is passed then find user based on the token

# lib/api/root.rb
module API
  class Root < Grape::API
    prefix    'api'
    format    :json

    rescue_from :all, :backtrace => true
    error_formatter :json, API::ErrorFormatter

    before do
      error!("401 Unauthorized", 401) unless authenticated
    end

    helpers do
      def warden
        env['warden']
      end

      def authenticated
        return true if warden.authenticated?
        params[:access_token] && @user = User.find_by_authentication_token(params[:access_token])
      end

      def current_user
        warden.user || @user
      end
    end

    mount API::V1::Root
    mount API::V2::Root
  end
end

这篇关于用户认证与葡萄和设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆