9 Stimmen

Verwendung eines Anmeldeformulars mit zwei Devise-Benutzermodellen und unterschiedlichen Authentifizierungsmethoden

Ich baue eine Website, die die Authentifizierung sowohl über LDAP als auch mit "lokalen" Benutzern unterstützen muss, die nur auf der Website verwaltet werden.

Derzeit habe ich die folgenden Devise-Modelle:

class User < ActiveRecord::Base
end

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

class LocalUser < User
  devise :database_authenticatable, :registerable, :confirmable, :recoverable, :trackable
end

Devise generiert separate Routen für jede dieser, d.h. /local_users/sign_in und /ldap_users/sign_in. Das ist nicht ideal, Benutzer sollten nicht wissen müssen, welcher Benutzertyp sie sind. Daher möchte ich alles in ein Formular oder eine Liste mit einem Satz von Anmelde- / Abmelde-URLs vereinheitlichen.

Ich habe mir einige Lösungen angesehen, wie man das machen kann, aber sie scheinen darauf zu beruhen, dass die Modelle die gleiche Devise-Konfiguration oder die gleiche Authentifizierungsmethode haben.

Das einzige weitere Online-Beispiel für diese Art von Problem, das ich gefunden habe, ist dieser Google Groups-Thread: https://groups.google.com/forum/#!topic/plataformatec-devise/x7ZI6TsdI2E - der nicht beantwortet wurde.

9voto

danpalmer Punkte 2126

Dies hat eine ganze Weile gedauert, bis ich es herausgefunden habe, aber ich habe schließlich eine funktionierende Lösung gefunden.

Den Großteil des Credits möchte ich auch Jordan MacDonald geben, der die Frage, die ich oben erwähnt habe, in der Devise Google Group gepostet hat. Obwohl dieser Thread keine Antwort hatte, fand ich das Projekt, an dem er gearbeitet hatte, las den Code und passte ihn an meine Bedürfnisse an. Das Projekt ist Triage und ich empfehle dringend, die Implementierungen des SessionController und des Routings zu lesen.

Ich empfehle auch Jordan's Blog-Post über Devise: http://www.wastedintelligence.com/blog/2013/04/07/understanding-devise/


Modell

Wie oben ist mein Modell wie folgt, und ich verwende das Paket devise_ldap_authenticatable. In diesem Beispiel habe ich zwei Benutzer, LdapUser und LocalUser, aber ich sehe keinen Grund, warum dies nicht für beliebige zwei Devise-Benutzermodelle funktionieren würde, solange Sie eine Möglichkeit haben, sie zu unterscheiden.

class User < ActiveRecord::Base
end

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

class LocalUser < User
  devise :database_authenticatable, :registerable, :confirmable, :recoverable, :trackable
end

Controller

Der erste Teil, den wir benötigen, ist der Controller. Er sollte von Devise::SessionsController erben und wählt aus, um welchen Typ Benutzer es sich handelt, leitet dies explizit an die Authentifizierungsphase weiter, die von Warden behandelt wird.

Weil ich LDAP gegen eine Active Directory-Domäne für einen Teil der Authentifizierung verwendet habe, konnte ich leicht erkennen, welche Details gegen LDAP authentifiziert werden sollten und welche nicht, aber das ist implementationsabhängig.

class SessionsController < Devise::SessionsController
  def create

    # Ermittle, um welchen Typ Benutzer es sich handelt.
    # Die Methode 'type_if_user' ist implementationsabhängig und nicht bereitgestellt.

    user_class = nil
    error_string = 'Anmeldung fehlgeschlagen'
    if type_of_user(request.params['user']) == :something
      user_class = :local_user
      error_string = 'Benutzername oder Passwort inkorrekt'
    else
      user_class = :ldap_user
      error_string = 'LDAP-Details inkorrekt'
    end

    # Kopiere Benutzerdaten zu ldap_user und local_user
    request.params['ldap_user'] = request.params['local_user'] = request.params['user']

    # Verwende Warden, um den Benutzer zu authentifizieren, falls wir nil zurückbekommen, ist es fehlgeschlagen.
    self.resource = warden.authenticate scope: user_class
    if self.resource.nil?
      flash[:error] = error_string
      return redirect_to new_session_path
    end

    # Jetzt, da wir wissen, dass der Benutzer authentifiziert ist, melde ihn auf der Website mit Devise an
    # Zu diesem Zeitpunkt ist self.resource ein gültiges Benutzerkonto.
    sign_in(user_class, self.resource)
    respond_with self.resource, :location => after_sign_in_path_for(self.resource)
  end

  def destroy
      # Sitzung beenden
  end

  def new
      # Lege eine leere Ressource für die Ansichtsrendering fest
      self.resource = User.new
  end
end

Routen

Devise richtet viele Routen für jeden Benutzertyp ein, und größtenteils möchten wir dies zulassen. Da wir jedoch den SessionsController überschreiben, müssen wir diesen Teil überspringen.

Nachdem es seine Routen eingerichtet hat, möchten wir dann unsere eigenen Handler für sign_in und sign_out hinzufügen. Beachten Sie, dass der Devise-Scope local_user keine Rolle spielt. Er braucht nur einen Standardbereich, den wir sowieso im Controller überschreiben. Beachten Sie auch, dass dies local_user singular ist. Dies hat mich überrascht und verursachte viele Probleme.

devise_for :ldap_users, :local_users, skip: [ :sessions ]

devise_scope :local_user do
  get 'sign_in' => 'sessions#new', :as => :new_session
  post 'sign_in' => 'sessions#create', :as => :create_session
  delete 'sign_out' => 'sessions#destroy', :as => :destroy_session
end

Ansicht

Die Ansicht ist sehr einfach und kann ohne allzu viele Probleme geändert werden.

  <%= form_for(resource, :as => 'user', url: create_session_path) do %>

      Anmelden
      LDAP-Benutzername oder Datenbank-E-Mail

      Passwort

  <% end %>

Ich hoffe, dass dies jemand anderem hilft. Dies ist die zweite Web-App, an der ich gearbeitet habe, die sowohl LDAP- als auch lokale Authentifizierung benötigte (die erste war eine C# MVC4-Anwendung), und beide Male hatte ich erhebliche Schwierigkeiten, Authentifizierungs-Frameworks dazu zu bringen, dies ordentlich zu handhaben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X