用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