使用Devise设置API进行身份验证

Ale*_*eña 6 api ruby-on-rails user-registration devise

我正在尝试为我的rails应用程序设置用户注册端点,以便我可以在iOS再现中访问应用程序功能.我已经开始将命名空间命名为API,到目前为止已经设法使用Devise和JWT进行用户身份验证.

这很棒,但我还需要能够通过API注册用户.坦率地说,我不知道如何正确实现这一点.几个GOogle文章要么提出过时的文章,使用已弃用的令牌可验证,要么从未得到过回答.

以下是我认为与此问题最相关的代码:

routes.rb(API的Namespaced部分)

namespace :api do
   namespace :v1 do
      devise_for :users, controllers: { registrations: 'api/v1/registrations' }
         resources :classrooms
         resources :notifications
      end
   end
end
Run Code Online (Sandbox Code Playgroud)

registrations_controller.rb(API contorller)

class Api::V1::RegistrationsController < Devise::RegistrationsController
  respond_to :json

  def create
    if params[:email].nil?
      render :status => 400,
      :json => {:message => 'User request must contain the user email.'}
      return
    elsif params[:password].nil?
      render :status => 400,
      :json => {:message => 'User request must contain the user password.'}
      return
    end

    if params[:email]
      duplicate_user = User.find_by_email(params[:email])
      unless duplicate_user.nil?
        render :status => 409,
        :json => {:message => 'Duplicate email. A user already exists with that email address.'}
        return
      end
    end

    @user = User.create(user_params)

    if @user.save!
      render :json => {:user => @user}
    else
      render :status => 400,
      :json => {:message => @user.errors.full_messages}
    end
  end

  private

  # Never trust parameters from the scary internet, only allow the white list through.
  def user_params
    devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute, :first_name, :last_name, :access_code])
  end
end
Run Code Online (Sandbox Code Playgroud)

注册终点

http://localhost:3000/api/v1/users

样邮递员回复

{
  "message": [
    "Email can't be blank",
    "Password can't be blank",
    "Access code is invalid [Beta]."
  ]
}
Run Code Online (Sandbox Code Playgroud)

我将非常感激任何帮助,因为我渴望学习更多(并让它发挥作用!).

更新1

这是我在发出生成用户的帖子请求后在服务器上得到的内容......

Started POST "/api/v1/users" for 127.0.0.1 at 2017-02-22 09:22:11 -0800
Processing by Api::V1::RegistrationsController#create as */*
  Parameters: {"user"=>{"email"=>"user@sampleapi.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "access_code"=>"uiux"}}
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."email" IS NULL LIMIT $1  [["LIMIT", 1]]
Completed 400 Bad Request in 2ms (Views: 0.2ms | ActiveRecord: 0.4ms)
Run Code Online (Sandbox Code Playgroud)

更新了Registrations_controller

class Api::V1::RegistrationsController < Devise::RegistrationsController
  before_action :configure_sign_up_params, only: [:create]
  respond_to :json

  def create
    @user = build_resource(sign_up_params)

    if @user.persisted?

      # We know that the user has been persisted to the database, so now we can create our empty profile

      if resource.active_for_authentication?
        sign_up(:user, @user)
        render :json => {:user => @user}
      else
        expire_data_after_sign_in!
        render :json => {:message => 'signed_up_but_#{@user.inactive_message}'}
      end
    else
      if params[:user][:email].nil?
        render :status => 400,
        :json => {:message => 'User request must contain the user email.'}
        return
      elsif params[:user][:password].nil?
        render :status => 400,
        :json => {:message => 'User request must contain the user password.'}
        return
      end

      if params[:user][:email]
        duplicate_user = User.find_by_email(params[:email])
        unless duplicate_user.nil?
          render :status => 409,
          :json => {:message => 'Duplicate email. A user already exists with that email address.'}
          return
        end
      end

      render :status => 400,
      :json => {:message => resource.errors.full_messages}
    end
  end

  protected

  # If you have extra params to permit, append them to the sanitizer.
  def configure_sign_up_params
    devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute, :first_name, :last_name, :access_code])
  end
end
Run Code Online (Sandbox Code Playgroud)

我很确定我此时的主要问题是我的参数的格式,因此任何正确方向的推动都会很棒.我确实找到了这篇文章,但我发现它有点难以理解它们的API工作原理......

小智 6

这里有 2 种解决方案,请选择您喜欢的一种。

  1. 覆盖 devise_parameter_sanitizer:

    class ApplicationController < ActionController::Base
      protected
    
      def devise_parameter_sanitizer
        if resource_class == User
          User::ParameterSanitizer.new(User, :user, params)
        else
          super # Use the default one
        end
      end
    end
    
    Run Code Online (Sandbox Code Playgroud)
  2. 覆盖 sign_up_params:

    def sign_up_params
      params.require(:user).permit(:email, :password, :password_confirmation)
    end
    
    Run Code Online (Sandbox Code Playgroud)

为什么?

如果您深入研究Devise ParameterSanitizer,则 resource_name 将是:api_v1_user,而不仅仅是:user因为您的路线:

namespace :api do
   namespace :v1 do
      devise_for :users, controllers: { registrations: 'api/v1/registrations' }
   end
end
Run Code Online (Sandbox Code Playgroud)

错误资源名称将导致sign_up_params始终返回空哈希{}


Gra*_*ick 0

为什么不使用simple_token_authentication gem ?

设置极其简单:

# Gemfile
gem "simple_token_authentication"

bundle install
rails g migration AddTokenToUsers "authentication_token:string{30}:uniq"
rails db:migrate

# app/models/user.rb
class User < ApplicationRecord
  acts_as_token_authenticatable
  # [...]
end
Run Code Online (Sandbox Code Playgroud)

在您的路线中:

# config/routes.rb
Rails.application.routes.draw do
  # [...]
  namespace :api, defaults: { format: :json } do
    namespace :v1 do
      resources :classrooms
      resources :notifications
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

在你的控制器中:

# app/controllers/api/v1/classrooms_controller.rb
class Api::V1::ClassroomsController < Api::V1::BaseController
  acts_as_token_authentication_handler_for User
  # [...]

end
Run Code Online (Sandbox Code Playgroud)

使用 RestClient gem 的示例调用:

url = "http://localhost:3000/api/v1/classrooms/"
params = {user_email: 'john@doe.com', user_token: '5yx-APbH2cmb11p69UiV'}

request = RestClient.get url, :params => params
Run Code Online (Sandbox Code Playgroud)

对于没有令牌的现有用户:

user = User.find_by_email("john@doe.com")
user.save
user.reload.authentication_token
Run Code Online (Sandbox Code Playgroud)