#270 Authentication in Rails 3.1
- Download:
- source codeProject Files in Zip (185 KB)
- mp4Full Size H.264 Video (8.86 MB)
- m4vSmaller H.264 Video (6.56 MB)
- webmFull Size VP8 Video (7.63 MB)
- ogvFull Size Theora Video (13.2 MB)
Rails 3.1 bringt neue Features zur Authentifizierung mit sich. In dieser Folge werden wir euch einige dieser Funktionen vorstellen. Wir verwenden hierfür eine einfache Anwendung. Die Anwendung besteht aus einer Seite, welche momentan für jeden zugänglich ist, der die zugehörige URL kennt.
HTTP Basic Authentication
Wir wollen den Zugang zu der Seite einschränken, so dass sie nur von berechtigten Benutzern betrachtet werden kann. Es ist also notwendig, dass wir einen Nutzer authentifizieren können und die einfachste Möglichkeit ist hier die HTTP Basic Authentication. Rails 3.1 bietet dafür eine neue Variante. Das einzige was wir zu tun haben ist den Controller der Seite zu ergänzen. Wir fügen einen Aufruf der Methode http_basic_authenticate_with
mit den Optionen :name
und :password
hinzu.
class SecretController < ApplicationController http_basic_authenticate_with :name => "frodo", :password => "thering" def index end end
Wenn wir die Authentifizierung auf bestimmte Actions beschränken wollen, können wir die Optionen :only
oder :except
verwenden. Natürlich würden wir in einer echten Anwendung den Benutzernamen und das Passwort nicht als Klartext in den Quellcode schreiben, sondern diese in eine Konfigurationsdatei auslagern. Aber für diese Beispielanwendung können wir sie hier belassen.
Wenn wir jetzt unsere Seite aufrufen, begrüßt uns ein Anmeldefenster, welches nur die eben über http_basic_authenticate_with
vorgegebenen Anmeldedaten akzeptiert.
HTTP Basic ist zwar recht schlicht gehalten, dafür bietet es aber einen schnellen und einfachen Weg Teile einer Anwendung zu schützen und mit Rails 3.1 ist es dazu sogar noch leichter einzusetzen.
secure_password
Manchmal benötigen wir aber ein etwas umfangreicheres Autorisierungssystem, das auch mit mehreren Nutzern umgehen kann. Rails 3.1 hilft uns hier mit secure_password
weiter.
In Folge 250 [watch, read] hatten wir eine Authentifizierung von Grund auf selbst programmiert. Rails 3.1 macht dies jetzt ein wenig einfacher. Um das zu demonstrieren, werden wir jetzt die HTTP Basic Authentifizierung durch unsere eigene ersetzen.
Als erstes generieren wir für unsere Nutzer ein Model User
mit einer Emailadresse und einem Passwort.
$ rails g model user email:string password_digest:string
Anschließend migrieren wir die Datenbank und erzeugen so die Nutzertabelle.
$ rake db:migrate
Wichtig hierbei ist, dass wir das Feld zur Speicherung des Passworts password_digest
nennen. Als nächstes müssen wir im Model User
ein Aufruf von has_secure_password
hinzufügen.
class User < ActiveRecord::Base has_secure_password end
Hierdurch werden Methoden zum Setzen und Überprüfen des eingegebenen Passworts, Validatoren für das Passwort und die Passwortbestätigung (confirmation), sowie weitere Authentifizierungsfunktionalität hinzugefügt. Das Feld password_digest
, dass wir vorhin angelegt haben, dient zur Speicherung des Passworts als Hash.
Standardmäßig existiert noch kein Validator validates_presence_of
der sicherstellt, dass beim Anlegen eines Nutzers überhaupt ein Passwort eingegeben wurde. Also erstellen wir uns als nächstes einen solchen.
class User < ActiveRecord::Base has_secure_password validates_presence_of :password, :on => :create end
In der Regel würden wir auch die Emailadresse validieren. Das sparen wir uns aber hier.
Damit sich Nutzer auch Accounts erstellen können, erzeugen wir als nächsten den Nutzercontroller.
$ rails g controller users
Der Quelltext des Controllers enthält nichts besonderes:
class UsersController < ApplicationController def new @user = User.new end def create @user = User.new(params[:user]) if @user.save redirect_to root_url, :notice => "Signed up!" else render "new" end end end
Die neue View besteht aus einem Formular, mit dem sich die Nutzer registrieren können.
<h1>Sign Up</h1> <%= form_for @user do |f| %> <% if @user.errors.any? %> <div class="error_messages"> <h2>Form is invalid</h2> <ul> <% for message in @user.errors.full_messages %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :email %> <%= f.text_field :email %> </div> <div class="field"> <%= f.label :password %> <%= f.password_field :password %> </div> <div class="field"> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation %> </div> <div class="actions"><%= f.submit %></div> <% end %>
Wenn wir nun versuchen, uns mit zwei nicht identischen Passwörtern zu registrieren, sehen wir die Validatoren in Aktion, welche durch den Aufruf von has_secure_password
bereitgestellt wurden.
Geben wir beide Passwörter korrekt ein, werden wir erfolgreich registriert.
Die Nutzer können sich nun zwar registrieren, aber noch nicht anmelden. Also erzeugen wir als nächstes ein Anmeldeformular zur Eingabe von Emailadresse und Passwort.
<h1>Log in</h1> <%= form_tag sessions_path do %> <div class="field"> <%= label_tag :email %> <%= text_field_tag :email, params[:email] %> </div> <div class="field"> <%= label_tag :password %> <%= password_field_tag :password %> </div> <div class="actions"><%= submit_tag "Log in" %></div> <% end %>
Da wir kein Model bearbeiten, verwenden wir anstelle des form_for
den form_tag
Helfer. Das Formular wird an sessions_path
gesendet. Also benötigen wir als nächstes einen Controller für Sessions.
$ rails g controller sessions
Damit Nutzer sich an- und abmelden können, besitzt der Controller new
, create
und destroy
Actions.
class SessionsController < ApplicationController def new end def create if # authenticated? session[:user_id] = user.id redirect_to root_url, :notice => "Logged in!" else flash.now.alert = "Invalid email or password" render "new" end end def destroy session[:user_id] = nil redirect_to root_url, :notice => "Logged out!" end end
Die create
Action ist noch nicht ganz vollständig. Wir müssen noch prüfen, ob der Nutzer das richtige Passwort eingegeben hat. Hier hilft uns die neue secure_password
Funktionalität aus Rails 3.1.
Als erstes suchen wir uns den zur eingegebenen Emailadresse passenden Nutzer. Dank has_secure_password
verfügen Nutzer nun über die Methode authenticate
. Ihr übergeben wir das Passwort und lassen es gegen das verschlüsselte Passwort in der Datenbank abgleichen. Konnte erst gar kein passender Nutzer gefunden werden, liefert find_by_email
nil
zurück. Deshalb prüfen wir erst einmal, ob der Nutzer überhaupt existiert, bevor wir ihn authentifizieren.
def create user = User.find_by_email(params[:email]) if user && user.authenticate(params[:password]) session[:user_id] = user.id redirect_to root_url, :notice => "Logged in!" else flash.now.alert = "Invalid email or password" render "new" end end
Mehr ist nicht notwendig, um einen Nutzer über secure_password
zu authentifizieren. Zum Testen versuchen wir uns einfach anzumelden. Geben wir ein falsches Passwort ein, werden wir auch nicht authentifiziert.
Geben wir hingegen gültige Daten ein, werden wir angemeldet und zur Startseite weitergeleitet.
Um überall in unserer Anwendung Zugriff auf den aktuellen Nutzer zu haben, erstellen wir die Methode current_user
im ApplicationController
und machen aus ihr eine Helfermethode, sodass sie in den Views verfügbar ist. Die Methode holt sich den aktuell angemeldeten Nutzer aus der Session.
/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base protect_from_forgery private def current_user @current_user ||= User.find(session[:user_id]) if session[:user_id] end helper_method :current_user end
Wirklich schön an dieser Form der Authentifizierung ist das einfache User
Model. Es besteht gerade einmal aus zwei Zeilen Quelltext.
class User < ActiveRecord::Base has_secure_password validates_presence_of :password, :on => :create end
Unsere Version aus Folge 250 hatte im Vergleich ein deutlich komplexeres User
Model. Unabhängig davon ist es noch sinnvoll durch die Zeile attr_accessible
im Nutzermodel den möglichen Zugriff auf email
, password
und password_confirmation
zu beschränken.
class User < ActiveRecord::Base attr_accessible :email, :password, :password_confirmation has_secure_password validates_presence_of :password, :on => :create end
HTTPS hinzufügen
Wenn wir schon eine Authentifizierung in unserer Anwendung anbieten, wollen wir natürlich die Anmeldedaten der Nutzer nicht ungeschützt übertragen. Es ist in diesem Fall eine gute Idee SSL zu verwenden und auf HTTPS zu wechseln. Vor Rails 3.1 musste das manuell oder über ein Plugin erledigt werden. Heute gibt es einen deutlich einfacheren Weg.
Um einen Controller auf HTTPS-Zugriff zu beschränken, brauchen wir nur die Klassenmethode force_ssl
aufzurufen. Wollen wir nur bestimmte Actions schützen, verwenden wir die von before_filter
bekannten Optionen :only
oder :except
.
class ApplicationController < ActionController::Base protect_from_forgery force_ssl private def current_user @current_user ||= User.find(session[:user_id]) if session[:user_id] end helper_method :current_user end
Die Methode force_ssl
erzwingt nur in der Test- oder Produktionsumgebung HTTPS. Wenn wir jetzt unseren Anwendungsserver in der Produktionsumgebung neustarten und die Anmeldeseite aufrufen, werden wir zur SSL-Variante weitergeleitet.
In diesem Fall sehen wir eine Fehlermeldung, da unser Server kein HTTPS unterstützt. Wäre dies der Fall, würden wir hier die sichere Version unserer Seite sehen.
Das war es auch schon wieder mit unserer Folge zum Thema Authentifizierung mit Rails 3.1. Die ganzen vorgestellten Erweiterungen machen es uns sehr viel einfacher eine Authentifizierung zu unseren Railsanwendungen hinzuzufügen.