【Rails】アクセス制限
アクセス制限の目的
パスやルーティングが見えなくとも、URLを予測し、別のユーザー情報にアクセスすることができます。
データを保護し、不正なアクセスから守るためにもアクセス制限は必要です。
データを表示させない仕組み
別ユーザーからアクセスされないよう、以下のフローのように制限します。
現在ログインしているユーザーかどうかの判断は、current_user
とparams[:id]
いうメソッドを使用します。それぞれについて解説します。
current_userとは
current_user
メソッドはDeviseのヘルパーメソッドです。簡単に言えば、このメソッドを使えば、現在ログインしているユーザーの情報を取得することができます。
以下の記事でも解説していますので参考にしてもらえたら嬉しいです。
細かい仕組みは次の通りです。
current_userメソッドの仕組み
-
セッションと認証
Webアプリケーションでは、ユーザーがログインするとセッション(ユーザー情報を一時的に保存する仕組み)が開始され、ユーザーの情報がサーバー側に保存されます。このセッションを通じて、アプリケーションはユーザーが誰であるかを識別し、適切なアクセス制御やパーソナライズされた機能を提供することができます。 -
セッションとユーザー情報の関連付け
ユーザーがログインするとき、一般的なフローは次の通りです。
- ユーザーが認証情報(ユーザー名とパスワードなど)を入力してログインフォームを送信。
- サーバー側では、入力された認証情報を検証し、正しい場合はセッションにユーザー情報を格納。
- サーバーは、セッションIDをブラウザにクッキー(ウェブサイトがユーザーのブラウザに保存する情報のひとつ)として送信。これにより、ブラウザとサーバーの間でセッションが維持される。
- 以降のリクエストでは、ブラウザから送信されるセッションIDを使用して、サーバーはセッションを特定し、関連するユーザー情報を取得。
-
current_userの定義と利用
current_user
は、セッションを通じて特定のユーザーを識別するための便利な方法です。Railsでは、セッションからユーザー情報を取り出し、current_user
に格納することで、アプリケーション内のどこからでも現在のユーザーを簡単にアクセスできるようになります。
余談:セッションとクッキーとは
"セッション"と"クッキー"について、自分の理解のために例を交えて説明します。
セッション
例えるならば、ホテルのルームキー(カードキー)のようなもの。ホテルに滞在する際、部屋に入るためのルームキーが渡される。このルームキーは一時的なものであり、滞在期間が終わると無効になる。セッションも同様で、ウェブサイトとのやり取りにおいて一時的な情報や状態を管理するための仕組み。セッションはサーバー側によって発行され、ブラウザ(クライアント)に個別の識別情報が与えられる。
クッキー
例えるならば、ポイントカードのようなもの。ポイントカードは常に持ち歩き、個人の情報や特典の履歴などが保存されている。ポイントカードを提示することで、特典を受けたりポイントを貯めたりすることができる。同様に、クッキーはブラウザに保存される小さなファイルであり、ユーザーの個人情報やウェブサイトの訪問履歴などが保存される。ウェブサイトはクッキーを使用してユーザーを識別し、特定の設定やパーソナライズされた情報を提供することができる。
params[:id]とは
例えば、Webブラウザでユーザーのプロフィールページにアクセスすると、そのページのURLは/users/1
のようになります。ここで、:id
には1
という値が入ります。params[:id]
は、URL内の:id
部分の値を取得するためのコードです。これにより、アプリケーションは特定のユーザーを識別し、関連するデータを取得したり、必要な処理を行ったりすることができます。
具体的には、コントローラーでparams[:id]
を使用することで、その値を利用して特定のユーザーのプロフィール情報を表示したり、編集したりすることができます。データベースのテーブル内のレコードを識別するために、ユーザーIDなどが一般的に使用されます。
簡単に言えば、params[:id]
はURL内の特定の値を取得するためのコードであり、その値に基づいてアプリケーションが必要な処理を行います。
実装
実装の流れは次の通りです。
- URLに含まれるユーザーidを
params[:id]
で取得。 - ログインしているユーザーのidを
current_user.id
で取得。 - 1と2のidが一致していなかった場合、 投稿一覧にリダイレクトする。
例としてユーザー情報の編集画面と更新処理に対して実装をします。
class UsersController < ApplicationController
:
:
def edit
+ user = User.find(params[:id])
+ unless user.id == current_user.id
+ redirect_to user_path(current_user)
+ end
@user = User.find(params[:id])
end
def update
+ user = User.find(params[:id])
+ unless user.id == current_user.id
+ redirect_to user_path(current_user)
+ end
@user = User.find(params[:id])
if @user.update(user_params)
flash[:notice] = "You have updated user successfully."
redirect_to user_path(current_user)
else
render :edit
end
end
:
:
メソッドで処理をまとめる
editとupdateに追加したコードは全く同じです。同じ処理を複数のアクションで実行する場合は、コードの重複を避けるために、メソッドとしてまとめることができます。これにより、コードの量を削減し、読みやすいコードにすることができます。
具体的には、ensure_correct_user
というメソッドを作成します。このメソッドは、controller内でのみ使用するため、private
ブロック内に記述します。
private
def user_params
params.require(:user).permit(:name, :introduction, :profile_image)
end
+ def ensure_correct_user
+ user = User.find(params[:id])
+ unless user.id == current_user.id
+ redirect_to user_path(current_user)
+ end
end
上記のようにメソッドを定義しました。その後、editとupdateアクション内でこのメソッドを呼び出すことができます。
def edit
+ ensure_correct_user
@user = User.find(params[:id])
end
def update
+ ensure_correct_user
@user = User.find(params[:id])
# その他の処理
end
このようにすることで、重複するコードを削減し、コードの保守性と可読性を向上させることができます。メソッドを利用することで、同じ処理を複数の場所で使い回すことができます。
before_actionについて
メソッドとして処理をまとめとことにより、before_action
を使用できます。このメソッドはコントローラー内のアクション(メソッド)の実行前に別のメソッドを実行するためのフィルターです。これにより、共通の処理を実行したり、アクションの実行条件をチェックしたりすることができます。
berore_actionの記述方法
基本的な構文は以下の通りです。
class UsersController < ApplicationController
before_action :method_name, options
# アクションの定義とその他のコード
end
before_action
は、:method_name
の部分に実行したいメソッドの名前を指定します。また、:options
にはオプションを指定することができます。
一般的なオプションとしては、:only
と:except
がよく使われます。
-
:only
onlyを使用すると、指定したアクションの前でのみフィルターが実行されます。複数のアクションを指定する場合は配列で指定します。
before_action :method_name, only: [:action1, :action2]
-
:except
exceptを使用すると、指定したアクション以外のアクションの前でフィルターが実行されます。同じく複数のアクションを指定する場合は配列で指定します。
before_action :method_name, except: [:action1, :action2]
複数のオプションを同時に指定することもできます。
before_action :method_name, only: [:action1], except: [:action2]
before_actionの実装
具体的には、以下のようにbefore_action
を使用してensure_correct_user
メソッドを呼び出します。
class UsersController < ApplicationController
+ before_action :ensure_correct_user, only: [:update, :edit]
# 他のコード
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
# その他の処理
end
private
def user_params
params.require(:user).permit(:name, :introduction, :profile_image)
end
def ensure_correct_user
user = User.find(params[:id])
unless user.id == current_user.id
redirect_to user_path(current_user)
end
end
上記のようにbefore_action
を使用することで、"edit"と"update"アクションの前にensure_correct_user
メソッドが呼び出されます。これにより、重複したコードを避けつつ、必要な処理を実行することができます。
before_action
の:only
オプションを使用することで、どのアクションの前でフィルターを実行するかを指定します。この例では、[:update, :edit]
と指定することで、"update"と"edit"アクションの前でensure_correct_user
メソッドが実行されます。
これにより、コードの重複を避けつつ、DRY原則(Don't Repeat Yourself)に従ったコードになります。
今日は息子が熱を出したため、保育園を休み、1日ずっと一緒に過ごしました。
なので投稿が日を超えるぎりぎりの時間になりました。
子どもがいるといつ体調を崩してしまうか本当にわからないですね💦
5月には胃腸炎で1週間休ませなければならなくなり、今月はRSウイルスとコロナでまた1週間休ませなければならなかったので、最近は心配が多かったです。
ただ、今日は息子と一日中一緒に過ごせたので、本当に楽しかったし幸せでした。昼以降は元気になってきたので、少しお散歩したり、絵本を読んだり、一緒にいないいないばあして遊んだり♪
何が起こるかわからないので、勉強できる時には集中して頑張ろうと思います。
子育て中は予期せぬことが起こることが多いですが、大切なのはその時々を大切に過ごすことだと思いました。
息子との時間を楽しみ、勉強の時間も充実させていけるようにしていきます!
Discussion