Ruby on Railsで、パスワードを非可逆暗号化する場合、has_secure_passwordを使うと便利です。しかし、私は、twilogサービスのように、メールアドレスを入力しない場合、暗号化したバスワードをシステムに保存しないようなRailsアプリを作りたく思っています。has_secure_passwordは、Rails 4では、validationsというオプションを持っていて、値falseを渡すと、has_secure_passwordがスキップされます。しかし、Rails 3.2では、そのようなオプションがなく、スキップが可能ではありません。一つの回避策として、stackoverflowに、How to skip has_secure_password validationsというスレッドがあり、それを試してみたのですが、コードが足りないようで、動作しませんでした。さらに、ググった所、RailsCastに、#250 Authentication from Scratchで、スクラッチから認証を実装しているサンプルを見つけました。
#250 Authentication from Scratchのサンプルを、Rails 3.2で動かそうとすると、問題にぶつかりました。サンプルでは、passwordにattr_accessorを使っていますが、これは、Rails 3.2では使用を推奨されていません。かわりに、attr_accessible を使用すべきですが、それが定義された属性は、データベーステーブルのカラムにならなければいけません(後記、ここの記述は間違いかもしれません。ググっていて、そういう情報に触れたのですが、そのurlを失念しました。また、カラムにならなくても、問題無いようです)。平文のpasswordやpassword_confirmationは、データベースに保存すべきではないので、attr_accessibleを使用できないということになります。そこで、Usersクラスでは、emailのみ、attr_accessibleを使用し、passwordとpassword_confirmationに対しては、アクセサをあらわに書きました。
def password @password end def password=(password_str) @password = password_str end def password_confirmation @password_confirmation end def password_confirmation=(password_confirmation_str) @password_confirmation = password_confirmation_str end
そして、メールアドレス、パスワード入力フォームから呼ばれるcontroller、UsersControllerクラスで、
def create email = params[:user][:email] password = params[:user][:password] password_confrimation = params[:user][:password_confirmation] @user = User.new(:email => email) @user.password = password @user.password_confirmation = password_confrimation if @user.save redirect_to root_url, notice: "signed up!" else render "new" end end
のように書き、Userクラスを生成するとき、emailの値のみを渡すようにしました。このようにすることで、パスワードが平文でデータベースに保存されることはなくなりました。
改良したサンプルを、GitHubに置きました。 andropenguin/auth_from_scratch
あとは、メールアドレス、パスワードの入力がない場合、パスワードの保存を行わず、入力がある場合、暗号化したパスワードをシステムに保存するようにしたいですが、それは多分、そんなに難しくはないでしょう。