ここまでは Devise のデフォルト設定のままメールアドレスを利用して認証を行ってきました。次にメールアドレスをのかわりにユーザ名を用いて認証を行うようにしてみたいと思います。
大体の手順は以下のとおりです。
- User モデルにユーザID (username) を追加する migration の実施
- devise の設定を変更して、username での認証に切り替え
- User の Devise controller を作成する。
- 上記で追加した controller で、username を parameter として定義
- devise で使用している View を、username に対応するように書き換え
User モデルに username を追加
まずは、User モデルに username を追加します。
1 2 3 |
$ bundle exec rails g migration AddUsernameToUser username:string invoke active_record create db/migrate/20190913052202_add_username_to_user.rb |
次に、emacs 等で migration ファイルを編集して、unique 等を設定します。
1 2 3 4 5 |
class AddUsernameToUser < ActiveRecord::Migration[5.2] def change add_column :users, :username, :string, :unique => true, :null => false, :default => "" end end |
migration ファイルを編集したら、migration を実行します。
1 2 3 4 5 |
$ bundle exec rails db:migrate == 20190913052202 AddUsernameToUser: migrating ================================ -- add_column(:users, :username, :string, {:unique=>true, :null=>false, :default=>""}) -> 0.0007s == 20190913052202 AddUsernameToUser: migrated (0.0009s) ======================= |
username での認証に切り替え
次に、Devise の設定変更を行い、email のかわりに username で認証するように変更します。
config/initializers/devise.rb の以下の部分を変更します。必須なのは config.authentication_keys の指定で :email から :username に変更するところです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# ==> Configuration for any authentication mechanism # Configure which keys are used when authenticating a user. The default is # just :email. You can configure it to use [:username, :subdomain], so for # authenticating a user, both parameters are required. Remember that those # parameters are used only when authenticating and not when retrieving from # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. - # config.authentication_keys = [:email] + config.authentication_keys = [:usernme] <snip> # Configure which authentication keys should be case-insensitive. # These keys will be downcased upon creating or modifying a user and when used # to authenticate or find a user. Default is :email. - config.case_insensitive_keys = [:email] + config.case_insensitive_keys = [:username] <snip> # Configure which authentication keys should have whitespace stripped. # These keys will have whitespace before and after removed upon creating or # modifying a user and when used to authenticate or find a user. Default is :email. - config.strip_whitespace_keys = [:email] + config.strip_whitespace_keys = [:username] |
User モデルに対応した controller の作成と username 対応
いくつかの記事では、ここで application controller でパラメータの許可を書いていたりしますが、アプリケーション全体に関係する話でもないのに application controller に書くのはちょっとイヤンな感じなので、ここはキッチリと Devise 用の controller を作成して対応します。
今回使用している User モデルに対応した controller を作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ bundle exec rails g devise:controllers users create app/controllers/users/confirmations_controller.rb create app/controllers/users/passwords_controller.rb create app/controllers/users/registrations_controller.rb create app/controllers/users/sessions_controller.rb create app/controllers/users/unlocks_controller.rb create app/controllers/users/omniauth_callbacks_controller.rb =============================================================================== Some setup you must do manually if you haven't yet: Ensure you have overridden routes for generated controllers in your routes.rb. For example: Rails.application.routes.draw do devise_for :users, controllers: { sessions: 'users/sessions' } end =============================================================================== |
インストラクションにあるように、config/routes.rb を編集して、session と registration の Controller として、先ほど作成した Controller を使用するように指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Rails.application.routes.draw do # devise_for :users devise_for :users, controllers: { sessions: 'users/sessions', passwords: 'users/passwords', registrations: 'users/registrations' } get 'home/index' root to: "home#index" # For details on the DSL available within this file, # see http://guides.rubyonrails.org/routing.html end |
編集したら、ちゃんと新しく作った controller に routing されているか確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ bundle exec rails routes Prefix Verb URI Pattern Controller#Action new_user_session GET /users/sign_in(.:format) users/sessions#new user_session POST /users/sign_in(.:format) users/sessions#create destroy_user_session DELETE /users/sign_out(.:format) users/sessions#destroy new_user_password GET /users/password/new(.:format) users/passwords#new edit_user_password GET /users/password/edit(.:format) users/passwords#edit user_password PATCH /users/password(.:format) users/passwords#update PUT /users/password(.:format) users/passwords#update POST /users/password(.:format) users/passwords#create cancel_user_registration GET /users/cancel(.:format) users/registrations#cancel new_user_registration GET /users/sign_up(.:format) users/registrations#new edit_user_registration GET /users/edit(.:format) users/registrations#edit user_registration PATCH /users(.:format) users/registrations#update PUT /users(.:format) users/registrations#update DELETE /users(.:format) users/registrations#destroy POST /users(.:format) users/registrations#create <snip> |
username を parameter として使用可能にする
次に、ログイン制御の sessions, ユーザ登録内容管理の registrations で :username を使用しているので、それぞれの controller で username を parameter として使用可能とします。
app/controllers/users/sessions_controller.rb
一番上の before_action と、一番下の方に protected 周りがコメントアウトされているので、以下のように変更します。
1 2 3 4 5 6 7 8 |
class Users::SessionsController < Devise::SessionsController before_action :configure_sign_in_params, only: [:create] protected def configure_sign_in_params devise_parameter_sanitizer.permit(:sign_in, keys: [:username]) end end |
app/controllers/users/registrations_controller.rb
同様に、一番上の brefore_action と一番下の方に protected 周りがコメントアウトされているので、以下のように変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Users::RegistrationsController < Devise::RegistrationsController before_action :configure_sign_up_params, only: [:create] before_action :configure_account_update_params, only: [:update] protected def configure_sign_up_params devise_parameter_sanitizer.permit(:sign_up, keys: [:username, :email]) end def configure_account_update_params devise_parameter_sanitizer.permit(:account_update, keys: [:username, :email]) end end |
devise で使用している View を、username に対応するように書き換え
app/view/users/sessions および app/view/users/registrations 以下の View を username に対応するように書き換えます。
app/view/users/sessions/new.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<div class="card col-sm-5 mx-auto mt-5"> <div class="card-body"> <h3 class="mt-1 mb-3">ログイン</h3> <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> <div class="form-group"> <%= f.label :username, "ユーザ名", :class => "form-label" %> <%= f.text_field :username, autofocus: true, autocomplete: "username", :class => "form-control" %> </div> <div class="form-group"> <%= f.label :password, "パスワード", :class => "form-label" %> <%= link_to "パスワードを忘れた場合", new_user_password_path, :class => "float-right" %> <%= f.password_field :password, :class => "form-control" %> <% if devise_mapping.rememberable? %> <div class="form-check"> <%= f.check_box :remember_me, :class => "form-check-input" %> <%= f.label :remember_me, "ログインを記憶", :class => "form-check-label" %> </div> <% end %> </div> <div class="actions"> <%= f.submit "ログイン", :class => "btn btn-primary btn-block" %> </div> <% end %> <div class="small text-muted text-center mt-4">新しいユーザですか?</div> <%= link_to "アカウントを作成", new_user_registration_path, :class => "btn btn-light btn-block border mt-1" %> </div> </div> |
app/view/users/registrations/new.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<div class="card col-sm-5 mx-auto mt-5"> <div class="card-body"> <h3 class="mt-1 mb-3">アカウントを作成</h3> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= render "devise/shared/error_messages", resource: resource %> <div class="form-group"> <%= f.label :username, "ユーザ名", :class => "form-label" %> <%= f.text_field :username, autofocus: true, autocomplete: "username", :class => "form-control" %> </div> <div class="form-group"> <%= f.label :email, "メールアドレス", :class => "form-label" %> <%= f.email_field :email, autocomplete: "email", :class => "form-control" %> </div> <div class="form-group"> <%= f.label :password, "パスワード (%s文字以上)"%@minimum_password_length, :class => "form-label" %> <%= f.password_field :password, :class => "form-control" %> </div> <div class="form-group"> <%= f.label :password_confirmation, "もう一度パスワードを入力してください", :class => "form-label" %> <%= f.password_field :password_confirmation, :class => "form-control" %> </div> <div class="actions"> <%= f.submit "アカウントを作成する", :class => "btn btn-primary btn-block" %> </div> <% end %> <div class="mt-2"> すでにアカウントをお持ちですか?<%= link_to "ログイン", new_user_session_path %> </div> </div> </div> |
app/view/users/registrations/edit.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<div class="card col-sm-6 mx-auto mt-5"> <div class="card-body"> <h3 class="text-center mt-1 mb-4">アカウントを編集する</h3> <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> <%= render "devise/shared/error_messages", resource: resource %> <div class="form-group"> <%= f.label :username, "ユーザ名", :class => "form-label" %> <%= f.text_field :username, autofocus: true, autocomplete: "username", :class => "form-control" %> </div> <div class="form-group"> <%= f.label :email, "Eメール", :class => "form-label" %> <%= f.email_field :email, autocomplete: "email", :class => "form-control" %> </div> <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div> <% end %> <div class="form-group"> <%= f.label :password, "パスワード", :class => "form-label" %> <span class="small">(パスワードを変更しない場合は記入しないでください。)</span> <%= f.password_field :password, autocomplete: "new-password", :class => "form-control" %> <% if @minimum_password_length %> <div class="small form-text text-muted">最低<%= @minimum_password_length %>文字以上</div> <% end %> </div> <div class="form-group"> <%= f.label :password_confirmation, "パスワード(確認)", :class => "form-label" %><br /> <%= f.password_field :password_confirmation, autocomplete: "new-password", :class=>"form-control" %> </div> <div class="form-group"> <%= f.label :current_password, "現在のパスワード", :class => "form-label" %> <%= f.password_field :current_password, autocomplete: "current-password", :class => "form-control" %> <div class="small form-text">変更のために現在のパスワードを入力してください。</div> </div> <div class="actions"> <%= f.submit "更新する", :class => "btn btn-primary btn-block" %> <%= link_to "キャンセルして戻る", :back, :class => "btn btn-secondary btn-block" %> </div> <% end %> </div> </div> |
動作確認
ここまでできたら、サーバを再起動して動作を確かめてみましょう。
1 |
$ bundle exec rails s |
ユーザ名ありで登録して、ユーザ名でログインできればオッケーです。こんな感じ。