Ruby on Rails での認証について調べたので、順番に実装を試してみる。
目次
前提条件
認証について作成する前に scaffold で Records テーブルと各コントローラを作っておいた。
1 | rails g scaffold Resords title:string body:string level:integer |
Basic 認証
ActionController::HttpAuthentication::Basic
を参考にする。
コントローラで定義する。
1 | class RecordsController < ApplicationController |
先頭で http_basic_authenticate_with を定義する。
定義することでアクセスしたときに、以下のように ID とパスワードを要求するようになる。
認証を要求しない画面例えば、一覧・個別の表示するだけならば、以下のようにexcept:
を定義する。
1 | class RecordsController < ApplicationController |
ここまでの場合、認証をRecordsController
に付けているので、
他のコントローラーを作ったらまた別途実装が必要になる。
なので、すべてのコントローラの継承元に認証を実装する。
1 | class ApplicationController < ActionController::Base |
これは動作しない。
こちらは動作する。
1 | class ApplicationController < ActionController::Base |
ID とパスワードに誤った入力をすれば、何度も要求を繰り返す。
authenticate_or_request_with_http_basic
を puts で出力して確認したところ、
認証の結果はtrue
もしくはHTTP Basic: Access denied.
を返していた。
(返り値型くらいは統一してほしいと思った。)
なので、認証のどこかへリダイレクトさせるとするなら、
以下のようにできるかと思った。
1 | class ApplicationController < ActionController::Base |
が、しかし動かない。
以下のようにすることで動かすことができたが、
一度認証エラーを起こすとサーバーとブラウザ再起動するまで、二度ログインできなくなった・・・。
1 | class ApplicationController < ActionController::Base |
これまでの内容をふまえて、以下を実現する。
- 複数のコントローラでも認証できるように定義
- 特定のページでは認証要求しない
1 | class ApplicationController < ActionController::Base |
1 | class RecordsController < ApplicationController |
RecordsController
はApplicationController
の継承で、basic_outh
はApplicationController
で定義されているので、RecordsController
からbasic_outh
が呼び出しできる。
Digest 認証
ActionController::HttpAuthentication::Digest](https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Digest.html)
を参考にする。
コントローラで定義する。
Basic 認証で学んだことも踏まえて、application_controller.rb で認証部分を作成
records_controller.rb で割り当てだけする。
以下の通り
1 | require 'digest/md5' |
1 | class RecordsController < ApplicationController |
以上で Digest 認証が作成できたけど、気になる点がいくつかあるので試す
ハッシュ値の作成関数
以下の部分で MD5 以外を当てたらどうなるか?
1 | USERLIST={"ID" => Digest::MD5.hexdigest(["ID",REALM,"p@ssw0rd"].join(":"))} |
この点を試すために、digest_outh
の処理で、辞書の検索結果をコンソールに出力するようにしてみる。
1 | def digest_outh |
上記の実装をしてブラウザからアクセスしユーザー名を ID として認証するとすると、コンソールに値が表示される。
MD5 のとき値はe1d7859d8d534102a331e11b9fb694b1
SHA1 のとき57df5d040ef136547a1c3f6e38af31c23b0a8903
SHA256 なら9e4e04ad705cc37a33db266460d4c039e73beb1cc2997784c8e3699c80599c08
より、長く複雑な文字列生成していることを確認できた。
ハッシュ値の作成の種部分
ハッシュ値作成の種が["ID",REALM,"p@ssw0rd"].join(":")
となっているけど
別にパスワード文字列でもできる
1 | USERLIST={"ID" => Digest::MD5.hexdigest(["ID",REALM,"p@ssw0rd"].join(":"))} |
この時生成されている値は0f359740bd1cda994f8b55330c86d845
になる。
以上 2 件を試したが これらは無駄である
- Digest 認証とはユーザー名とパスワードを MD5 でハッシュ化して送るものなので、
やったところで、送られたものとは一致しないので認証できない。 - 種部分をパスワードのみにしたが、
クライアントは[ユーザー名]:[realm]:[パスワード]
のようにそれぞれを:
でつないだ文字列を MD5 でハッシュ化したものを送る仕様だから認証できない。
realm には今回zxcvbn
を使用したけど、RFC2617では例として”myhost@testrealm.com“の利用を示していた。
無駄ではあったかもしれないが勉強になった
これで認証はできるんだけど、ログイン・ログアウトの仕組みにはならないので、まだまだやることがある。
ではでは。