So you want to do authentication in Rails, but don’t want to find out which of the twentythousand Authentication systems might work for you (hint: try Acts_as_authenticated); here’s a quick rundown on how authentication basically works.
Method One: This may be vulnerable to SQL injection in Person.find(). See method two, below.
The ApplicationController contains our authentication system gatekeeper method.
class ApplicationController < ActionController::Base
protected
def authenticate
unless session["person"]
redirect_to :controller => "login"
return false
end
end
end
The controller that needs protection:
class WeblogController < ApplicationController
before_filter :authenticate, :only => [ :index ]
def index
# show the secret stuff
end
end
The controller that does login:
class LoginController < ApplicationController
def index
# show login screen
end
def login
if session["person"] = Person.authenticate(params["name"], params["password"])
redirect_to :controller => "weblog"
else
redirect_to :action => "index"
end
end
end
The model that does authentication:
class Person < ActiveRecord::Base
def self.authenticate(name, password)
find(:first,
:conditions => [ "name = '%s' AND password = '%s'", name, password ]
)
end
end
Ka-ching!
Method Two: Instead of using
find(:first, :conditions => [ "name = '%s' AND password = '%s'", name, password ] )try using
find(:first, :conditions => { :name => n, :password => p }Also, I want to keep server[‘REQUEST_URI’] the same through the login process. In other words, instead of redirect_to, I want to render a form requesting a username and password at whatever URL was requested. Then I don’t have to save/restore the URL in the session, and some browsers won’t even add the login page to the history.
So here’s an even shorter, and hopefully a more secure way of doing the same thing:
class ApplicationController < ActionController::Base
protected
def login_required
session[:auth] ? yield : render(:template => 'login/index')
end
end
Please explain how the check method is used
class LoginController < ApplicationController
skip_filter :login_required
def logout(msg = "")
reset_session
flash[:notice] = msg if msg.length
redirect_to '/'
end
def check
if not request.post?
logout("Invalid request.")
elsif session[:auth] = Person.find(:first, :conditions => { :name => params[:login][:n], :password => params[:login][:p] })
redirect_to :back
else
logout("Your user name and password are invalid.")
end
end
end
The file app/helpers/login_helper.rb is also there (not mentioned above!) but only has two lines, and is not very interesting:
module LoginHelper
end
Controllers you want to protect should have around_filter :login_required or you can put around_filter :login_required in class ApplicationController and then un-protect controllers just like LoginController does, with
skip_filter :login_required
Only 26 lines of code. (Less code can lead to fewer bugs!) And you do still need views/login/index.rhtml with a form in it to send when the user is not logged in yet.
That would look like this: Login Please
<= error_messages_for :login >
< form_for(:login) do |f| >
Username: <= f.text_field :n >
Password: <= f.text_field :p >
<= f.submit “Login”>
< end >