1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】JWTを用いたUser周りのAPI実装

Last updated at Posted at 2024-11-03

Rails APIでUser周りの実装をしていきます。User周りの実装は基本的なアプリでは必須になってくると思うのでぜひ参考にしてください。
Docker環境上で開発しています。
のちに同じプロジェクト内で管理画面を開発することを考えたため、Rails APIでは開発していません。
駆け出しエンジニアですので間違っているところはコメントください。

Userテーブル

Userテーブルは以下の設計にしました

カラム名 データ型 制約 説明
id integer 主キー、オートインクリメント ユーザーの一意な識別子
name string null: false ユーザーの名前
email string null: false, 一意制約 ユーザーのメールアドレス
password_digest string null: false ハッシュ化されたパスワード
created_at datetime null: false レコード作成日時
updated_at datetime null: false レコード更新日時

ルーティング

今回の実装はユーザーの新規登録、ログイン、退会、ログアウトを実装していきます。

HTTPメソッド パス コントローラ#アクション 説明
POST /v1/signup users#create ユーザーの新規登録
POST /v1/login sessions#create ユーザーのログイン(JWT発行)
DELETE /v1/logout sessions#destroy ユーザーのログアウト
DELETE /v1/profile users#destroy ユーザーの退会
  1. ユーザーの新規登録では、リクエストボディにname、email、passwordを含め、成功すると、登録されたユーザーの情報が返されます
  2. ユーザーのログインでは、リクエストボディにemailとpasswordを含め、認証に成功すると、JWTトークンが発行され、今後のリクエストでの認証に使用されます
  3. ユーザーのロウアウトでは、現在のセッションを終了し、ユーザーをログアウトさせます。このエンドポイントにアクセスすることで、JWTトークンを無効化し、以後のリクエストに対して認証を行えなくします
  4. ユーザーの退会では、ユーザーアカウントを削除します。このエンドポイントを利用することで、ユーザーは自分のアカウントを退会することができます。成功すると、関連するユーザーデータも削除されます

以下がroutsファイルです

config/routes.rb
Rails.application.routes.draw do
  namespace :v1 do
    post '/signup', to: 'users#create'
    post '/login', to: 'sessions#create'
    delete '/logout', to: 'sessions#destroy'
    delete '/profile', to: 'users#destroy'
  end
end

期待するレスポンス

  • POST /v1/signup
json
{
    "message": "ユーザー登録が完了しました",
    "user": {
        "id": 1,
        "name": "bar",
        "email": "bar@example.com"
    }
}
  • POST /v1/login
json
{
    "message": "ログインに成功しました。",
    "token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo0fQ.KzYrVPsdU0i8n1g5qoQRtadIj8CCYTJSbgof5nf-5HM"
}
  • DELETE /v1/logout
json
{
  "message": "ログアウトしました。。"
}
  • DELETE /v1/profile
json
{
  "message": "アカウントが削除されました。"
}

Userテーブルの作成

マイグレーションの作成

docker-compose run web rails generate model User name:string email:string:uniq password_digest:string

このコマンドにより、Userテーブルのマイグレーションファイルが生成されます。name、email、password_digestのカラムが追加されます。

マイグレーションファイルの修正

生成されたマイグレーションファイルを開き、emailカラムにユニーク制約を明示的に追加して、以下のように修正します。

xxxx_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.string :email, null: false, unique: true
      t.string :password_digest, null: false

      t.timestamps
    end
    add_index :users, :email, unique: true
  end
end

マイグレーションの実行

docker-compose run web rails db:migrate

これでUserテーブルがデータベースに作成されます。
また、userモデルも作成されます

bcryptの有効化

  • Gemfileにbcryptを追加
    まず、プロジェクトのGemfileにbcryptを追加します。bcryptはパスワードのハッシュ化に必要です。
Gemfile
gem 'bcrypt', '~> 3.1.7'
  • bundle installを実行
docker-compose run web bundle install 
  • build
docker-compose build 
  • Userモデルにhas_secure_passwordを追加
user.rb
class User < ApplicationRecord
  has_secure_password

  validates :name, presence: true
  validates :email, presence: true, uniqueness: true
end

コントローラの作成

Apiコントローラ

APIモードでないRailsアプリケーションで、JSONリクエストを扱うためにapiコントローラでCSRFを防止します。apiで扱われるコントローラはapiコントローラを継承します。
また、ログイン中のユーザーをリクエストヘッダーから取得するためのcurrent_userメソッドを作成します。

docker-compose exec web rails generate controller api/v1/Api
users_controller.rb
module Api
  module V1
    class ApiController < ApplicationController
      protect_from_forgery with: :null_session
      def current_user
        token = request.headers['Authorization']&.split(' ')&.last
        return nil unless token
    
        begin
          decoded_token = JWT.decode(token, Rails.application.secrets.secret_key_base)[0]
          user_id = decoded_token['user_id']
          @current_user ||= User.find(user_id)
        rescue JWT::DecodeError
          nil
        end
      end
    end
  end
end

Usersコントローラー

ユーザーの新規登録、退会を管理するために、UsersControllerを作成します

docker-compose exec web rails generate controller api/v1/Users

コントローラーを編集します

users_controller.rb
module Api
  module V1
    class UsersController < ApiController
      # ユーザー登録
      def create
        user = User.new(user_params)
        if user.save
          render json: { message: "ユーザー登録が完了しました", user: {id: user.id, name: user.name, email: user.email} }, status: :created
        else
          render json: { errors: user.errors.full_messages }, status: :unprocessable_entity
        end
      end

      # プロフィール削除(退会処理)
      def destroy
        user = current_user
        if user && user.destroy
          render json: { message: "アカウントが削除されました。" }, status: :ok
        else
          render json: { error: "アカウントの削除に失敗しました" }, status: :unprocessable_entity
        end
      end

      private

      # ユーザー登録時のパラメータ
      def user_params
        params.require(:user).permit(:name, :email, :password, :password_confirmation)
      end
      wrap_parameters :user, include: [:name, :email, :password, :password_confirmation]
    end
  end
end

Sessionsコントローラー

ユーザーのリグイン、ログアウトを管理するために、SessionsControllerを作成します

docker-compose exec web rails generate controller api/v1/Sessions

コントローラーを編集します

sessions_controller.rb
module Api
  module V1
    class SessionsController < ApiController
      # ログイン
      def create
        user = User.find_by(email: params[:email])

        if user&.authenticate(params[:password])
          token = encode_token(user_id: user.id)
          render json: { message: "ログインに成功しました。", token: token }, status: :ok
        else
          render json: { error: "メールアドレスまたはパスワードが無効です。" }, status: :unauthorized
        end
      end

      # ログアウト
      def destroy
        render json: { message: "ログアウトしました。" }, status: :ok
      end

      private

      # JWTトークンをエンコード
      def encode_token(payload)
        JWT.encode(payload, Rails.application.secrets.secret_key_base)
      end
    end
  end
end

JWTの有効化

  • Gemfileにjwtを追加
    まず、プロジェクトのGemfileにjwtを追加します。
Gemfile
gem 'jwt'
  • bundle installを実行
docker-compose run web bundle install 
  • build
docker-compose build 

Postmanを使ってリクエスト

Postmanを使ってそれぞれリクエストを送ってみます。

新規登録

リクエストメソッドをPOSTに設定しURLを以下に設定する

http://localhost:3000/v1/signout

リクエストボディを以下のように設定して

json
{
  "name": "bar",
  "email": "bar@example.com",
  "password": "password123"
}

リクエストを送ると以下のレスポンスが返ってきます。

json
{
    "message": "ユーザー登録が完了しました",
    "user": {
        "id": 1,
        "name": "bar",
        "email": "bar@example.com"
    }
}

ログイン

リクエストメソッドをPOSTに設定しURLを以下に設定する

http://localhost:3000/v1/login

リクエストボディを以下のように設定して

json
{
  "email": "bar@example.com",
  "password": "password123"
}

リクエストを送ると以下のレスポンスが返ってきます。
tokenはJWTでidをエンコードしたものです。

json
{
    "message": "ログインに成功しました。",
    "token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo1fQ.7wAvPaPWV1iQxks7yQhrFM5wJO6fDoiRvVbTHU7nxP0"
}

ログアウト

リクエストメソッドをDELETに設定しURLを以下に設定する

http://localhost:3000/v1/logout

リクエストボディは設定せずにリクエストを送ると以下のレスポンスが返ってきます。

json
{
    "message": "ログアウトしました。"
}

ログアウトではフロント側でsessionnを削除する操作を行います。

退会

リクエストメソッドをDELETに設定しURLを以下に設定する

http://localhost:3000/v1/profile

リクエストヘッダにkayをAuthorization、valueをログインした時にトークンとして返ってきた値をセットします。
リクエストボディは設定せずに
リクエストを送ると以下のレスポンスが返ってきます。
tokenはJWTでidをエンコードしたものです。

json
{
    "message": "アカウントが削除されました。"
}

まとめ

今回はRails APIでUser周りの実装をしました。ぜひ参考にしていただけたらと思います。
プログラムで気になったことを以下の記事にまとめたのでぜひご覧ください。
【Rails】Rails APIで新規登録、退会処理
【Rails】Rails APIでログイン、ログアウト

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?