用rails实现一个简单的用户网站登录模块
>rails -v
Rails 3.2.13
首先创建一个用户信息的model,设置表结构如下(包含昵称,加密密码以及salt值)(*_create_users.rb)
class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.string :hashed_password t.string :salt t.timestamps end end end
接着修改model文件如下(model/user.rb):
class User < ActiveRecord::Base #配置可访问属性 attr_accessible :hashed_password, :name, :salt, :password, :password_confirmation attr_accessor :password #数据验证 validates_presence_of :name validates_uniqueness_of :name attr_accessor :password_confirmation validates_confirmation_of :password #供controller调用的鉴权函数 def self.authenticate(name, password) user = self.find_by_name(name) if user expected_password = encrypted_password(password, user.salt) if user.hashed_password != expected_password user = nil end end user end # 'password' is a virtual attribute def password @password end def password=(pwd) @password = pwd return if pwd.blank? create_new_salt self.hashed_password = User.encrypted_password(self.password, self.salt) end private #计算密码加密结果 def create_new_salt self.salt = self.object_id.to_s + rand.to_s end def self.encrypted_password(password, salt) string_to_hash = password + "wibble" + salt Digest::SHA1.hexdigest(string_to_hash) end end
创建controller(admin),增加login/logout/register/index等action如下,其中index表示受限资源
class AdminController < ApplicationController def register if request.post? @user = User.new(params[:user]) if @user.save session[:user_id] = @user.id uri = session[:original_uri] #跳转到登录之前的页面 flash[:notice] = "User #{@user.name} was successfully created." redirect_to(uri || { :action => "index" }) else redirect_to({ :action => "register" }) end else @user = User.new end end def login if request.post? user = User.authenticate(params[:name], params[:password]) if user session[:user_id] = user.id uri = session[:original_uri] #跳转到登录之前的页面 session[:original_uri] = nil redirect_to(uri || { :action => "index" }) else flash.now[:notice] = "Invalid user/password combination" redirect_to({ :action => "index" }) end end end def logout session[:user_id] = nil flash[:notice] = "Logged out" redirect_to(:action => "login" ) end def index @user_id = session[:user_id] end end
受限资源内容如下:
<h1>Restricted Resources</h1> User ID <%= @user_id %>. <%= link_to 'Logout', :controller => 'admin', :action => 'logout' %>
register的view如下(view/admin/register.html.erb)
<div class="depot-form"> <%= form_for @user, :url => url_for(:controller => 'admin', :action => "register") do |f| %> <div class="field"> <%= f.label :name %>: <%= f.text_field :name %> </div> <div class="field"> <%= f.label :user_password, 'Password' %>: <%= f.password_field :password, :size => 40 %> </div> <div class="field"> <%= f.label :user_password_confirmation, 'Confirm' %>: <%= f.password_field :password_confirmation, :size => 40 %> </div> <div class="actions"> <%= f.submit %> </div> <% end %> </div>
login的view如下(view/admin/login.html.erb)
<div class="depot-form"> <%= form_tag do %> <fieldset> <legend>Please Log In</legend> <div> <label for="name">Name:</label> <%= text_field_tag :name, params[:name] %> </div> <div> <label for="password">Password:</label> <%= password_field_tag :password, params[:password] %> </div> <div> <%= submit_tag "Login" %> </div> </fieldset> <% end %> <%= link_to 'Register', :controller => 'admin', :action => 'register' %> </div>
然后需要配置application_controller.rb,因为所有controller都继承它,在这里可以轻松实现统一的资源限制,我们就不必在每一个controller中单独限制资源了
class ApplicationController < ActionController::Base #配置指定action以外都需要进行用户权限验证 before_filter :authorize, :except => [:login, :register] protect_from_forgery protected #用户权限验证函数,失败则跳转回登录页面 def authorize unless User.find_by_id(session[:user_id]) session[:original_uri] = request.url flash[:notice] = "Please log in" redirect_to :controller => 'admin' , :action => 'login' end end end