Rails初学メモ
Remember me機能について
- ユーザーがログイン情報を入力してログインを試みる。
- サーバーはデータベースに対して認証を要求し、結果を受け取る。
- 認証に成功した場合、サーバーはデータベースから記憶トークンを要求し、それをブラウザにCookieとして設定する。これにより、ユーザーはホームページを表示できる。
- ユーザーがブラウザを閉じても、セッションは終了するが、Cookieは残る。
- ユーザーが再度ブラウザを開き、サイトにアクセスすると、ブラウザはCookie(ユーザーIDと記憶トークン)を含むリクエストをサーバーに送信する。
- サーバーはデータベースに対して記憶トークンの検証を要求し、結果に基づいてユーザーをログイン状態にするか、ログインページを表示するかを決定する。
user_idをcookieに保持する理由
user_idをCookieに保持する主な理由は、ユーザーがブラウザを閉じた後も、サーバーがユーザーを識別し、自動的にログイン状態を復元するため。
Remember me機能の実装において、user_idと記憶トークン(remember_token)を組み合わせて使用することで、セキュリティと利便性のバランスを取る。
以下にそのプロセスを詳しく記載。
-
ユーザー識別
user_idはデータベース内の特定のユーザーを識別するために必要。
ユーザーが再度ウェブサイトにアクセスした際に、サーバーはCookieに保存されたuser_idを使用して、そのユーザーのデータベースレコードを検索。 -
セキュリティ
単独でuser_idをCookieに保存することはセキュリティリスクを伴うが、記憶トークンと組み合わせることでリスクを軽減する。
サーバーは、Cookieに保存された記憶トークンがユーザーのデータベースレコードに保存されているハッシュ化されたトークンと一致するかを検証。
この一致が確認できた場合のみ、ユーザーを認証し、ログイン状態を復元。 -
利便性
ユーザーが毎回ログイン情報を入力する手間を省き、ブラウザを閉じた後もスムーズにサイトを利用できる。特に、複数のデバイスやブラウザを使用するユーザーにとって、この機能は大きな利便性を提供する。 -
セッション管理
user_idと記憶トークンの組み合わせは、セッション管理においても重要。
サーバーはこれらの情報を使用して、ユーザーのセッションを識別し、管理。
これにより、ユーザーが複数のタブやウィンドウでウェブサイトを利用していても、一貫したユーザー体験を提供できる。
Strong Parametersについて
コントローラー内で受け取ることが許可されているパラメータを明示的に指定できるパラメータ。
特定のアクションで必要とされるパラメータのみを許可し、
それ以外のパラメータが含まれている場合はエラーを発生させることで、不正なデータの受け入れを防ぐ。
Rails4.0から導入されたセキュリティ機能で、コントローラー層でのマスクアサインメントを防ぐために設計されたもの。
マスクアサインメント
ハッシュを用いてオブジェクトの複数の属性を一度に設定すること。
これには、セキュリティ上のリスクがあり、ユーザーがWebリクエストに管理者属性を不正に設定し、Webサイトの管理者権限を奪い取ることが可能。
例
params.require(:user).permit(:name, :email, :password, :password_confirmation)
:user
キーを持つparamsハッシュから、名前、メールアドレス、パスワード、パスワード確認の各フィールドのみを取り出し、残りの不正なパラメータを無視。
Strong Parametersを効率的に使用するためには、user_paramsのような外部メソッドを定義。
例
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
認可
認可(authorization)はそのユーザーが実行可能な操作を管理すること。
例えば、ユーザーAがユーザーBしか操作できないページに遷移する場合に認可処理をしないとユーザーAがユーザーBのページへ遷移できてしまい情報を変更できてしまう。
このようなケースでは、before_action
メソッドを使って処理を書く。
before_action
コントローラの全てのアクションが実行される前に何らかの処理を行う。
下記コードの例では、イメージなので本来であればbefore_action
メソッドで実行する処理は色々なケースを考慮してもっと複雑になる。
例
class UsersController < ApplicationController
before_action :メソッド名
end
class UsersController < ApplicationController
# 特定のアクションのときだけ使いたくない場合は、exceptオプションを用いる
before_action :logged_in_user, only: [:edit, :update]
# something
private
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
パスワード再設定のフロー
- ユーザーがパスワードの再設定をリクエスト→ユーザーが送信したメールアドレスをキーにしてデータベースからユーザーを見つける
- 該当メールアドレスがデータベースにある場合、再設定用トークンと対応する再設定ダイジェストを生成
- 再設定用ダイジェストはデータベースに保存しておき、再設定用トークンはメールアドレスと一緒に、ユーザーに送信する有効化用メールのリンクに仕込んでおく
- ユーザーがメールのリンクをクリックしたら、メールアドレスをキーとしてユーザーを探し、データベース内に保存しておいた再設定用ダイジェストと比較する(トークンを認証する)
- 認証に成功したら、パスワード変更用のフォームをユーザーに表示する
Userテーブルに追加するカラム↓
reset_digest、reset_sent_at
マイクロポスト
用意するモデル
Micropost
id | integer |
content | text |
user_id | integer |
created_at | datetime |
updated_at | datetime |
Text型は、ある程度の文字列を格納するため。String型は、255文字まで。
(本番環境でもパフォーマンスの差は出ない。)
モデル作成
rails generate model Micropost content:text user:references
user:references
はユーザーと1対1を示す。自動的に、インデックスと外部キー参照付きのuser_id
カラムを追加できる。
マイグレファイルで下記を追加。
add_index :microposts, [:user_id, :created_at]
user_id
とcreated_at
に対して、インデックスを貼ることで、user_id
に関連するすべてのマイクロポストを作成時刻の逆順で取りやすくできたりする。
また、[:user_id, :created_at]
で同じ配列に含めることで、Active Recordは両方のキーを同時に扱う 複合キーインデックスを作成。
belongs_to / has_many
belongs_to
→1対1
has_many
→1対多
上記を定義し関連付けることで、
@user = users(:michael)
@micropost = Micropost.new(content: "ほげ", user_id: @user.id)
を
@user = users(:michael)
@micropost = @user.microposts.build(content: "ほげ")
のように書けるようになる。
dependent: destroy
依存しているもの同士があり、片方が破棄された場合、もう片方を破棄する流れを自動的にやってくれる。
class User < ApplicationRecord
has_many :microposts, dependent: :destroy
...
end
feed
where
メソッド
Micropost.where("user_id = ?", id)
?
があることで、SQLに代入する前にid
がエスケープされるため、SQLインジェクション を避けることができる。
画像アップロード
Railsに標準搭載されているActive Storage
を使用。
has_one_attached
メソッドやhas_many_attached
メソッドが提供されている。
rails active_storage:install
rails db:migrate
※Active Storageはファイルのバリデーションに関する機能を提供していないため、自前で用意する必要がある。
バリデーションのgem
active_storage_validations
画像リサイズ
ImageMagick
sudo apt-get -y install imagemagick
gem
image_processing
、mini_magick
フォロー
複合キーインデックス
add_index :relationships, [:follower_id, :followed_id], unique: true
followed_id
とfollower_id
の組み合わせが必ずユニークになることを保証。
フィード
サブセレクト
SQLのサブセレクト(subselect)は、SQLクエリ内で別のSQLクエリを埋め込む方法。
サブセレクトは、外部クエリの条件や結果に基づいて内部クエリを実行し、その結果を外部クエリに組み込むことができる。データのフィルタリング、集計、結合、および他の多くの操作に使用される。
SELECT column1, column2, ...
FROM table_name
WHERE column_name IN (SELECT column_name FROM another_table WHERE condition);