###ユーザーの作成と認証
以上でUserモデルの基本部分が完了しましたので、今度はユーザー情報表示ページを作成するときに備えて、データベースに新規ユーザーを1人作成
しましょう。
また、Userモデルにhas_secure_password
を追加した効果についても見ていきましょう。
ただしWebからのユーザー登録はまだできないので、今回はRailsコンソールを使ってユーザーを手動で作成
することにしましょう。create
を使いますが、後々実際のユーザーを作成する必要が出てくるので、今回はサンドボックス環境は使いません。
したがって、今回作成したユーザーを保存すると、データベースに反映
されます。
それでは、まずrails consoleコマンドを実行してセッションを開始し、次に有効な名前・メールアドレス・パスワード・パスワード確認を渡してユーザーを作成してみましょう。
>> User.create(name: "Michael Hartl", email: "micael@example.com",
?> password: "foobar", password_confirmation: "foobar")
(1.6ms) SELECT sqlite_version(*)
(0.1ms) begin transaction
User Exists? (0.6ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "micael@example.com"], ["LIMIT", 1]]
User Create (17.2ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?) [["name", "Michael Hartl"], ["email", "micael@example.com"], ["created_at", "2021-09-27 13:35:33.010426"], ["updated_at", "2021-09-27 13:35:33.010426"], ["password_digest", "$2a$12$DFJchIS8RiYTgJniH8HFReeYPE.Yq.E9xtfLl6h71xlmpNj5PsimC"]]
(7.2ms) commit transaction
=> #<User id: 3, name: "Michael Hartl", email: "micael@example.com", created_at: "2021-09-27 13:35:33", updated_at: "2021-09-27 13:35:33", password_digest: [FILTERED]>
うまくデータベースに保存されたかどうかを確認するために、開発環境用のデータベースをDB Browser for SQLiteで開き、usersテーブルの中身を見てみましょう。
もしクラウドIDEを使っている場合は、データベースのファイルをダウンロードして開いてください。
このとき、先ほど定義したUserモデルの属性に対応したカラムがあることにも注目しておいてください。
コンソールに戻ってpassword_digest
属性を参照してみると、has_secure_password
の効果を確認できます。
>> user = User.find_by(email: "micael@example.com")
User Load (2.7ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "micael@example.com"], ["LIMIT", 1]]
=> #<User id: 3, name: "Michael Hartl", email: "micael@example.com", created_at: "2021-09-27 13:35:33", updated_at: "2021-09-27 13:35:33", password_digest: [FILTERED]>
>> user.password_digest
=> "$2a$12$DFJchIS8RiYTgJniH8HFReeYPE.Yq.E9xtfLl6h71xlmpNj5PsimC"
これは、Userオブジェクトを作成したときに、"foobar"という文字列がハッシュ化された結果
です。
bcrypt
を使って生成されているので、この文字列から元々のパスワードを導出することは、コンピュータを使っても非現実的
です 。
has_secure_password
をUserモデルに追加したことで、そのオブジェクト内でauthenticate
メソッドが使えるようになっています。
このメソッドは、引数に渡された文字列(パスワード)をハッシュ化した値と、データベース内にあるpassword_digestカラムの値を比較します。試しに、先ほど作成したuserオブジェクトに対して間違ったパスワードを与えてみましょう。
>> user.authenticate("not_the_right_password")
=> false
>> user.authenticate("foobaz")
=> false
間違ったパスワードを与えた結果、user.authenticateがfalseを返したことがわかります。
次に、正しいパスワードを与えて、今度はauthenticateがそのユーザーオブジェクトを返すようになります。
>> user.authenticate("foobar")
=> #<User id: 3, name: "Michael Hartl", email: "micael@example.com", created_at: "2021-09-27 13:35:33", updated_at: "2021-09-27 13:35:33", password_digest: [FILTERED]>
第8章では、このauthenticateメソッドを使ってログインする方法を解説します。
なお、authenticateがUserオブジェクトを返すことは重要ではなく、返ってきた値の論理値がtrueであることが重要です。
!!でそのオブジェクトが対応する論理値オブジェクトに変換
できることを思い出してください。
この性質を利用すると、user.authenticate
がいい感じに仕事をしてくれるようになります
>> !!user.authenticate("foobar")
=> true
###演習
1.コンソールを一度再起動して(userオブジェクトを消去して)、このセクションで作ったuserオブジェクトを検索してみてください。
> user = User.find_by(name: "Michael Hartl")
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "Michael Hartl"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com", created_at: "2021-09-23 05:29:52", updated_at: "2021-09-23 05:29:52", password_digest: nil>
2.オブジェクトが検索できたら、名前を新しい文字列に置き換え、saveメソッドで更新してみてください。うまくいきませんね...、なぜうまくいかなかったのでしょうか?
> user.name = "mannbou"
=> "mannbou"
>> user.save
(0.1ms) begin transaction
User Exists? (0.8ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? AND "users"."id" != ? LIMIT ? [["email", "michael@example.com"], ["id", 1], ["LIMIT", 1]]
(0.1ms) rollback transaction
=> false
>> user
=> #<User id: 1, name: "mannbou", email: "michael@example.com", created_at: "2021-09-
3.今度は6.1.5で紹介したテクニックを使って、userの名前を更新してみてください。
>> user
=> #<User id: 3, name: "Michael Hartl", email: "micael@example.com", created_at: "2021-09-27 13:35:33", updated_at: "2021-09-27 14:39:47", password_digest: [FILTERED]>
>> user.errors.full_messages
=> []
>> user.name = "aa"
=> "aa"
>> user.save
(0.1ms) begin transaction
User Exists? (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? AND "users"."id" != ? LIMIT ? [["email", "micael@example.com"], ["id", 3], ["LIMIT", 1]]
User Update (0.9ms) UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "aa"], ["updated_at", "2021-09-27 14:40:24.447792"], ["id", 3]]
(5.7ms) commit transaction
=> true