Websiteentwicklung: Ruby on Rails: User und Login
In diesem Kapitel werden wir eine App um eine n:m Beziehung erweitern: Ausgangspunkt ist tvtogether, eine App die Serien und Folgen von tvrage.com laden kann.
In diesem Kapitel werden wir als ersten Schritt in die App die Möglichkeit einbauen, dass User sich anmelden, einloggen und ausloggen. Dabei unterstützt uns Rails 3 sehr, wir brauchen nur wenig selbst zu Programmieren.
Diese App werden wir um ein Model 'user' erweitern. Zwischen users und episodes besteht eine n:m Beziehung: eine Person kann mehrere Episoden gesehen haben, eine Episode wurde von mehreren Personen gesehen.
User und Authentisierung
[Bearbeiten]Wir werden ein Model "User" brauchen, das Namen, E-Mail und das gehashte Passwort gespeichert.
Ausserdem werden wir den Hash session verwenden. Dieser wird von Rails zur Verfügung gestellt. Daten die wir hier speichern sind noch vorhanden, wenn vom gleichen Browser auf die nächste Seite aufgerufen wird. (Das wird mittels Cookies erreicht, diese werden automatisch von Rails gesetzt)
User
[Bearbeiten]Dazu gibt es einen Railscast [1] Authentication in Rails 3.1 |
Diese Funktionalität braucht ein zusätzliches gem: Wir fügen in das Gemfile ein:
gem 'bcrypt-ruby'
und führen danach einmal
bundle install
aus. Damit wird das gem installiert. Damit es in der App verwendet wird, müssen wir den Webserver stoppen (mit STRG-C) und neu starten
rails server
Nun legen wir das User-Model an.
rails g model user name:string email:string password_digest:string rake db:migrate
Dem Model fügen wir eine Zeile hinzu:
class User < ActiveRecord::Base has_secure_password validates_presence_of :password, :on => :create attr_accessible :name, :email, :password, :password_confirmation end
Diese eine Zeile fügt sehr viel Automatik ein: Methoden zum Setzen und Verifizieren des eingegeben Passworts, validations. In der Datenbank wird dabei nie das Original-Passwort gespeichert, sondern nur eine gehashte Version. Damit sich neue User anmelden können brauchen wir einen Controller:
rails g controller users
Der Code des Controllers ist kurz:
class UsersController < ApplicationController def new @user = User.new end def create @user = User.new(params[:user]) if @user.save redirect_to root_url, :notice => "Erfolgreich angemeldet!" else render "new" end end end
Und dann brauchen wir noch eine View zur Anmeldung:
<h1>Anmeldung</h1> <%= form_for @user do |f| %> <% if @user.errors.any? %> <div class="error_messages"> <h2>Fehler bei der Anmeldung</h2> <ul> <% for message in @user.errors.full_messages %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :name %><br> <%= f.text_field :name %> </div> <div class="field"> <%= f.label :email %><br> <%= f.text_field :email %> </div> <div class="field"> <%= f.label :password %><br> <%= f.password_field :password %> </div> <div class="field"> <%= f.label :password_confirmation %><br> <%= f.password_field :password_confirmation %> </div> <div class="actions"><%= f.submit %></div> <% end %>
Session, Login, Logout
[Bearbeiten]Wie oben erwähnt ist die session ein Hash in Rails der in jedem Controller zugänglich ist, ähnlich wie params. Wir werden nun aber so tun, als ob sesssion ein model wäre, und eine View und einen Controller dafür anlegen.
Das Login-Formular wird as view sessions/new gespeichert. Dies ist das erste Formular, das nicht mit form_for erstellt wird, sonder mit from_tag. Der Unterschied: form_for ist immer an ein Model gebunden, mit form_tag kann man beliebige Formulare ganz unabhängig von Datenbank und Model, erzeugen.
<h1>Login</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 %>
Zu dieser View gehört ein Controller:
rails g controller sessions
Der Controller hat 3 Actions, eine ist hier noch nicht vollständig implementiert:
class SessionsController < ApplicationController def new end def create # ...... if # ..... session[:user_id] = user.id redirect_to root_url, :notice => "Logged in!" else flash.now.alert = "Falsche E-Mail or falsches Passwort" render "new" end end def destroy session[:user_id] = nil redirect_to root_url, :notice => "Logged out!" end end
Wenn das Login erfolgreich ist speichern wir die user_id in der Session - und nicht mehr.
Wie können wir nun prüfen ob der Login erfolgreich ist? Zuerst holen wir den user mit der Passenden E-Mail aus der Datenbank. Mit der authenticate-Methode des user-Objekts können wir prüfen ob das Passwort passt:
user = User.find_by_email(params[:email]) if user && user.authenticate(params[:password])
Damit wir in jedem Controller und jeder View die Information zur Verfügung haben ob jemand eingeloggt ist legen wir im applications_controller eine helper-Funktion an:
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
Navigation mit Login und Logout
[Bearbeiten]Ähnlich wie hier in Wikibooks soll auch in der App tvtogether oben auf jeder Seite angezeigt werden ob mein eingloggt ist, bzw. Links für Login und Logout angeboten werden.
Das kann man in das Layout einfügen: app/views/layouts/application.html.erb
<nav> <% if current_user.nil? %> <%= link_to "Login", new_session_path %> <% else %> Eingeloggt als <%= current_user.name %>. <%= link_to "Logout", session_path( session ), :method => :delete %> <% end %> </nav>
Quellen
- ↑ Railscast Authentication in Rails 3.1