ruby on rails爬坑(一):用户账号密码管理

一,相关资料

惯例,先上文档,BCrypt::Engine主要用于生成随机字符串,hash值。

二,内容及思路

最近的项目需要引进用户管理,主要功能有:
  • 注册新用户
  • 修改密码
  • 登录验证

思路:
手稿,可能不容易理解,下面简单说说

数据库user表结构:
encrypted_password对应上图中得hash_secret
整体流程:核心内容,看这个就够了

数据库里存了encrypted_password,salt两个关键的项。用户注册的时候,先随机生成salt值,再根据用户输入的邮箱(或名字)与salt进行哈希运算,得到encrypted_password,存进数据库里。在用户登录的时候,根据用户输入的email(或名字)在数据库中查询,取得对应的user,将用户输入的password与数据库里的user.salt进行同样的哈希运算,将得到的结果与数据库中存储的user.encrypted_password进行比较,如果相等,证明账号密码正确,允许登录。成功登录后,将数据库中的user.name与user.salt进行哈希运算,得出user_token存成cookie[“user_token”],同时存储cookie[“user_id”]。下次登录时尝试读取该cookie,取得user_id,根据user_id获取数据库中对应的user,user.name与user.salt进行哈希运算,将得到的结果与cookie[“user_token”]比较,如果相同,则登录成功。

三,用户注册

引用上文:用户注册的时候,先随机生成salt值,再根据用户输入的邮箱(或名字)与salt进行哈希运算,得到encrypted_password,存进数据库里。 

不多说,看代码

  def encrypt_password
    if password.present?
      #生成salt
      self.salt = BCrypt::Engine.generate_salt if self.salt.nil?
      #生成encrypted_password
      self.encrypted_password = BCrypt::Engine.hash_secret(password, self.salt)
    end
  end

关于BCrypt::Engine的具体用法,请查阅文档。

四,修改密码

与注册用户一样,只是根据新的password与原来的salt重新生成encrypted_password。
    self.encrypted_password = BCrypt::Engine.hash_secret(new_password, self.salt)

五,登录验证

引用上文:在用户登录的时候,根据用户输入的email(或名字)在数据库中查询,取得对应的user,将用户输入的password与数据库里的user.salt进行同样的哈希运算,将得到的结果与数据库中存储的user.encrypted_password进行比较,如果相等,证明账号密码正确,允许登录。成功登录后,将数据库中的user.name与user.salt进行哈希运算,得出user_token存成cookie["user_token"],同时存储cookie["user_id"]。下次登录时尝试读取该cookie,取得user_id,根据user_id获取数据库中对应的user,user.name与user.salt进行哈希运算,将得到的结果与cookie["user_token"]比较,如果相同,则登录成功。

情况一,首次登录

登录验证:
  def authenticated?(password)
    #验证密码,返回boolen
    BCrypt::Engine.hash_secret(password, self.salt) == self.encrypted_password
  end
生成cookie:
  def gen_token
    #返回生成的user_token
    Digest::SHA1.hexdigest(self.email + self.salt)
  end

  #保存user_id
  cookies[:user] = { value:  user.id, expires: 20.years.from_now.utc }
  #保存user_token
  cookies[:user_token] = { value:  user.gen_token(), expires: 20.years.from_now.utc }

情况二,非首次,自动登录

def valid_token?(token)
    gen_token() == token
end  
def gen_token
    Digest::SHA1.hexdigest(self.email + self.salt)
end
def login?
    if !cookies[:user].present? || !cookies[:user_token].present?
      return false
    else
      begin
        #从cookie中获取user_id
        @current_user = User.find(cookies[:user])
      rescue
        @current_user = nil
        return false
      end
      #利用user_token进行登陆认证
      if !@current_user.valid_token?(cookies[:user_token])
        @current_user = nil
        return false
      end
    end
    return true
  end

六,rails

用户验证应该在程序开始的时候进行。在ApplicationController里面进行用户验证技能满足我们的需求。
class ApplicationController < ActionController::Base
  #设置为before_action在程序运行前被调用
  before_action :require_login
  def require_login
    #根据是否登录跳转到不同页面
    redirect_to :controller => 'login', :action => 'index' if !login?
  end

  #判断以前是否登录过
  def login?
    if !cookies[:user].present? || !cookies[:user_token].present?
      return false
    else
      begin
        @current_user = User.find(cookies[:user])
      rescue
        @current_user = nil
        return false
      end
      if !@current_user.valid_token?(cookies[:user_token])
        @current_user = nil
        return false
      end
    end
    return true
  end
end

七,一些思考

在cookie中为什么不存用户的邮箱而存user_token?

从数据安全的角度想,即使是cookie也不应该以明文的方式存储用户的信息(邮箱)。
更新:其实上面的想法有点幼稚,因为存邮箱根本不可行,在数据库已有user表项的基础上,邮箱与salt进行哈希运算得出的结果根本无法与其他的做比较,除非数据库里面又存了一个user_token用来作比较,这样增加了数据库的负担,不是个好的选择。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章