Ruby-on-Rails
  Home arrow Ruby-on-Rails arrow Login Systems and More with Ruby on Rails
Dev Articles Forums 
ADO.NET  
Apache  
ASP  
ASP.NET  
C#  
C++  
ColdFusion  
COM/COM+  
Delphi-Kylix  
Design Usability  
Development Cycles  
DHTML  
Embedded Tools  
Flash  
Graphic Design  
HTML  
IIS  
Interviews  
Java  
JavaScript  
MySQL  
Oracle  
Photoshop  
PHP  
Reviews  
Ruby-on-Rails  
SQL  
SQL Server  
Style Sheets  
VB.Net  
Visual Basic  
Web Authoring  
Web Services  
Web Standards  
XML  
Mobile Linux 
App Generation ROI 
IBM® developerWorks 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
RUBY-ON-RAILS

Login Systems and More with Ruby on Rails
By: O'Reilly Media
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 12
    2007-04-05

    Table of Contents:
  • Login Systems and More with Ruby on Rails
  • 15.9 Storing Hashed User Passwords in the Database
  • 15.10 Escaping HTML and JavaScript for Display
  • 15.11 Setting and Retrieving Session Information

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    Login Systems and More with Ruby on Rails


    (Page 1 of 4 )

    In this third article of a six-part series covering web development and Ruby on Rails, you'll learn how to add some very desirable features to your web application. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O'Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

    15.8 Creating a Login System

    Problem

    You want your application to support a login system based on user accounts. Users will log in with a unique username and password, as in most commercial and community web sites.

    Solution

    Create a users table that contains nonnull username and password fields. The SQL to create this table should look something like this MySQL example:

      use mywebapp_development;
      DROP TABLE IF EXISTS `users`;
      CREATE TABLE `users` (
       
    `id` INT(11) NOT NULL AUTO_INCREMENT,
       
    `username` VARCHAR(255) NOT NULL,
        `password` VARCHAR(40) NOT NULL,
        PRIMARY KEY (`id`)
      );

    Enter the main directory of the application and generate a User model corresponding to this table:

      $ ./script/generate model User
            exists  app/models/
            exists  test/unit/
            exists  test/fixtures/
            create  app/models/user.rb
            create  test/unit/user_test.rb 
            create  test/fixtures/users.yml

    Open the generated file app/models/user.rb and edit it to look like this:

      class User < ActiveRecord::Base 
        validates_uniqueness_of :username 
        validates_confirmation_of :password, :on => :create
        validates_length_of :password, :within => 5..40

        # If a user matching the credentials is found, returns the User object.
        # If no matching user is found, returns nil.
        def self.authenticate(user_info)

          find_by_username_and_password(user_info[:username],
                          user_info[:password])
        end
      end

    Now you've got a User class that represents a user account, and a way of validating a username and password against the one stored in the database.

    Discussion

    The simple User model given in the Solution defines a method for doing username/password validation, and some validation rules that impose limitations on the data to be stored in the users table. These validation rules tell User to:

    • Ensure that each username is unique. No two users can have the same username.
    • Ensure that, whenever the password attribute is being set, the password_confirmation attribute has the same value.
    • Ensure that the value of the password attribute is between 5 and 40 characters long.

    Now let's create a controller for this model. It'll have a login action to display the login page, a process_login action to check the username and password, and a logout action to deauthenticate a logged-in session. So that the user accounts will actually do something, well also add a my_account action:

      $ ./script/generate controller user login process_login logout my_account
            exists  app/controllers/
            exists  app/helpers/
            create  app/views/user
            exists  test/functional/
            create  app/controllers/ user_controller.rb
            create  test/functional/ user_controller_test.rb
            create  app/helpers/user_helper.rb
            create  app/views/user/login.rhtml
            create  app/views/user/ process_login.rhtml
            create  app/views/user/logout.rhtml

    Edit app/controllers/user_controller.rb to define the three actions:

      class UserController < ApplicationController
       
    def login
          @user = User.new
          @user.username = params[:username]
        end

        def process_login
         
    if user = User.authenticate(params[:user])
            session[:id] = user.id # Remember the user's id during this session
            redirect_to session[:return_to] || '/'
         
    else
            flash[:error] = 'Invalid login.'
            redirect_to :action => 'login', :username => params[:user][:username]
         
    end
        end

        def logout
          reset_session
          flash[:message] = 'Logged out.'
          redirect_to :action => 'login'
       
    end

        def my_account
        end
      end

    Now for the views. The process_login and logout actions just redirect to other actions, so we only need views for login and my_account. Here's a view for login:

      <!-- app/views/user/login.rhtml -->
      <% if @flash[:message] %><div><%= @flash[:message] %></div><% end %>
      <% if @flash[:error] %><div><%= @flash[:error] %></div><% end %>

      <%= form_tag :action => 'process_login'
    %>
      
    Username: <%= text_field "user", "username" %>&#x00A;
        Password: <%= password_field "user", "password" %>&#x00A;
        <%= submit_tag %>
     
    <%= end_form_tag %>

    The @flash instance variable is a hashlike object used to store temporary messages for the user between actions. When the logout action sets flash[:message] and redirects to login, or process_login sets flash[:error] and redirects to login, the results are available to the view of the login action. Then they get cleared out.

    Here's a very simple view for my_account:

      <!-- app/views/user/my_account.rhtml -->
      <h1>Account Info</h1>

      <p>Your username is <%= User.find(session[:id]).username %>

    Create an entry in the users table, start the server, and you'll find that you can log in from http://localhost:3000/user/login, and view your account information from http:// localhost:3000/user/my_account.

      $ ./script/runner 'User.create(:username => "johndoe", \
                                     :password => "changeme")'

    There's just one missing piece: you can visit the my_account action even if you're not logged in. We don't have a way to close off an action to unauthenticated users. Add the following code to your app/controllers/application.rb file:

      class ApplicationController < ActionController::Base
        before_filter :set_user

      protected
        def set_user
          @user = User.find(session[:id]) if @user.nil? && session[:id]
        end

        def login_required
          return true if @user
          access_denied
          return false
        end

        def access_denied
          session[:return_to] = request.request_uri
          flash[:error] = 'Oops. You need to login before you can view that page.'
          redirect_to :controller => 'user', :action => 'login'
       
    end
      end

    This code defines two filters, set_user and login_required, which you can apply to actions or controllers. The set_user filter is run on every action (because we pass it into before_filter in ApplicationController, the superclass of all our controllers). The set_user method sets the instance variable @user if the user is logged in. Now information about the logged-in user (if any) is available throughout your application. Action methods and views can use this instance variable like any other. This is useful even for actions that don't require login: for instance, your main layout view might display the name of the logged-in user (if any) on every page.

    You can prohibit unauthenticated users from using a specific action or controller by passing the symbol for the login_required method into before_filter. Here's how to protect the my_account action defined in app/controllers/user_controller.rb:

      class UserController < ApplicationController
        before_filter :login_required, :only => :my_account
      end

    Now if you try to use the my_account action without being logged in, you'll be redirected to the login page.

    See Also

    1. Recipe 13.14, "Validating Data with ActiveRecord"
    2. Recipe 15.6, "Integrating a Database with Your Rails Application"
    3. Recipe 15.9, "Storing Hashed User Passwords in the Database"
    4. Recipe 15.11, "Setting and Retrieving Session Information"
    5. Rather than doing this work yourself, you can install the login_generator gem and use its login generator: it will give your application a User model and a controller that implements a password-based authentication system; see http://wiki. rubyonrails.com/rails/pages/LoginGenerator; also see http://wiki.rubyonrails.com/ rails/pages/AvailableGenerators for other generators (including the more sophisticated model_security_generator)

    More Ruby-on-Rails Articles
    More By O'Reilly Media


       · This article is an excerpt from the "Ruby Cookbook," published by O'Reilly. We hope...
     

    Buy this book now. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O'Reilly, 2006; ISBN: 0596523696). Check it out today at your favorite bookstore. Buy this book now.

    RUBY-ON-RAILS ARTICLES

    - Iterating and Incrementing Strings in Ruby
    - Comparing and Manipulating Strings in Ruby
    - Strings in Ruby
    - Ruby On Rails: Making Your First Dynamic Site
    - Ruby on Rails: Beginning Rails
    - Ruby: Modules, Mixins, Fixins, and Rails
    - Controlling Information Access with the Rail...
    - URLs, Filters and the Rails Action Controller
    - Flash and the Rails Action Controller
    - Rails Action Controller
    - Dropping and Sorting with AJAX and script.ac...
    - Drag and Drop with script.aculo.us and Rails
    - Introducing script.aculo.us
    - Ruby Classes and Objects
    - Ruby Loops







    © 2003-2009 by Developer Shed. All rights reserved. DS Cluster 6 Hosted by Hostway
    For more Enterprise Application Development news, visit eWeek