Ruby Rails学习中:添加安全密码
接上篇
一. 添加安全密码
我们已经为 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学习中:添加安全密码的更多相关文章
- Ruby Rails学习中:注册表单,注册失败,注册成功
接上篇 一. 注册表单 用户资料页面已经可以访问了, 但内容还不完整.下面我们要为网站创建一个注册表单. 1.使用 form_for 注册页面的核心是一个表单, 用于提交注册相关的信息(名字.电子邮件 ...
- Ruby Rails学习中:调试信息和 Rails 的三种环境,Users 资源,调试器,Gravatar 头像和侧边栏
注册 一.调试信息和 Rails 环境 现在咱们要实现的用户资料页面是我们这个应用中第一个真正意义上的动态页面.虽然视图的代码不会动态改变, 不过每个用户资料页面显示的内容却是从数据库中读取的.添加动 ...
- Ruby Rails学习中:Sass 和 Asset Pipeline,布局中的链接(Rails路由,具名路由),用户注册: 第一步
接上篇: 一.Sass 和 Asset Pipeline Rails 中最有用的功能之一是 Asset Pipeline, 它极大地简化了静态资源文件(CSS.JavaScript 和图像)的生成和管 ...
- Ruby Rails学习中:网站导航,Bootstrap和自定义的CSS,局部视图
添加一些结构 一.网站导航 1.添加一些结构后的网站布局文件 打开文件:app/views/layouts/application.html.erb 简单介绍一下,添加的代码: 我们从上往下看一下这段 ...
- Ruby Rails学习中:User 模型,验证用户数据
用户建模 一. User 模型 实现用户注册功能的第一步是,创建一个数据结构,用于存取用户的信息. 在 Rails 中,数据模型的默认数据结构叫模型(model,MVC 中的 M).Rails 为解决 ...
- Ruby Rails学习中:登陆
登陆 一. Sessions 控制器 登录和退出功能由 Sessions 控制器中相应的 REST 动作处理 : 登录表单在 new 动作中处理, 登录的过程是向 create 动作发送 POST 请 ...
- Ruby Rails学习中:Ruby内置的辅助方法,基础内容回顾补充
一. Ruby内置的辅助方法 1.打开文件:app/views/layouts/application.html.erb(演示应用的网站布局) 来咱把注意力放在圈起来的那一行: 这行代码使用 Rail ...
- Ruby Rails学习中:有点内容的静态页面
续上篇: 一. 有点内容的静态页面 rails new 命令创建了一个布局文件, 不过现在最好不用.我们重命名这个文件: $ mv app/views/layouts/application.html ...
- Ruby Rails学习中:关于测试的补充,MiniTest报告程序,Guard自动测试
一. 关于测试的补充 1.MiniTest报告程序 为了让 Rails 应用的测试适时显示红色和绿色,我建议你在测试辅助文件中加入以下内容: (1).打开文件:test/test_helper.rb ...
随机推荐
- CSS子元素在父元素中水平垂直居中的几种方法
1. 水平居中(margin: auto;)子父元素宽度固定,子元素上设置 margin: auto; 子元素不能设置浮动,否则居中失效. #div1{ width: 300px; height: 3 ...
- solr系列之solr-5.5.5 window单机版默认Jetty安装
Solr5.5.5单机部署 Solr5和Solr4有很大区别,最为明显的就是Solr5已经可以独立部署,从Solr5开始,Solr已经不再以war包形式部署,Solr已经成为了一个独立的java服务端 ...
- Mysql索引查询失效的情况
首先,复习一下索引的创建: 普通的索引的创建: CREATE INDEX (自定义)索引名 ON 数据表(字段); 复合索引的创建: CREATE INDEX (自定义)索引名 ON 数据 ...
- ArcGIS Python 坐标系信息
############################################################# import arcpy import os from arcpy impo ...
- 简易总结react-hook三大基础
react-hook的最重要的三大基础 import { useEffect, useState, useContext } from 'react' 1.每一个简单的一句话总结 useEffect ...
- js 生成树以及关键字搜索生成树
function main(keywords,data){ function fn(arr){ var flag = false; for(var i = 0;i <arr.length;i++ ...
- HTML <a> 标签的 href 属性_定位资源
* 定位资源 ** 如果想要定位资源:定义一个位置 <a name="top">顶部</a> ** 回到这个位置 <a href="#top ...
- java和delphi共用的des加密解密
java: import antlr.StringUtils;import org.jeecgframework.core.util.StringUtil; import java.security. ...
- Nginx日志中的金矿
http://www.infoq.com/cn/articles/nignx-log-goldmine/
- 【笔记】7天玩转容器&CKA管理员实训
第一部分 day1,容器基础知识介绍 安装 apt-get install docker-engine [root@cce-7day-fudonghai-24106 01CNL]# docker -v ...