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

来源:互联网 发布:软件测试技术语言 编辑:程序博客网 时间:2024/06/06 09:52

一,相关资料

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

二,内容及思路

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

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

数据库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() == tokenend  def gen_token    Digest::SHA1.hexdigest(self.email + self.salt)enddef 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  endend

七,一些思考

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

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

0 0