Pull to refresh

Авторизация в Ruby on Rails с помощью плагина restful-authentication

Reading time6 min
Views7.4K
Сидя в Москве (как тут у вас многолюдно :), нашел время на написание серии статей по Rails.
Сегодняшняя тема навеяна печальным сообщением. Итак моя задача: показать как можно легко сделать авторизацию в Ruby on Rails. Задача новичков прочитать и попробовать, задача не новичков прочитать и написать как сделать лучше и/или указать на ошибки.

Использованные инструменты:
  • Ruby 1.8.6
  • Mac OS X 10.5.6
  • Rails 2.2.2
  • Mongrel 1.1.5
  • MySQL 5.0.51b

Стандартные операции:

rails auth -d [любимая БД]
cd auth
rm public/index.html
[отредактировать config/database.yml]
rake db:create

Для авторизации в RoR существует куча плагинов. Я буду использовать restful-authentication.
Установка достаточно стандартна:

script/plugin install git://github.com/technoweenie/restful-authentication.git

Теперь нужно создать модель пользователя, в которой будет храниться логин пароль и другие пользователь-related данные. Так же понадобиться два контроллера. Один для регистрации, а другой для логина/логаута. Пусть модель пользователя будет user, а контроллер для логина логаута sessions, тогда выполнив такую команду:

script/generate authenticated user sessions

плагин все сделает за нас. У этого генератора есть несколько опций, например --include-activation, которая добавит активацию пользователя по e-mail. За более подробной информацией рекомендую обратиться к документации.
Генератор создаст два контроллера users и sessions, модель user, миграцию для модели user, записи в config/routes.rb и пару виюх которые скорей всего вы сразу же замените своими.
Роуты выглядят следующим образом:
Copy Source | Copy HTML<br/>ActionController::Routing::Routes.draw do |map|<br/>  map.logout '/logout', :controller => 'sessions', :action => 'destroy'<br/>  map.login '/login', :controller => 'sessions', :action => 'new'<br/>  map.register '/register', :controller => 'users', :action => 'create'<br/>  map.signup '/signup', :controller => 'users', :action => 'new'<br/>  map.resources :users<br/>  map.resource :session<br/>end <br/>

Как видно все довольно хорошо вписывается в REST:

sessions/new [GET] форма логина
sessions [POST] авторизация
sessions [DELETE] logout
users/new [GET] форма регистрации
users [POST] регистрация

Я рекомендую с помощью опций роутов(введенных не так давно, не помню с какой версии) :only и :except, отредактировать запись map.resources :users (map.resources :users, :except => [:edit, :update, :destroy], for example). Например редактирование лучше перенести в отдельный роут: map.resource :profile, (редактирование в таком случае будет выглядить так: /profile/edit), так как скорей всего в вашем приложении одни пользователи других редактировать не должны.
Посмотрим на модель app/models/user.rb:

Copy Source | Copy HTML<br/>require 'digest/sha1'<br/> <br/>class User < ActiveRecord::Base<br/>  include Authentication<br/>  include Authentication::ByPassword<br/>  include Authentication::ByCookieToken<br/> <br/>  validates_presence_of :login<br/>  validates_length_of :login, :within => 3..40<br/>  validates_uniqueness_of :login<br/>  validates_format_of :login, :with => Authentication.login_regex, :message => Authentication.bad_login_message<br/> <br/>  validates_format_of :name, :with => Authentication.name_regex, :message => Authentication.bad_name_message, :allow_nil => true<br/>  validates_length_of :name, :maximum => 100<br/> <br/>  validates_presence_of :email<br/>  validates_length_of :email, :within => 6..100<br/>  validates_uniqueness_of :email<br/>  validates_format_of :email, :with => Authentication.email_regex, :message => Authentication.bad_email_message<br/> <br/>  attr_accessible :login, :email, :name, :password, :password_confirmation<br/> <br/>  def self.authenticate(login, password)<br/>    return nil if login.blank? || password.blank?<br/>    u = find_by_login(login.downcase) # need to get the salt<br/>    u && u.authenticated?(password) ? u : nil<br/>  end<br/> <br/>  def login=(value)<br/>    write_attribute :login, (value ? value.downcase : nil)<br/>  end<br/> <br/>  def email=(value)<br/>    write_attribute :email, (value ? value.downcase : nil)<br/>  end<br/>end <br/>


В принципе ничего интересного разве что, мне не совсем нравится, что логином выступает не e-mail. Чтобы это поправить нужно сдеать следующее: удалить из миграции db/migrate/xxxx_users.rb запись t.column :login, :string, :limit => 40. Модель пользователя сделать такой:
Copy Source | Copy HTML<br/>require 'digest/sha1'<br/> <br/>class User < ActiveRecord::Base<br/>  include Authentication<br/>  include Authentication::ByPassword<br/>  include Authentication::ByCookieToken<br/> <br/>  validates_format_of :name, :with => Authentication.name_regex, :message => Authentication.bad_name_message, :allow_nil => true<br/>  validates_length_of :name, :maximum => 100<br/> <br/>  validates_presence_of :email<br/>  validates_length_of :email, :within => 6..100<br/>  validates_uniqueness_of :email<br/>  validates_format_of :email, :with => Authentication.email_regex, :message => Authentication.bad_email_message<br/> <br/>  attr_accessible :email, :name, :password, :password_confirmation<br/> <br/>  def self.authenticate(login, password)<br/>    return nil if login.blank? || password.blank?<br/>    u = find_by_email(login.downcase) # need to get the salt<br/>    u && u.authenticated?(password) ? u : nil<br/>  end<br/> <br/>  def email=(value)<br/>    write_attribute :email, (value ? value.downcase : nil)<br/>  end<br/>end <br/>


Файл app/views/users/new.html.erb тоже придеться изменить:
<h1>Sign up as a new user</h1><br><% @user.password = @user.password_confirmation = nil %><br><br><%= error_messages_for :user %><br><% form_for :user, :url => users_path do |f| -%><br><br><p><%= label_tag 'email' %><br/><br><%= f.text_field :email %></p><br><br><p><%= label_tag 'password' %><br/><br><%= f.password_field :password %></p><br><br><p><%= label_tag 'password_confirmation', 'Confirm Password' %><br/><br><%= f.password_field :password_confirmation %></p><br><br><p><%= label_tag 'name' %><br/><br><%= f.text_field :name %></p><br><br><p><%= submit_tag 'Sign up' %></p><br><% end -%><br><br>* This source code was highlighted with Source Code Highlighter.

Все готово! Осталось выполнить миграции и запустить проект.
Для полноты можно добавить рутовый роут. Генерация рутового контроллера:

script/generate controller home index

в config/routes.rb добавить:

map.root :controller => 'home'

Добавить лайтаут (app/views/layouts/application.html.erb):
<html><br> <head><br>  <meta http-equiv="Content-type" content="text/html; charset=utf-8" /><br>  <title>auth demo</title><br> </head><br><body><br> <%= render :partial => 'users/user_bar' %><br> <%= yield %><br></body><br></html><br><br>* This source code was highlighted with Source Code Highlighter.


Некоторые пояснения. В контроллере/виюхе «залогиненность» нужно проверять функцией logged_in? (true/false) доступ к пользователю который сейчай авторизован осуществляется через current_user. Готовый проект, в котором есть несколько исправлений не отраженных в статье.
Tags:
Hubs:
+10
Comments16

Articles

Change theme settings