前回、Rails APIでUser周りの実装をしました。
【Rails】JWTを用いたUser周りのAPI実装
その記事の新規登録、退会処理で気になったことをまとめます。
先にmodel、controllerを貼り付けておきます。
class User < ApplicationRecord
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: true
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
end
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
has_secure_password
RailsのActiveRecordモデルで、ユーザーのパスワードを安全に管理するためのメソッドです。このメソッドを使用すると、パスワードのハッシュ化、パスワード確認、認証機能などが自動的に提供され、セキュリティ強化が簡単に行えます。
has_secure_passwordの前提条件
- bcrypt Gemが必要
bcryptというGemが必要です。このGemは、パスワードのハッシュ化と比較を行うために使用されます。Gemfileに以下を追加し、bundle installでインストールします。
gem 'bcrypt', '~> 3.1.7'
- password_digestカラムが必要
モデルのデータベーステーブルにpassword_digestという名前のカラムを必要とします。このカラムにハッシュ化されたパスワードが保存されます。
主な機能と動作
has_secure_passwordをモデルに追加するだけで、次の機能が自動的に利用できるようになります。
- パスワードのハッシュ化
- ユーザーがパスワードを設定(password)すると、bcryptによってハッシュ化され、password_digestカラムに保存されます
- ハッシュ化されるため、実際のパスワード文字列はデータベースに保存されません。データベースに保存されるのはハッシュ化された文字列のみです
- passwordとpassword_confirmation属性
- passwordとpassword_confirmationという仮想属性がモデルに追加されます
- ユーザーが新しいパスワードを設定するときに、password_confirmationに同じパスワードを入力することで、パスワードの一致を確認できます
- passwordとpassword_confirmationが一致しない場合、バリデーションエラーが発生します
- 認証機能
- authenticateメソッドが自動的に提供されます。このメソッドにパスワードを渡して、ユーザーの認証を行います
- authenticateは、渡されたパスワードをハッシュ化し、password_digestと比較します。一致すればユーザーオブジェクトを返し、不一致ならfalseを返します。
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
# 認証成功
else
# 認証失敗
end
user_params
RailsのStrong Parametersという仕組みを使って、ユーザーからのリクエストパラメータを安全にフィルタリングするためのメソッドです。
ユーザーが送信するデータ(params)のうち、許可された属性のみを受け取り、他の属性を除外することで、不正なデータがモデルに渡されないようにします。
params.require(:user)
paramsは、コントローラが受け取るリクエストのデータが含まれるオブジェクトです。たとえば、フォーム送信やAPIリクエストから送られた情報はすべてparamsで受け取ります。
- require(:user)は、paramsオブジェクトから:userというキーを必須項目として要求します。この例では、リクエストデータがuserというキーの下に存在することが前提になっています
- require(:user)がない場合、:userキーが存在しないときにエラーを発生させることができず、モデルに想定外のデータが渡ってしまう可能性があります
permit(:name, :email, :password, :password_confirmation)
permitは、paramsの中で許可したいキー(属性)を指定します。この例では、:name、:email、:password、:password_confirmationのみを許可しています。
- 許可された属性のみが最終的にuser_paramsメソッドから返され、User.newメソッドに渡されます
- 許可されていない属性は自動的に削除されるため、予期しないパラメータの混入や、悪意のあるデータの挿入を防ぐことができます
wrap_parameters
Railsのコントローラにおいてリクエストパラメータのラップ(ラッピング)を行うためのメソッドです。これにより、JSONリクエストのデータが適切な形で扱いやすくなります。
背景と目的
JSON形式でデータを送信するとき、リクエストボディが特定のキーにラップされていることを期待することがあります。wrap_parametersを使うことで、外部APIやクライアントから受け取るデータの形式に柔軟に対応できます。
クライアントから次のようなJSONデータが送信されるとします:
{
"name": "John Doe",
"email": "john@example.com",
"password": "secret123"
}
Railsのparamsはデフォルトでデータがモデル名にラップされることを期待するため、次のようにラップされていることが理想です:
{
"user": {
"name": "John Doe",
"email": "john@example.com",
"password": "secret123"
}
}
wrap_parametersを使用すると、クライアントがこのラップされた形式で送信しなくても、Rails側で自動的にラップして処理できるようになります。
使用方法
wrap_parametersは、コントローラ内部やアプリケーション全体の設定で利用できます。コントローラ内で使用する場合は以下のように書きます:
class Api::V1::UsersController < ApplicationController
wrap_parameters :user, include: [:name, :email, :password, :password_confirmation]
end
- wrap_parameters :user
:userの部分は、どのキーでラップするかを指定します。この場合、ラップ後のデータがparams[:user]として扱えるようになります。 - include: [:name, :email, :password, :password_confirmation]
includeオプションで、ラップする際に含める属性を指定します。ここで指定した属性が含まれるJSONデータのみがラップされ、params[:user][:name]といったアクセスが可能になります。