LoginSignup
3
6

More than 3 years have passed since last update.

Rails6 のちょい足しな新機能を試す11(has_secure_password 編)

Last updated at Posted at 2019-05-06

はじめに

Rails 6 に追加されそうな新機能を試す第11段。 今回のちょい足し機能は、 has_secure_password 編です。
has_secure_password にオプションとしてカラムを指定できるようになりました。

記載時点では、Rails は 6.0.0.rc1 です。 gem install rails --prerelease でインストールできます。


$  rails --version
Rails 6.0.0.rc1

プロジェクトを作成する

rails プロジェクトを作成します。

$ rails new sandbox6_0_0rc1
$ cd sandbox6_0_0rc1

Gemfile を編集して bcrypt gem を追加する

has_secure_password を使うために bcrypt gem を追加します。

Gemfile の bcrypt の行を有効にします。

Gemfile
# Use Active Model has_secure_password
gem 'bcrypt', '~> 3.1.7'

bundle を実行します

$ bundle

User モデルを作成する

User モデルを作成します。
このとき、password の他に token 用の digest カラムを追加します。

$ bin/rails g model User name password_digest token_digest
$ bin/rails db:create db:migrate

User モデルを編集する

User model を編集し、 has_secure_password を追加します。
:token オプションを追加したものとオプションを省略したもの2行を追加します。
オプションを省略した場合は従来と同じですね。

app/models/user.rb
class User < ApplicationRecord
  has_secure_password # これは今までと同じ
  has_secure_password :token, validations: true # こちらは新機能
end

has_secure_password の機能を確認する

rails コンソールから確認してみます。


$ bin/rails c
Running via Spring preloader in process 393
Loading development environment (Rails 6.0.0.rc1)

オプションなしの方は今までと変わりません。 password, password_confirmation などのメソッドが使えます。

irb(main):001:0> u = User.new
=> #<User id: nil, name: nil, password_digest: nil, token_digest: nil, created_at: nil, updated_at: nil>
irb(main):002:0> u.password
=> nil
irb(main):003:0> u.password_confirmation
=> nil

オプションを指定した :token に合わせて、 token token_confirmation などのメソッドが追加されます。

irb(main):004:0> u.token
=> nil
irb(main):005:0> u.token_confirmation
=> nil

validation を試してみると password だけではなく token についてもチェックしていることがわかります。

irb(main):006:0> u.valid?
=> false
irb(main):007:0> u.errors.full_messages
=> ["Password can't be blank", "Token can't be blank"]

tokentoken_confirmation が一致しているかどうかのチェックもしてくれます。

irb(main):008:0> u.password = 'password'
=> "password"
irb(main):009:0> u.password_confirmation = 'pass'
=> "pass"
irb(main):010:0> u.token = 'token'
=> "token"
irb(main):011:0> u.token_confirmation = 'tok'
=> "tok"
irb(main):012:0> u.valid?
=> false
irb(main):013:0> u.errors.full_messages
=> ["Password confirmation doesn't match Password", "Token confirmation doesn't match Token"]

password_confirmation, token_confirmation を設定してデータベースに保存してみましょう。

irb(main):014:0> u.password_confirmation = 'password'
=> "password"
irb(main):015:0> u.token_confirmation = 'token'
=> "token"
irb(main):016:0> u.name = 'user1'
=> "user1"
irb(main):017:0> u.save
   (0.3ms)  BEGIN
   User Create (0.5ms)  INSERT INTO "users" ("name", "password_digest", "token_digest", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["name", "user1"], ["password_digest", "$2a$10$uvvJuUHV8fNQ/0GRRtyewuboiAEUpD4Q1.0/coyWWhVxOivHnIMia"], ["token_digest", "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe27/EE56UpC"], ["created_at", "2019-05-03 23:04:03.971339"], ["updated_at", "2019-05-03 23:04:03.971339"]]
   (7.3ms)  COMMIT
=> true

今保存したデータを読み直します。


irb(main):022:0> u = User.last
User Load (0.6ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]
=> #<User id: 2, name: "user1", password_digest: [FILTERED], token_digest: "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe...", created_at: "2019-05-03 23:04:03", updated_at: "2019-05-03 23:04:03">

password は今まで通り authenticate で認証できます。

irb(main):023:0> u.authenticate('pass')
=> false
irb(main):024:0> u.authenticate('password')
=> #<User id: 2, name: "user1", password_digest: [FILTERED], token_digest: "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe...", created_at: "2019-05-03 23:04:03", updated_at: "2019-05-03 23:04:03">

tokenauthenticate_token で認証できます。

irb(main):025:0> u.authenticate_token('password')
=> false
irb(main):026:0> u.authenticate_token('token')
=> #<User id: 2, name: "user1", password_digest: [FILTERED], token_digest: "$2a$10$56jLRgb1n0WjLOa/9NqIoOm3Il8nfyXCCisk5oieTxe...", created_at: "2019-05-03 23:04:03", updated_at: "2019-05-03 23:04:03">

validations: false にしたときは?

validations: false のときは、validation のチェックをしません。 authenticate_token (authenticate_xxx) は使えます。

app/models/user.rb
class User < ApplicationRecord
  has_secure_password
  has_secure_password :token, validations: false
end
irb(main):007:0> u = User.new
=> #<User id: nil, name: nil, password_digest: nil, token_digest: nil, created_at: nil, updated_at: nil>
irb(main):008:0> u.valid?
=> false
irb(main):009:0> u.errors.full_messages
=> ["Password can't be blank"]
irb(main):010:0> u.password = u.password_confirmation = 'password'
=> "password"
irb(main):011:0> u.token = 'token'
=> "token"
irb(main):012:0> u.save
(0.3ms)  BEGIN
User Create (0.7ms)  INSERT INTO "users" ("password_digest", "token_digest", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["password_digest", "$2a$10$NC1IH2S8G/RKX7myjNfeeepMknnQUuKIqD60S70iom3ZOQl92j/Cq"], ["token_digest", "$2a$10$xFYYXX7Bxk6qAOaqd3M7COlkZBCYodZK4HYQoBxLTNjr2dibMU/MK"], ["created_at", "2019-05-03 23:42:47.628292"], ["updated_at", "2019-05-03 23:42:47.628292"]]
(7.2ms)  COMMIT
=> true
irb(main):013:0> u = User.last
User Load (0.6ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]
=> #<User id: 3, name: nil, password_digest: [FILTERED], token_digest: "$2a$10$xFYYXX7Bxk6qAOaqd3M7COlkZBCYodZK4HYQoBxLTNj...", created_at: "2019-05-03 23:42:47", updated_at: "2019-05-03 23:42:47">
irb(main):014:0> u.authenticate_token('pass')
=> false
irb(main):015:0> u.authenticate_token('token')
=> #<User id: 3, name: nil, password_digest [FILTERED], token_digest: "$2a$10$xFYYXX7Bxk6qAOaqd3M7COlkZBCYodZK4HYrQoBxLTNj...", created_at: "2019-05-03 23:42:47", updated_at: "2019-05-03 23:42:47">"'"]

:validations オプションを省略したときは?

validations: true を指定したときと同じ動作になります。

おまけ

irb の出力で password_digest[FILTERED] となっているのに token_digest[FILTERED] にならないのは、

config/initializers/filter_parameter_logging.rb で設定されていないからです。
token も追加してあげれば [FILTERED] になります。

config/initializers/filter_parameter_logging.rb
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password, :token]

参考情報

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6