【Rails】GraphQL + Devise认证

首先

我经常看到使用devise_token_auth作为Rails API的身份认证方法,但很少见到直接使用现有的devise实现的方法,所以我写了这篇文章。

前提 tí)

    • すでに運用開始している Railsアプリケーションに追加で実装する(APIモードではない)

devise を使用する(devise_token_auth は使用しない)
既存の User モデルに適用する
GraphQL を実装する
フロント側の実装は割愛

组成

我將認證流程整理在圖表中。

image.png

关于第③项,由于是前端实现,所以在本文中省略介绍。

实施流程

    • Rails アプリケーション実装

 

    • GraphQL 実装

 

    Devise 実装

Rails 应用程序的实现

新建一个Rails应用程序,并创建一个任务模型。

$ rails new graphql_devise_sample
$ rails g model Task body:string
$ rails db:create
$ rails db:migrate

GraphQL的实现

有关GraphQL的实现方法的详细说明,请参阅以下内容。(本文中提到了命令,但详细说明被省略了。)

    • 初めてのGraphQL with Rails

https://qiita.com/sazumy/items/58106174392516da5f1d

在 Gemfile 文件中添加 GraphQL 的 gem。

gem 'graphql'

group :development do
  gem 'graphiql-rails'
end

执行bundle install命令,安装GraphQL,并应用于Task模型。

$ bundle install
$ rails g graphql:install
$ rails g graphql:object Task

添加查询。

module Types
  class QueryType < Types::BaseObject
    # Add `node(id: ID!) and `nodes(ids: [ID!]!)`
    include GraphQL::Types::Relay::HasNodeField
    include GraphQL::Types::Relay::HasNodesField

    field :task, Types::TaskType, null: true do
      description "Find Task by ID"
      argument :id, ID, required: true
    end
    def task(id:)
      Task.find(id)
    end

    field :all_tasks, Types::TaskType.connection_type, null: true do
      description 'All Tasks'
    end
    def all_tasks
      Task.all
    end
  end
end

如果可以做到这一步,您可以访问localhost:3000/graphiql,并确认能够执行查询。

{
  todayTasks {
    edges{
      node{
        id
        body
        createdAt
      }
    }
  }
}

设计实施

请参阅以下内容以了解Devise的具体实现方法。(本文中也提供了相应的命令,但没有详细解释。)

    • 【Rails】deviseを導入してみる

https://qiita.com/Hal_mai/items/350c400e8763ce0487a3

首先,在Gemfile中添加devise gem。

gem 'devise'

执行bundle install命令,安装Devise并将其应用于User模型。

$ bundle install
$ rails g devise:install
$ rails g devise user
$ rails g migration add_access_token_to_user

在用户模型中添加access_token。

class AddAccessTokenToUser < ActiveRecord::Migration
  def change
    add_column :users, :access_token, :string
  end
end
$ rails db:migrate

当用户创建时,将创建访问令牌。

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  after_create :update_access_token!

  def update_access_token!
    self.access_token = "#{self.id}:#{Devise.friendly_token}"
    save
  end
end

我們將在 application_controller.rb 中添加一個方法,以確認在 API 執行時,access_token 在認證時一起傳遞過來。

class ApplicationController < ActionController::Base
  protect_from_forgery with: :null_session

  def authenticate_user_from_token!
    auth_token = request.headers['Authorization']

    if auth_token
      authenticate_with_auth_token(auth_token)
    else
      authenticate_error
    end
  end

  private

  def authenticate_with_auth_token(auth_token)
    unless auth_token.include?(':')
      authenticate_error
      return
    end

    _, token = auth_token.split(' ')
    user_id = token.split(':').first
    user = User.where(id: user_id).first

    if user && Devise.secure_compare(user.access_token, token)
      # User can access
      sign_in user, store: false
    else
      authenticate_error
    end
  end

  ##
  # Authentication Failure
  # Renders a 401 error
  def authenticate_error
    render json: { error: t('devise.failure.unauthenticated') }, status: 401
  end
end

我会添加路由。

Rails.application.routes.draw do
  # 以下を追加
  namespace :api do
    resource :users, only: [:create]
    resource :login, only: [:create], controller: :sessions
  end
end

在这里,为了创建access_token获取API和用户注册API,我们分别创建两个文件。

app/controllers/api/sessions_controller.rb

ログイン(access_token取得)(構成図の①)

app/controllers/api/users_controller.rb

サインアップ(ユーザー登録)

module Api
  class SessionsController < ApplicationController
    def create
      @user = User.find_for_database_authentication(email: params[:email])
      return invalid_email unless @user

      if @user.valid_password?(params[:password])
        sign_in :user, @user
        render json: @user, root: nil
      else
        invalid_password
      end
    end

    private

    def invalid_email
      warden.custom_failure!
      render json: { error: 'invalid_email' }
    end

    def invalid_password
      warden.custom_failure!
      render json: { error: 'invalid_password' }
    end
  end
end
module Api
  class UsersController < ApplicationController
    def index
      @users = User.all
    end

    def create
      @user = User.new(user_params)
      if @user.save!
        render json: @user
      else
        render json: { error: 'user_create_error' }, status: :unprocessable_entity
      end
    end

    private

    def user_params
      params.require(:user).permit(:email, :password)
    end
  end
end

以上是实施部分。接下来要确认执行结果。

执行结果

我們將確認以下4點是否在實施中正確執行。

    • ユーザー登録API

access_token取得API

access_token を持っていないと、GraphQL の実行結果が取得できないこと

access_token を持っていると、GraphQL の実行結果が取得できないこと

首先,在开发环境中使用rails s命令启动服务器。然后,请使用WebAPI开发工具,例如POSTMAN,执行以下操作。

用户注册API

通过向以下URL发送POST请求来确认可以进行用户注册。

    • URL

localhost:3000/api/users?user[email]=test@example.com&user[password]=aaaaaa

获得API的访问令牌。

通过执行POST请求到以下URL,可以确认可以获取access_token。

    • URL

localhost:3000/login?email=test@example.com&password=aaaaaa

如果没有access_token,无法获取GraphQL的执行结果。

通过指定以下的URL、Authorization请求头部和正文内容,进行POST请求来确认无法获取执行结果。

    • URL

localhost:3000/graphql

Body

GraphQL
Query

以下のクエリを記載すること

{
  todayTasks {
    edges{
      node{
        id
        body
        createdAt
      }
    }
  }
}

如果拥有 access_token,则无法获取 GraphQL 的执行结果。

通过指定以下URL、Authorization请求头和Body,执行POST操作以确认获取执行结果。

    • URL

localhost:3000/graphql

Authorization リクエストヘッダー

Type

Bearer Token

Token

1:fkib5vzMa1YjqyMnbMUo(access_token取得API実行時に取得したaccess_tokenを記載すること)

Body

GraphQL
Query

以下のクエリを記載すること

{
  todayTasks {
    edges{
      node{
        id
        body
        createdAt
      }
    }
  }
}

GraphQL + Devise的实施和确认处理到此为止。

总结

我写了关于使用原始的方法来实现devise作为Rails API的身份验证方法的实现方法。虽然篇幅有点长,但如果能对某人有所帮助,我会很高兴。

顺便说一下,最近我终于开始使用GitHub Copilot了,它大大提高了我的生产效率,我很喜欢。

请参考

    • 初めてのGraphQL with Rails

https://qiita.com/sazumy/items/58106174392516da5f1d

【Rails】deviseを導入してみる

https://qiita.com/Hal_mai/items/350c400e8763ce0487a3

Rails5 API + devise でユーザーの認証と作成機能を実装した API を作成する

GitHub (skedesu / graphql_devise_sample )

https://github.com/skedesu/graphql_devise_sample

广告
将在 10 秒后关闭
bannerAds