接上篇

一. 添加安全密码

我们已经为 name 和 email 字段添加了验证规则, 现在要加入用户所需的最后一个常规属性: 安全密码。每个用户都要设置一个密码(还要二次确认), 数据库中则存储经过哈希(hash)加密后的密码(这里的hash是加密算法)。

验证身份的方法是, 获取用户提交的密码, 哈希加密, 再与数据库中存储的密码哈希值对比。如果二者一致, 用户提交的就是正确的密码, 用户的身份也就通过验证了。我们要对比的是密码哈希值, 而不是原始密码, 所以不用在数据库中存储用户的密码。因此, 就算被“脱库”了, 用户的密码仍然安全。

1.计算密码哈希值

我们使用的安全密码机制基本上用一个 Rails 方法即可实现,这个方法是 has_secure_password 。我们要在User 模型中调用这个方法, 如下所示:

在模型中调用这个方法后, 会自动添加如下功能:

• 在数据库中的 password_digest 列存储安全的密码哈希值;
• 获得一对虚拟属性, 18 password 和 password_confirmation ,而且创建户对象时会执行存在性验证和匹配验证;
• 获得 authenticate 方法,如果密码正确,返回对应的用户对象,否则返回 false 。

has_secure_password 发挥功效的唯一要求是, 对应的模型中有个名为 password_digest 的属性。(digest(摘要)是哈希加密算法中的术语。“密码哈希值”和“密码摘要”是一个意思。) 对 User 模型来说,我们要实现下图所示的数据模型。

为了实现上图中的数据模型, 首先要创建一个适当的迁移文件, 添加 password_digest 列。迁移的名字随意, 不过最好以 to_users 结尾, 因为这样 Rails 会自动生成一个向 users 表添加列的迁移。我们把这个迁移命名为 add_password_digest_to_users , 生成迁移的命令如下:

$ rails generate migration add_password_digest_to_users password_digest:string

注:在这个命令中,我们还加入了参数 password_digest:string , 指定想添加的列名和类型。加入 password_digest:string 后, 我们为 Rails 提供了足够的信息, 它会为我们生成一个完整的迁移, 如下图所示:

(1).向 users 表添加 password_digest 列的迁移

打开文件:db/migrate/[timestamp]_add_password_digest_to_users.rb

这个迁移使用 add_column 方法把 password_digest 列添加到 users 表中。执行下述命令在数据库中运行迁移:

$ rails db:migrate

has_secure_password 方法使用先进的 bcrypt 哈希算法计算密码摘要。使用 bcrypt 计算密码哈希值, 就算攻击者设法获得了数据库副本也无法登录网站。为了在演示应用中使用 bcrypt, 我们要把 bcrypt gem 添加到 Gem-file 文件中, 如下图所示:

(2).把 bcrypt gem 添加到 Gemfile 文件中

执行 bundle install 命令:

$ bundle install

2.用户有安全的密码

现在我们已经在 User 模型中添加了 password_digest 属性, 也安装了 bcrypt, 下面可以在 User 模型中添加 has_secure_password 方法了, 如下图所示:

(1).在 User 模型中添加 has_secure_password 方法 RED

打开文件:app/models/user.rb

我们在前面说过, has_secure_password 会在 password 和 password_confirmation 两个虚拟属性上执行验证, 但是现在前面创建 @user 变量时没有设定这两个属性:

所以,为了让测试组件通过, 我们要添加这两个属性, 如下图所示:

(2).添加密码和密码确认 GREEN

打开文件:test/models/user_test.rb

现在测试应该可以通过了:

3.密码的最短长度

一般来说, 最好为密码做些限制, 让别人更难猜测。在 Rails 中增强密码强度有很多方法, 简单起见, 我们只限制最短长度, 而且要求密码不能为空。最短长度为 6 是个不错的选择, 针对这个验证的测试如下图所示:

(1).测试密码的最短长度 RED

打开文件:test/models/user_test.rb

注意这段代码中使用的双重赋值:
@user.password = @user.password_confirmation = "a" * 5
这行代码同时为 password 和 password_confirmation 赋值,值是长度为 5 的字符串,使用字符串连乘创建。
参照 name 属性的 maximum 验证,你或许能猜到限制最短长度所需的代码:
validates :password, length: { minimum: 6 }
在上述代码的基础上,还要加上存在性验证,得出的 User 模型如下图所示。( has_secure_pass-word 方法本身会验证存在性,但是可惜,只会验证有没有密码,因此用户可以创建 “”(6 个空格)这样的无效密码。)

(2).实现安全密码的全部代码 GREEN

打开文件:app/models/user.rb

现在,测试应该可以通过了:

4.创建并验证用户的身份

至此, 基本的 User 模型已经完成了。接下来, 我们要在数据库中创建一个用户, 为以后开发的用户资料页面做准备。同时也看一下在 User 模型中添加 has_secure_password 方法后的效果, 还要用一下重要的 authen-ticate 方法。

因为现在还不能在网页中注册, 我们要在 Rails 控制台中手动创建新用户。为了方便, 我们会使用前面所说的 create 方法。注意, 不要在沙盒模式中启用控制台, 否则结果不会存入数据库。我们要使用 rails console 启动普通的控制台, 然后使用有效的名字和电子邮件地址, 以及密码和密码确认, 创建一个用户:

注:这里, 我出了一个莫名奇妙的BUG(上图是我改完BUG之后才能通过的结果), 先暂停补充一下:

(1).修改BUG

BUG内容为:

You don't have bcrypt installed in your application. 
Please add it to your Gemfile and run bundle install

我Gemfile文件已经添加并按装过bcrypt, 并且装了好几个版本都出现了这个问题

解决步骤:先删掉现有的所有的bcrypt包

gem uninstall bcrypt

查看gem列表是否还存在bcrypt

gem list bcrypt

然后:关掉IDE和正在运行的Ruby Rails项目

再关掉正在使用的终端

然后重新打开终端,定位到自己的项目,添加好Gemfile文件中的包

bundle install

然后就好了,莫名奇妙。。。

(2).我们继续

好了改完BUG, 为了确认结果, 我们使用 SQLite 数据库浏览器查看开发数据库( db/development.sqlite3 )中的 users 表, 如下图所示:

回到控制台,查看 password_digest 属性的值, 由此可以看出 has_secure_password 方法的作用:

注:这是创建用户对象时指定的密码( "foobar" )的哈希值。这个值由 bcrypt 计算得出, 很难反推出原始密码。

前面说过, has_secure_password 方法会自动在对应的模型对象中添加 authenticate 方法。这个方法会计算给定密码的哈希值, 然后与数据库中 password_digest 列的值比较, 以此判断用户提供的密码是否正确。

我们可以在刚创建的用户上试几个错误密码:

我们提供的密码都是错误的, 所以 user.authenticate 返回 false 。如果提供正确的密码, authenticate 方法会返回数据库中对应的用户:

会使用 authenticate 方法把注册的用户登入网站。其实, authenticate 方法返回的用户对象并不重要, 关键是这个值是“真值”。前面说过,  !! 会把对象转换成相应的布尔值。

我们可以使用这种方式确认:

。。。

下班了,先到这把,做个总结:

二. 总结

• 使用迁移可以修改应用的数据模型;
• Active Record 提供了很多创建和处理数据模型的方法;
• 使用 Active Record 验证可以在模型的数据上添加约束条件;
• 常见的验证有存在性、长度和格式;
• 正则表达式晦涩难懂,但功能强大;
• 数据库索引可以提升查询效率,而且能在数据库层实现唯一性约束;
• 可以使用内置的 has_secure_password 方法在模型中添加一个安全的密码。

Ruby Rails学习中:添加安全密码的更多相关文章

  1. Ruby Rails学习中:注册表单,注册失败,注册成功

    接上篇 一. 注册表单 用户资料页面已经可以访问了, 但内容还不完整.下面我们要为网站创建一个注册表单. 1.使用 form_for 注册页面的核心是一个表单, 用于提交注册相关的信息(名字.电子邮件 ...

  2. Ruby Rails学习中:调试信息和 Rails 的三种环境,Users 资源,调试器,Gravatar 头像和侧边栏

    注册 一.调试信息和 Rails 环境 现在咱们要实现的用户资料页面是我们这个应用中第一个真正意义上的动态页面.虽然视图的代码不会动态改变, 不过每个用户资料页面显示的内容却是从数据库中读取的.添加动 ...

  3. Ruby Rails学习中:Sass 和 Asset Pipeline,布局中的链接(Rails路由,具名路由),用户注册: 第一步

    接上篇: 一.Sass 和 Asset Pipeline Rails 中最有用的功能之一是 Asset Pipeline, 它极大地简化了静态资源文件(CSS.JavaScript 和图像)的生成和管 ...

  4. Ruby Rails学习中:网站导航,Bootstrap和自定义的CSS,局部视图

    添加一些结构 一.网站导航 1.添加一些结构后的网站布局文件 打开文件:app/views/layouts/application.html.erb 简单介绍一下,添加的代码: 我们从上往下看一下这段 ...

  5. Ruby Rails学习中:User 模型,验证用户数据

    用户建模 一. User 模型 实现用户注册功能的第一步是,创建一个数据结构,用于存取用户的信息. 在 Rails 中,数据模型的默认数据结构叫模型(model,MVC 中的 M).Rails 为解决 ...

  6. Ruby Rails学习中:登陆

    登陆 一. Sessions 控制器 登录和退出功能由 Sessions 控制器中相应的 REST 动作处理 : 登录表单在 new 动作中处理, 登录的过程是向 create 动作发送 POST 请 ...

  7. Ruby Rails学习中:Ruby内置的辅助方法,基础内容回顾补充

    一. Ruby内置的辅助方法 1.打开文件:app/views/layouts/application.html.erb(演示应用的网站布局) 来咱把注意力放在圈起来的那一行: 这行代码使用 Rail ...

  8. Ruby Rails学习中:有点内容的静态页面

    续上篇: 一. 有点内容的静态页面 rails new 命令创建了一个布局文件, 不过现在最好不用.我们重命名这个文件: $ mv app/views/layouts/application.html ...

  9. Ruby Rails学习中:关于测试的补充,MiniTest报告程序,Guard自动测试

    一. 关于测试的补充 1.MiniTest报告程序 为了让 Rails 应用的测试适时显示红色和绿色,我建议你在测试辅助文件中加入以下内容: (1).打开文件:test/test_helper.rb ...

随机推荐

  1. Leetcode题目160.相交链表(简单)

    题目描述 编写一个程序,找到两个单链表相交的起始节点. 如下面的两个链表: 在节点 c1 开始相交. 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = ...

  2. cs231n lecture2 image classification

    1.图片分类若采用最近邻法: import numpy as np class NearestNeighbor: def _init_(self): pass def train(self, X, y ...

  3. linux内核中的subsys_initcall是干什么的?

    注意:使用的内核源码版本为5.1.3 1. subsys_initcall长什么样子? 它其实是个宏定义,定义如下: #define subsys_initcall(fn)     __define_ ...

  4. CentOS 7 应用

    为方便以下CentOS7简称C7,CentOS6简称C6 优化 1.安装常用功能 yum -y install bash-completion lrzsz telnet tree vim wget n ...

  5. nfs服务共享,解决文件没有权限访问问题

    最近在了解一些服务权限的设置,突然就被这个nfs服务的权限给绊住了.当你挂载上服务器上的共享目录 时,却无法访问里面的一些内容.内心满满的忧桑...经过努力奋斗几分钟终于搞明白了. 无法访问的原因:因 ...

  6. C++继承中的属性

    class A { public: int a; A() { } void print() { cout<<a; } }; class B:public A { public: int a ...

  7. 微信小程序添加卡券到微信卡包,使用wx.addCard()方法传参及整体流程

    一.准备: 1.经微信认证过的微信公众号. 2.经微信认证过的微信小程序号. 先来看看微信小程序官方的文档,https://developers.weixin.qq.com/miniprogram/d ...

  8. SQL引擎及事务支持

    查看当前表引擎 SHOW CREATE TABLE table_name MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.MyISAM类型的表强调的是性能,其执行数度比InnoDB类 ...

  9. mdk3 工具使用-表白神器

    一:在root目录下新建个txt文档,用vim编辑文档,写几段暧昧的话 二:终端执行命令: 开启网卡监听模式 airmon-ng start wlan0 开启无线广播 mdk3 wlan0mon b ...

  10. LCA -cogs2098 [SYOI 2015] Asm.Def的病毒

    题目链接:http://cogs.pro:8081/cogs/problem/problem.php?pid=vQXmxVaPU [题目描述] “这就是我们最新研制的,世界上第一种可持久化动态计算机病 ...