Posterous theme by Cory Watilo

Obsługa użytkowników w RoR (cz 1)

1. Zaczynamy Na początek wygenerujemy sobie wszystkie potrzebne elementy, czyli:
  • model: script/generate model User
  • kontroler oraz potrzebne akcje script/generate controller User singup login logout change_passwd forgot_passwd
  • mailer (przez niego będziemy wysyłać użytkownikowi nowe hasło) script/generate mailer Notifications forgot_password
  • I na koniec tabela w bazie: CREATE TABLE 'users'( 'id' INT UNSIGNED NOT NULL, 'login' VARCHAR (255) NOT NULL, 'password_sha' VARCHAR (255) NOT NULL, 'salt' VARCHAR (255) NOT NULL, 'created_at' DATETIME NOT NULL, 'email' VARCHAR (255) NOT NULL, PRIMARY KEY('id') ) Tutaj można wykazać się inwencją i dodać więcej pół przechowujących np. imię, nazwisko, i inne dane o użytkowniku.
2. Model Przejdźmy do pliku models/user.rb Najpierw przed deklaracją klasy user dodamy linijkę załączającą funkcję hashującą sha1. require 'digest/sha1' Następne linijki będą odpowiadać za walidację pól w bazie (dodajemy to po deklaracji klasy): validates_length_of :login, :within => 5..40 validates_length_of :password, :within => 10..40 validates_presence_of :login, :email, :password, :password_confirmation, :salt validates_uniqueness_of :login, :email validates_confirmation_of :password validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "Błędny email" Wartości te można zmieniać w zależności od potrzeb. W tym przypadku funkcje te sprawdzają czy pole login zawiera łańcuch o odpowiedniej długości (5-40 znaków), analogicznie jest sprawdzane pole password, czy pola login, email, password, password_confirmation i salt są wypełnione także, czy login i email nie istnieją już w innym rekordzie bazy oraz, czy password i password_confirmation mają tą samą wartość. Wyrażenie regularne w ostatniej linijce służy do potwierdzenia poprawnej składni wpisanego adresu email. Jeśli dodałeś do bazy swoje pola, możesz zgodnie z podanymi regułami dodać ich sprawdzanie. Teraz zabezpieczymy niektóre parametry. attr_protected nie pozwala na modyfikowanie wartości pola z parametrów przekazanych konstruktorowi, a attr_accessor zabrania dostępu od oznaczonych pól z poza klasy. attr_protected :id, :salt attr_accessor :password, :password_confirmation Dodamy funkcje generującą losowe hasło (do mechanizmu salt i generowania nowego hasła) i pomocniczą funkcję hashującą def self.random_string(len) #tworzy losowe hasło z liter i cyfr chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a newpass = "" 1.upto(len) { |i| newpass Funkcja przeładowująca przypisanie wartości do pola password (od razu będziemy tworzyć hash hasła): def password=(pass) @password=pass self.salt = User.random_string(10) if !self.salt? self.password_sha = User.encrypt(@password, self.salt) end I na koniec dwie funkcje, już typowo do obsługi użytkowników: def self.authenticate(login, pass) u=find(:first, :conditions=>["login = ?", login]) return nil if u.nil? return u if User.encrypt(pass, u.salt)==u.hashed_password nil end def send_new_password new_pass = User.random_string(10) self.password = self.password_confirmation = new_pass self.save Notifications.deliver_forgot_password( self.email, self.login, new_pass) end Pierwsza funkcja sprawdza, czy podany login i hasło pasują do tych z bazy, a druga na żądanie generuje, zapisuje w bazie i wysyła mailem nowe hasło. To wszystko co trzeba było zrobić w modelu, zajmijmy się teraz kontrolerem. 3. Kontroler Mamy już wygenerowany schemat klasy UserController, więc zabierzemy się za jego wypełnianie. Zaczniemy od akcji signup: def signup @user = User.new(@params[:user]) if request.post? if @user.save session[:user] = User.authenticate(@user.login, @user.password) flash[:notice] = "Rejestracja przebiegła pomyślnie" redirect_to :controller=> "news" else flash[:warning] = "Błąd podczas rejestracji" end end end Można zauważyć przypisanie komunikatu do tablicy flash[]. W następnej części napisze jak można wyświetlić te komunikaty na stronie. Funkcja ta tworzy nowy obiekt User, wypełnia go danymi z formularza i zapisuje w bazie, przy okazji logując użytkownika. Kod akcji login wygląda bardzo podobnie, tylko tutaj nie tworzymy nowego obiektu User: def login if request.post? if session[:user] = User.authenticate(params[:user][:login], params[:user][:password]) flash[:notice] = "Zostałeś zalogowany" redirect_to_stored else flash[:error] = "Błędny login/hasło" end end end Musimy także zadbać o wylogowanie użytkownika: def logout session[:user] = nil flash[:notice] = 'Zostałeś wylogowany' redirect_to :action => 'login' end I ostatnie 2 funkcje służące do wysłania nowego oraz zmienienia hasła: def forgot_passwd if request.post? u= User.find_by_email(params[:user][:email]) if u and u.send_new_password flash[:notice] = "Nowe hasło zostało wysłane emailem" redirect_to :action=>'login' else flash[:error] = "Nie udało się wysłać hasła" end end end def change_passwd @user=session[:user] if request.post? @user.update_attributes(:password=>params[:user][:password], :password_confirmation => params[:user][:password_confirmation]) if @user.save flash[:notice]="Hasło zmienione" end end end Chyba nic nie wymaga tu komentarza ;). Teraz trzeba dodać jeszcze kilka funkcji pomocniczych do pliku application.rb. def login_required if session[:user] return true end flash[:warning]='Zaloguj się, aby kontynuować' session[:return_to]=request.request_uri redirect_to :controller => "user", :action => "login" return false end def redirect_to_stored if return_to = session[:return_to] session[:return_to]=nil redirect_to_url(return_to) else redirect_to :controller=>'twój_kontroller', :action=>'jakaś_akcja' end end Druga funkcja pozwala na odesłanie do strony, która była wyświetlana przed wyświetleniem formularza logowania, może być przydatna także w innych zastosowaniach. Ważne! Pamiętaj aby podać domyślny kontroler i akcję. 4. Widoki Na początek zajmiemy się 'widokiem' mailera, który ukrył się w views/notifications/forgot_password.rhtml – jest to treść maila jaki zostanie wysłany do użytkownika. Dodamy tu prosty tekst, ty możesz wpisać coś ciekawszego: Witaj <%= @login %>! System wygenerował dla Ciebie nowe hasło: <%= @pass %>. Można pokusić się o wysłanie w mailu tylko linka do strony na której będzie podane hasło, ale to już zostawiam wam. Teraz zamieszczą przykładowy kod reszty widoków: signup.rhtml <%= start_form_tag :action=> "signup" %> <%= error_messages_for 'user' %><br/> <label for="user_login">Username</label><br/> <%= text_field "user", "login", :size => 20 %><br/> <label for="user_password">Password</label><br/> <%= password_field "user", "password", :size => 20 %><br/> <label for="user_password_confirmation">Password Confirmation</label><br/> <%= password_field "user", "password_confirmation", :size => 20 %><br/> <label for="user_email">Email</label><br/> <%= text_field "user", "email", :size => 20 %><br/> <%= submit_tag "Zarejestruj się" %> <%= end_form_tag %> login.rhtml <%= start_form_tag :action=> "login" %> <label for="user_login">Nazwa użytkownika:</label><br/> <%= text_field "user", "login", :size => 20 %><br/> <label for="user_password">Hasło:</label><br/> <%= password_field "user", "password", :size => 20 %><br/> <%= submit_tag "Zaloguj się" %> <p> <%= link_to 'Zarejestruj się', {:action => 'signup'}%> | <%= link_to 'Zapomniałeś hasła?', {:action => 'forgot_password'}%> </p> <%= end_form_tag %> forgot_passwd.rhtml <%= start_form_tag :action=>'forgot_password'%> <h3>Forgotten password</h3> Email: <%= text_field "user","email" %><br/> <%= submit_tag 'Prześlij hasło' %> <%= end_form_tag %> change_passwd.rhtml <%= error_messages_for 'user' %> <h3>Change password</h3> <%= start_form_tag :action => 'change_password' %> <label for="user_password">Choose password:</label><br/> <%= password_field "user", "password", :size => 20, :value=>"" %><br/> <label for="user_password_confirmation">Confirm password:</label><br/> <%= password_field "user", "password_confirmation", :size => 20, :value=>"" %><br/> <%= submit_tag "Zmień" %> <%= end_form_tag %> Jeśli chcesz użyć widoku logout.rhtml, możesz w kontrolerze User, przy akcji logout usunąć przekierowanie na akcję login. Oczywiście, to są tylko przykładowe formularze, możesz je w pełni dostosować do wymogów aplikacji, ważne tylko, żeby wszystkie parametry były przekazywane do funkcji. 5. Użycie Użycie tego w praktyce jest bardzo proste. Aby się zarejestrować wystarczy wejść na http://twój-adres-serwera/user/singup . Analogicznie wygląda sprawa z logowaniem. Tak samo proste jest ograniczanie dostępu do niektórych akcji lub całego kontrolera – skorzystamy z funkcji login_required:
  • Ograniczenie pojedynczych akcji: before_filter :login_required, :only=>['akcja1', 'akcja2', ...]
  • Ograniczenie wszystkich akcji oprócz wybranych: before_filter :login_required, :except=>['akcja1', 'akcja2', ...]
Skoro wiemy już jak pozwolić tylko niektórym użytkownikom na dostęp do akcji, to zabezpieczmy nasz kontroler UserController dodając od razu po deklaracji klasy ten kod: before_filter :login_required, :only=>['change_password'] Możesz chcieć wyświetlać niektóre cześci strony tylko wtedy gdy użytkownik jest zalogowany- można to zrobić takim kodem: <% if @session[:user]%> twój kod <% end %> 6. Co dalej? W następnej części najprawdopodobniej napiszę o obsłudze ról użytkowników, dodawaniu captchy do rejestracji i może dodam jeszcze coś o potwierdzaniu rejestracji. Bibliografia: Basic User Authentication in Rails
| Viewed
times
Filed under: