はじめに
ユーザーログイン・ログアウトとアクセス制限!
難しそう!
記事はいつも通り箇条書き形式です!
気になった見出しがあればお読みください
メモず
表示するエラーメッセージの種類
- フォームのバリデーションエラー:
- 使用方法:
.errors.full_messageを使う。- 説明: Active Recordモデルのバリデーションに失敗した際、自動的に生成されるエラーメッセージ。これらは通常、フォームのビュー内で、個々のフォームフィールドに近い位置で表示されます。
- サーバーエラー:
- 使用方法: インスタンス変数
@error_messageを使うか、エラーハンドリングメカニズムを使う。- 説明: 例外が発生した場合、
rescue_fromなどを使用して特定のエラーをキャッチし、適切なメッセージをインスタンス変数@error_messageなどに格納してビューで表示します。または、500エラーページをカスタマイズすることもできます。
- 権限に関するエラー:
- 使用方法: インスタンス変数を使うか、特定のビューを用意する。
- 説明: 権限がないと判断された場合、インスタンス変数にエラーメッセージを格納し、それをビューで表示します。または、専用の「アクセス拒否」ビューを用意することもできます。
- ページが見つからないエラー(404エラー):
- 使用方法: publicディレクトリに専用ビューを用意する。
- 説明: Railsは
public/404.htmlのような静的ファイルを使用して404エラーを処理します。アプリケーションのルックアンドフィールに合わせてカスタマイズすることができます。
- リクエストのフォーマットエラー:
- 使用方法: インスタンス変数を使うか、APIの場合はJSONメッセージを返す。
- 説明: 通常のビューで表示する場合は、インスタンス変数にエラーメッセージを格納して表示します。APIの場合は、JSON形式でエラー詳細を含むレスポンスを返すのが一般的です。
インスタンス変数が適している部分は、flashも適してそう。
ただ、2つ問題がある。
flashと@error_messageの使い分け (flash.now)
-
flashは通常、redirectを伴う場合によく使われる。
renderの場合(新しいHTTPリクエストが発生しない場合)は**flash.nowもしくはインスタンス変数@error_messageが適している。** -
統一はしたい。Progateでは、
-
入力formのエラーメッセージはそのビューのみに表示 -
flash: ページリダイレクトの時のステータスは全ビューの共通部分で表示
と、違うCSSを適用し、別物として扱っている。
どっちも同じでいいや!って思えば「flashandflash.now×全ビューの共通部分」ですべて表示してもいい。 -
flash.nowとは?
flashはリダイレクト後のビューでメッセージを表示する。
それに対し、flash.nowは現在のビューで直接メッセージが表示される。次のHTTPリクエストには引き継がれない
特殊な変数:セッションsession[:key] = value
まず、「セッション」という概念が理解しきれてなかったので、そこから考えてみる。
英単語「session」と、ITにおける「セッション」
「Session」という英単語は、一般的に「会合」「集まり」「セッション」という意味を持ちます。この言葉は、特定の活動や作業、議論、または学習のために集まる特定の時間帯や期間を指します。例えば、学会や会議での「セッション」は、特定のテーマに焦点を当てた討論やプレゼンテーションの期間を指します。
IT業界における「セッション」という用語は、上記の基本的な意味から派生して、特定の時間や期間にわたる連続的な対話または交流のシーケンスを指します。特に、コンピュータネットワークやWebアプリケーションの文脈でよく使われます。ここでの「セッション」は、以下のような特徴を持ちます:
- ユーザーとサーバー間の対話:
- Webアプリケーションにおいて、セッションはユーザーがサーバーと交流を始めてから終えるまでの期間を指します。これにより、ユーザーがサイトにアクセスしている間、その状態や行動を追跡します。
- 状態の管理:
- インターネットは本質的に「ステートレス」で、各リクエストは独立しています。セッションは、これらの個別のリクエスト間でユーザーの状態やデータを維持する方法を提供します(例:ログイン状態の保持)。
- セキュリティと識別:
- セッションを通じて、サーバーは各ユーザーを識別し、そのセッションに対して適切なデータやアクセス権を提供します。セッションIDは、この識別に使われる重要な要素です。
- 時間制限:
- セッションには通常、一定の時間制限があります。ユーザーが一定期間アクティブでない場合、セッションは終了(タイムアウト)することが一般的です。
なるほど?「ステートレス」な中で状態・データを維持するのがセッション、しっくりきたな。時間制限があるのも「セッション」て感じだ。
そういえば「セッションタイムアウトしました」って言われることあるもんな。これか。
セッションIDはなんだ?user.idか?
セッションIDとは:セッションごとのランダムなID
- セッションごとに作成される
- ランダムで予測が困難
- クライアントサイドはセッションIDのみ保存し、リクエストに使用する。
- サーバーサイドはセッションIDを照合し、該当するデータにアクセスする。
- 基本的に
user.idなどユーザーデータと直接的な関係はないものと考えてよい。
しかし、一部、どのユーザーに関連するかの情報(user.idなど)が含まれていることはある。
user.idではないっぽい。セッションIDのメリットは?
セッションIDを使うメリット:ユーザーIDとの比較
セッションIDを使用してセッションを管理するメリットを、ユーザーIDを使用する場合と比較して考える。
セキュリティ
- ユーザーIDを使用してリクエストを行うということは、「ユーザーIDがわかれば、そのユーザーになりすませる」ということになる。
- セッションIDはランダムで予測が難しく、一時的である。なりすましは難しい。
セッションの制御
セッションタイムアウト
- セッションIDを用意することで、有効期限を設定し、一定時間アクティビティがない場合にセッションを自動的に終了させることができる。サーバーの負荷軽減になる。
- ユーザーIDのみでは、サーバーサイドが「ユーザーがまだアクティビティ中かどうか」を追跡するのが難しく、セッションタイムアウトが難しくなり、サーバーの負荷軽減ができない。
パスワードの変更時、セッションの終了
- パスワードが変更された時、以前のパスワードでログインされていたセッションは終了したい。
- ユーザーIDのみでは、
- セッションの終了ができない
- パスワードが変更されても、ユーザーIDを知っていれば不正アクセスができてしまう。
- セッションIDなら既存セッションをすべて終了し、ユーザーが以前使用した端末や、古いパスワードからのアクセスを防ぐことができる。
効率の低下
- ユーザーIDのみでは、都度1からユーザー情報を検索する必要があり、リソースを多く消費する。
- セッションIDでは、対応するセッションデータに迅速にアクセスできる。
スケーラビリティ(サービス規模が大きくなった時の影響)
- セッションIDの場合、セッションデータを効率的に管理する専用システム(Redis)などの使用により、影響を抑えることができる。
- ユーザーIDのみでは、セッション数が増えるごとにデータベースを強化する必要がある。これにはコストがかかる上、非常に効率が悪い。
実際の挙動
def login
@user = User.find_by(email: params[:email], password: params[:password])
if @user
session[:user_id] = @user.id # セッションへuser.idを追加
flash[:notice] = "ログインしました"
redirect_to("/posts/index")
else
@error_message = "メールアドレスまたはパスワードが間違っています"
@email = params[:email]
@password = params[:password]
render("users/login_form")
end
end
セッションの作成はいつ行われる?:初回リクエスト時
- ユーザーがアプリケーションに初めてアクセスすると、Railsは自動的に新しいセッションを生成します。
- この時、ユーザーのブラウザにセッションIDが格納されたクッキーが送信されます。
セッション変数への代入
session[:key] = valueのような代入は、既に生成されたセッションにデータを追加または更新する操作です。- この操作自体が新しいセッションを生成するわけではありません。既存のセッションを利用して、その中のデータを変更します。
セッションの動作
- クライアントは、リクエストをサーバーに送信する際にセッションIDを含めます。
- サーバーは、このセッションIDを使用して対応するセッションデータを取得し、その中の
user_idを参照して、現在のユーザーが誰であるかを判断します。
セッションの終了
やり方は主に2つ
session[:user_id] = nilのように、特定のキーをクリアする。
例:小規模なECサイトのログアウト
ログイン情報のみリセットすることで、ユーザーの言語設定やショッピングカートの中身などは保持される。
この時、セッションの完全な削除は、セッションの有効期限が切れるのを待つことになる。
(大規模なECサイトであればショッピングカートはデータベースに保存されるが、小規模なECサイトであればセッションに保存されている。サーバーのコストがかからないメリットがある。)
reset_session:セッションを完全に削除する
例:一般的なログアウト
全てのセッション情報を削除することで、セッションハイジャックを防ぐことができる。
メモ:セッションの変更はpostリクエスト
まとめ:セッション・セッションID、すごい
よく考えられてるなあ
before_action:application.html.erbの変数
構文はbefore_action :method_name
需要:application.html.erbで変数を使いたい
-
application.html.erb=共通部 は、すべてのコントローラーから呼び出される。 - 単に変数を使おうとすると全部のメソッドに変数をコピペしなきゃいけない。
-
application.html.erbに直接書くこともできるが、できれば変数はコントローラで一元管理したい。
そんなときに:before_action :method_name
これを使うと、すべてのアクションの前にmethod_nameメソッドが実行される。
application_controller.rbというファイルがあるので、ここに書くのが望ましい。
雑記:特定のアクションのみにbefore_actionしたい
そんな時は、:onlyオプションを使用すると良い。
before_action :authenticate_user, only: [:edit, :update]
この時、authenticate_userメソッドはeditメソッドとupdateメソッドのみに対してbefore_acitionする。
before_action :authenticate_user, except: [:edit, :update]
逆に、editメソッドとupdateを除いたすべてのメソッドに適用したい場合は、:exceptメソッドを使う。
雑記:認証authenticate_userについて
認証=authenticate、特にauthという略はITでよく出てくるので、覚えておいて損はない。
Ruby on Railsの認証・アクセス制御で使う場合は、application_controller.rbに書くと良い。各コントローラーはapplication_controller.rb (ApplicationController)を継承しているため。
終わりに
セッションを納得いくまで調べたのがハイライトですね!
OSI