BETA

Devise を使って Active Directory 認証を実装する

投稿日:2019-04-01
最終更新:2019-09-27

社内の認証基盤で、Active Directory(AD) を使っている企業は多くありますが、
業務システムの認証基盤として、 社内の AD を使用したいというニーズもあると思います。
今回は、Rails で業務システムを作り、Devise を使用して、AD 認証を実装してみます。

Ruby と Rails のバージョンは↓のとおりです。

$ ruby -v  
ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-linux]  

$ bundle exec rails -v  
Rails 5.2.3  

Gemfile の編集

AD 認証を実装するために、Devise と、Devise LDAP Authenticatable(Devise LDAP) を使用します。
ちなみに、Devise の詳しい解説については、よくわかってないのでこの記事では取り扱いませんのであしからず。

gem 'devise'  
gem 'devise_ldap_authenticatable'  

編集したら、bundle install でインストールします。

Devise と Devise LDAP のセットアップ

はじめに、Devise をセットアップしUserモデルを作成します。その後で、Devise LDAP のセットアップを行います。

bundle exec rails g devise:install ## Devise のインストール  
bundle exec rails g devise User ## User モデルの作成  
bundle exec rails g devise_ldap_authenticatable:install ## Devise LDAP のインストール  

User モデルの編集

生成された User モデルを編集します。
トラブルシュートなどでの利用を想定して、ログインしたPCのIPアドレスを記録したり、
ログイン回数などを記録できる:trackableを追記しました。

# app/models/user.rb  

class User < ApplicationRecord  
  devise :ldap_authenticatable, :rememberable, :trackable  
end  

マイグレーションファイルの修正

修正した User モデルに合わせて、マイグレーションファイルを修正します。
また、デフォルトではメールアドレスとパスワードで認証を行うのですが、メールアドレスを持っていなかったり、AD の mail 属性を使っていない場合を考慮して、ログインユーザー名(sAMAccountName)でログインできるようにします。

# db/migrate/XXXXXXXXXX_devise_create_users.rb  

# frozen_string_literal: true  

class DeviseCreateUsers < ActiveRecord::Migration[5.2]  
  def change  
    create_table :users do |t|  

      ## LDAP authenticatable  
      t.string :username, null: false, default: "", unique: true  

      ## Database authenticatable  
      # t.string :email,              null: false, default: ""  
      # t.string :encrypted_password, null: false, default: ""  

      ## Recoverable  
      # t.string   :reset_password_token  
      # t.datetime :reset_password_sent_at  

      ## Rememberable  
      t.datetime :remember_created_at  

      ## Trackable  
      t.integer  :sign_in_count, default: 0, null: false  
      t.datetime :current_sign_in_at  
      t.datetime :last_sign_in_at  
      t.string   :current_sign_in_ip  
      t.string   :last_sign_in_ip  

      ## Confirmable  
      # t.string   :confirmation_token  
      # t.datetime :confirmed_at  
      # t.datetime :confirmation_sent_at  
      # t.string   :unconfirmed_email # Only if using reconfirmable  

      ## Lockable  
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts  
      # t.string   :unlock_token # Only if unlock strategy is :email or :both  
      # t.datetime :locked_at  


      t.timestamps null: false  
    end  

    add_index :users, :username,                unique: true  

    # add_index :users, :email,                unique: true  
    # add_index :users, :reset_password_token, unique: true  
    # add_index :users, :confirmation_token,   unique: true  
    # add_index :users, :unlock_token,         unique: true  
  end  
end  

マイグレーションファイルを修正したら、bundle exec rails db:migrate でマイグレートします。

Devise の初期設定

Devise の初期設定を行います。変更した箇所だけを抜粋します。
今回は、アプリ側でユーザー登録しなくても、AD にユーザーがいればログイン可能なようにしたいと思います。

# config/initializers/devise.rb  

  config.ldap_logger = true  # LDAP クエリを Rails のログに記録する。  
  config.ldap_create_user = true  # すべての有効な AD ユーザーがログイン可能になり、自動的にユーザーのレコードが登録される。  
  config.ldap_update_password = false  # パスワード変更を AD 側に書き戻さないようにする。  
  config.ldap_use_admin_to_bind = true  # LDAP認証時のバインドで Administrator を利用する。  
  config.authentication_keys = [:username]  # 認証で利用するキー。今回は、User モデルの username を利用する。  
  config.case_insensitive_keys = [:username]  # キーの大文字小文字を区別しない  
  config.strip_whitespace_keys = [:username]  # キーに含まれる空白を削除する  

LDAP の設定

AD の接続情報などを設定します。

# config/ldap.yml  

development:  
  host: <ドメインコントローラ名>  
  port: 389  
  attribute: sAMAccountName  
  base: CN=Users,DC=devel,DC=local  
  admin_user: CN=Administrator,CN=Users,DC=devel,DC=local  
  admin_password: <admin_user のパスワード>  
  ssl: false  

View の作成

Devise の View を作成します。今回は、ユーザー登録やメールでの登録確認等は行わないので、ログイン画面だけを作成します。

bundle exec rails generate devise:views users -v sessions  

View の修正

作成したログイン画面を修正します。
デフォルトではメールアドレスの入力欄になっているので、これを Windows アカウント名の入力欄に変更します。

# app/views/users/sessions/new.html.erb  

<h2>Log in</h2>  

<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>  
  <div class="field">  
    <%= f.label :username, "Windows アカウント名" %><br />  
    <%= f.text_field :username, autofocus: true, autocomplete: "username" %>  
  </div>  

  <div class="field">  
    <%= f.label :password %><br />  
    <%= f.password_field :password, autocomplete: "current-password" %>  
  </div>  

  <% if devise_mapping.rememberable? %>  
    <div class="field">  
      <%= f.check_box :remember_me %>  
      <%= f.label :remember_me %>  
    </div>  
  <% end %>  

  <div class="actions">  
    <%= f.submit "Log in" %>  
  </div>  
<% end %>  

<%= render "users/shared/links" %>  

Controller の生成

次にコントローラを作成します。
ログイン周りのハンドリングができれば良いので、Sessions のコントローラのみを生成します。

bundle exec rails generate devise:controllers users -c=sessions  

Controller の修正

生成したコントローラはすべてコメントアウトされているので、適宜コメントを外します。

# app/controllers/users/sessions_controller.rb  

# frozen_string_literal: true  

class Users::SessionsController < Devise::SessionsController  
  before_action :configure_sign_in_params, only: [:create]  

  # GET /resource/sign_in  
  def new  
    super  
  end  

  # POST /resource/sign_in  
  def create  
    super  
  end  

  # DELETE /resource/sign_out  
  def destroy  
    super  
  end  

  protected  

  # If you have extra params to permit, append them to the sanitizer.  
  def configure_sign_in_params  
    devise_parameter_sanitizer.permit(:sign_in, keys: [:username])  
  end  
end  

ログイン後のページを作成

今の状態だと、ログイン画面はありますがログイン後に表示するページがありません。
静的なページhomeを作って、ログイン後はhomeに遷移するようにします。

bundle exec rails g controller StaticPages home  

デフォルトで生成されるページだとあまり面白くないので、username を表示させたいと思います。
また、どこからもログアウトできないと困るので、ログアウトのリンクを追記します。

# app/views/static_pages/home.html.erb  

<h1>StaticPages#home</h1>  
<p>Find me in app/views/static_pages/home.html.erb</p>  
<p>Welcome <%= current_user.username %> !</p>  
<%= link_to  "ログアウト", destroy_user_session_path %>  

routes の編集

コントローラを作成した際にいくつか自動で追加されているルートもありますが、今回のアプリではログイン画面を URL の ルートにしたいと思います。
こんな感じで routes.rb を編集します。

# config/routes.rb  

Rails.application.routes.draw do    
  get 'home', to: 'static_pages#home'  

  devise_for :users, controllers: {  
    sessions: 'users/sessions'  
  }  

  devise_scope :user do  
    root to: 'users/sessions#new'  
    get 'sign_in', to: 'users/sessions#new'  
    get '/users/sign_out', to: 'users/sessions#destroy'  
  end  
end  

デフォルトの動作では、ログインしたあとに URL のルートに遷移するため、ログイン画面が無限ループします。
それを回避するために、application_controller.rb に、after_sign_in_path_for メソッドを追記してhomeに遷移させます。

# app/controllers/application_controller.rb  

class ApplicationController < ActionController::Base  
  rescue_from DeviseLdapAuthenticatable::LdapException do |exception|  
    render :text => exception, :status => 500  
  end  

  def after_sign_in_path_for(resource)  
    home_path  
  end  
end  

ログインしてみる

ここまでできたらbundle exec rails s でサーバーを起動して、実際にログインしてみましょう。
localhost:3000 にアクセスしたら、こんなログイン画面が表示されるはずです。

AD に登録されているユーザー名とパスワードを入力してログインすると、、、

アプリ側で特にユーザー登録をしていませんが、ADで認証されたユーザーが Rails のデータベースに登録されているので、無事にログインできました。

ところで、ひとつバグがあります。
今の状態だとログインしていなくてもhomeにアクセスできてしまうので、homeは認証必須にします。

before_action フィルタで、認証済みのユーザーのみがhomeにアクセスできるようにします。

# app/controllers/static_pages_controller.rb  

class StaticPagesController < ApplicationController  
  before_action :authenticate_user!  

  def home  
  end  
end  

これで、ダイレクトに/homeにアクセスしようとすると、ログイン画面にリダイレクトされるようになりました。

今回作成したアプリのリポジトリはGithubに登録しています。
ad_auth


続編を書きました!!
Devise を使って Active Directory から情報を取得する方法

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@toyocyの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!