`
peryt
  • 浏览: 52490 次
  • 来自: ...
最近访客 更多访客>>
社区版块
存档分类
最新评论
  • waiting: 既然都指定了dataType为'script'那就不必特别在b ...
    jQuery

9.3 sign in success.

阅读更多

1. we will first finish the create action:

 

def create
    user = User.authenticate(params[:session][:email],
                             params[:session][:password])
    if user.nil?
      flash.now[:error] = "Invalid email/password combination."
      @title = "Sign in"
      render 'new'
    else
      sign_in user
      redirect_to user
    end
  end

 

for the below, we will write sign_in method!!!

 

2. start from TDD again!!!

 

describe SessionsController do
  .
  .
  .
  describe "POST 'create'" do
    .
    .
    .
    describe "with valid email and password" do

      before(:each) do
        @user = Factory(:user)
        @attr = { :email => @user.email, :password => @user.password }
      end

      it "should sign the user in" do
        post :create, :session => @attr
        # Fill in with tests for a signed-in user.
      end

      it "should redirect to the user show page" do
        post :create, :session => @attr
        response.should redirect_to(user_path(@user))
      end
    end
  end
end

 

3. in this section, we will need some methods that are need to in both controller and view, 

for view, we can define the method in SessionHelpr, (the method in all helpers are viewable for all views.)

to make controller see this method, we just need to include this module into the controller.

 

class ApplicationController < ActionController::Base
  protect_from_forgery
  include SessionsHelper
end

 

4. session and cookies:

 

 

Because HTTP is a stateless protocol, web applications requiring user signin must implement a way to track each user’s progress from page to page. One technique for maintaining the user signin status is to use a traditional Rails session (via the special session function) to store a remember token equal to the user’s id:

  session[:remember_token] = user.id 
This session object makes the user id available from page to page by storing it in a cookie that expires upon browser close. On each page, the application can simply call
  User.find_by_id(session[:remember_token])
  to retrieve the user. Because of the way Rails handles sessions, this process is secure; if a malicious user tries to spoof the user id, Rails will detect a mismatch based on a special session id generated for each session.

but if we want a permanant token, so that it still work after browser close, then just include user id in the session[:remember_token] is not secure enough, hacker can use user.id to do bad, so we can include user.salt
[user.id, user.salt]
also, a permanant token has another security hole, hacker can get it by inspecting the user browser cookie.
the solution is when user change his password, we change the cookie.

4. now we are ready to implement the sign_in function:

a. we will put place a remember_token as a cookie on the user's browser.
b. app will use this token to find the user record from database when user move from page to page.
c. session will have a current_user.

module SessionHelper
    def sign_in(user)
        cookies.permanent.signed[:remember_token] = [user.id, user.salt]
        self.current_user = user
    end
end
 this part of code revealed the cookies utility supplied by rails.
we can use cookies as if it were a hash.
each element in the cookie is itself a hash of two elements, a value and an optional expired data.

for example:
cookies[:remember_token] = {:value => user.id, :expires => 20.years.from_now.utc}

then we can retrieve the user in this way:
User.find_by_id(cookies[:remember_token])
of course, cookies is not a real hash, since assigning to cookies actually saves a piece of text on the browser, but the beauty of rails is let you forget about the detail, and concentrate on writing the app. 

since just using user.id is not secure, before rails 3, rails use a secure token associated with the user model.
since this is so commen, rails 3 now implements it for us using:
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
 using permanent, cause rails to set the expiration to 20.years.from_now.
and signed makes the cookie secure, so that the user's id is never exposed in the browser.


5. since ruby class is open, rails add many handy method to ruby classes:

1. year.from_now
10.weeks.ago

1. kilobyte ===> 1024
5.megabytes  ====> 5242880
These two are useful when validate the file size of uploads, like images.


6. current user.

now we will look at how to get and set session's current user.

self.current_user = user
since this method is shared by the controller, so the self is the controller object.

the purpose of this line is to create current user, accessible in both controllers and views.

so that we can use <%= current_user.name %> directly in views, and
redirect_to current_user
 

ok TDD first:
it "should sign the user in" do
    post :create, :session => @attr
    controller.current_user.should == @user
    controller.should be_signed_in
end
 
note, 
controller.should be_signed_in 
 is equivalent to
controller.signed_in?.should be_true 
now, we can implement 
self.current_user = user
so we need to define a function, which is an assignment.
def current_user=(user)
	@current_user = user
end
and very natrual, we need to define a get method too:
def current_user
	@current_user
end
if we did this, it is actually equivalent of using:
attr_accessor :current_user
but this is not what we want, because @current_user will disappear for next request which will generate a new controller object.

to fix this, we need to get the user from the token in the cookie in the get method.

def current_user
  @current_user ||= user_from_remember_token
end

private
  def user_from_remember_token
    User.authenticate_with_salt(*remember_token)
  end
  
  def remember_token
    cookies.signed[:remember_token] || [nil, nil]
  end

end
 
a. in this part of code, there is one totally new thing:

*remember_token

the "*" operator, allows us to use a two element array as an argument to a method expectiong two vars.

so 
def foo(bar, baz)
  bar + baz
end

foo(*[1,2])
====> 3
 the reason of using  "*" is that 

authenticate_with_salt in general should accept two arguments.
(id, user_salt)

b. another thing is:
cookies.signed[:remember_token] || [nil, nil]
 
c. the next step is to define the 

authenticate_with_salt method in user.rb

def self.authenticate_with_salt(id, cookie_salt)
	user = find_by_id(id)
	(user && user.salt == cookie_salt) ? user : nil
end
 
(user && user.salt == cookie_salt) ? user : nil

this is very traditional rails code, you really should get used to it!!! 

 

d. we still need to define the signed_in method.

 

def signed_in?

!current_user.nil?

end

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics