登陆

一. Sessions 控制器

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

1.首先生成 Sessions 控制器, 以及其中的 new 动作:

$ rails generate controller Sessions new

Users 资源使用特殊的 resources 方法自动获得 REST 式路由, 而 Sessions 资源则只能使用具名路由, 处理发给 /login 地址的 GET 和 POST 请求, 以及发给 /logout 地址的 DELETE 请求, 如下图所示(删除了 rails generate controller 生成的无用路由)。

(1).添加一个资源, 获得会话的标准 REST 式动作 RED

打开文件:config/routes.rb

添加上图中的路由规则之后, 还要创建Session时生成的测试, 使用新的登录路由, 如下图所示:

(2).更新 Sessions 控制器的测试, 使用新的登录路由 GREEN

打开文件:test/controllers/sessions_controller_test.rb

Sessions 资源的路由规则会把 URL 和动作对应起来, 如下图所示:

至此, 我们添加了好几个自定义的具名路由, 现在最好看一下完整的路由列表。我们可以执行 rails routes 命令生成路由列表:

注:你没必要完全理解输出的这些路由。像这样查看路由能对应用支持的动作有个整体认识。

二. 登录表单

登录表单和注册表单之间的主要区别是, 会话不是模型, 因此不能创建类似 @user 的变量。所以, 构建登录表单时, 我们要为 form_for 稍微多提供一些信息。form_for(@user)的作用是让表单向 /users 发起 POST 请求。对会话来说, 我们需要指明资源的名称以及相应的 URL:form_for(:session, url: login_path)知道怎么调用 form_for 之后, 参照注册表单编写登陆表单, 如图所示:

1.登录表单的代码

打开文件:app/views/sessions/new.html.erb

注:为了操作方便, 我们还加入了指向“注册”页面的链接。(导航栏中的“Login”还没填写地址,所以你要在地址栏中输入 /login。稍后会修正这个问题。)

2.页面

三. 查找并验证用户的身份

首先,我们要为 Sessions 控制器编写一个最简单的 create 动作, 以及空的 new 动作和 destroy 动作, 如下图所示:

1.Sessions 控制器中 create 动作的初始版本

打开文件:app/controllers/sessions_controller.rb

create 动作现在只渲染 new 视图, 不过这为后续工作做好了准备。提交 /login 页面中的表单后, 显示的页面如下图所示:

2.查找并验证用户的身份

打开文件:app/controllers/sessions_controller.rb

上图中红框内的第一行使用提交的电子邮件地址从数据库中取出相应的用户。(我们前面说过, 电子邮件地址都是以小写字母形式保存的, 所以这里调用了 downcase 方法, 确保提交有效的地址后能查到相应的记录。)高亮显示的第二行看起来很怪, 但在 Rails 中经常使用:

user && user.authenticate(params[:session][:password])

我们使用 && (逻辑与)检测获取的用户是否有效。因为除了 nil 和 false 之外的所有对象都被视作真值, 所以上面这个语句可能出现的结果如下表所示。从表中可以看出, 当且仅当数据库中存在提交的电子邮件地址, 而且对应的密码和提交的密码匹配时, 这个语句才会返回 true 。

3.渲染闪现消息

布局中已经加入了显示闪现消息的局部视图, 所以无需其他修改, flash[:danger] 消息就能显示出来; 而且因为使用了 Bootstrap 提供的 CSS, 消息的样式也很美观,如图所示::

注:就跟代码中标注的一样, 现在的闪现消息有问题问题在于, 闪现消息在一个请求的生命周期内是持续存在的, 而重新渲染页面(使用 render 方法)与重定向不同, 不算是一次新请求, 所以你会发现这个闪现消息存在的时间比预期的要长很多。

例如:提交无效的登录信息, 然后访问首页, 还会显示这个闪现消息:

4.测试闪现消息

首先, 为应用的登录功能生成一个集成测试文件:

$ rails generate integration_test users_login

(1).捕获继续显示闪现消息的测试 RED

打开文件:test/integration/users_login_test.rb

让上图中的测试通过的方法是, 把 flash 换成特殊的 flash.now 。 flash.now 专门用于在重新渲染的页面中显示闪现消息。与 flash 不同的是, flash.now 中的内容会在下次请求时消失——这正是上图中的测试所需的行为。替换之后, 正确的应用代码如下图所示:

(2).处理登录失败正确的代码 GREEN

四. 登录

Rails 生成器很人性化, 生成 Sessions 控制器时自动生成了一个 Ses-sions 辅助模块。而且, 其中的辅助方法会自动引入 Rails 视图。如果在控制器的基类( ApplicationCon-troller )中引入辅助方法模块, 还可以在控制器中使用,如下图所示:

1.在 Application 控制器中引入 Sessions 辅助模块

打开文件:app/controllers/application_controller.rb

注:做好这些准备工作后,现在可以开始编写代码登入用户了。

2.login 方法

有 Rails 提供的 session 方法协助,登入用户很简单。( session 方法与前面生成的 Sessions 控制器没有关系。)我们可以把 session 视作一个散列, 按照下面的方式赋值:

session[:user_id] = user.id

这么做会在用户的浏览器中创建一个临时 cookie, 内容是加密后的用户 ID。在后续的请求中, 可以使用 ses-sion[:user_id] 取回这个 ID。到后面我们会使用的 cookies 方法创建的是持久 cookie, 而 session 方法创建的是临时会话, 浏览器关闭后立即失效。
我们想在多个不同的地方使用这个登录方式, 所以在 Sessions 辅助模块中定义一个名为 log_in 的方法, 如下图所示:

(1).login 方法

打开文件:app/helpers/sessions_helper.rb

session 方法创建的临时 cookie 会自动加密, 所以上图中的代码是安全的, 攻击者无法使用会话中的信息以该用户的身份登录。不过, 只有 session 方法创建的临时 cookie 是这样, cookies 方法创建的持久cookie 则有可能会受到会话劫持(session hijacking)攻击。所以在以后我们会小心处理存入用户浏览器中的信息。
定义好 log_in 方法后, 我们可以完成 Sessions 控制器中的 create 动作, 登入用户, 然后重定向到用户的资料页面, 如下图所示:

(2).登入用户

打开文件:app/controllers/sessions_controller.rb

注:简洁的重定向代码redirect_to user我们在前面节见过。Rails 会自动转换成用户资料页的地址: user_url(user)。

3.当前用户

把用户 ID 安全地存储在临时会话中之后,在后续的请求中可以将其读取出来。我们要定义一个名为 cur-rent_user 的方法,从数据库中取出用户 ID 对应的用户。 current_user 方法可用于编写类似下面的代码:
  <%= current_user.name %>
或是:
  redirect_to current_user
查找用户的方法之一是使用 find 方法,在用户资料页面就是这么做的:
  User.find(session[:user_id])
前面说过,如果用户 ID 不存在, find 方法会抛出异常。在用户的资料页面可以使用这种行为,因为必须有相应的用户才能显示他的信息。但 session[:user_id] 的值经常是 nil (表示用户未登录),所以我们要使用 create 动作中通过电子邮件地址查找用户的 find_by 方法,通过 id 查找用户:
  User.find_by(id: session[:user_id])
如果 ID 无效, find_by 方法返回 nil ,而不会抛出异常。因此,我们可以按照下面的方式定义 current_user 方法:
  def current_user
    User.find_by(id: session[:user_id])
  end
这样定义应该可以,不过如果在一个页面中多次调用 current_user 方法,会多次查询数据库。所以,我们要使用一种 Ruby 习惯写法,把 User.find_by 的结果存储在实例变量中,只在第一次调用时查询数据库,后续再调用直接返回实例变量中存储的值:
  if @current_user.nil?
    @current_user = User.find_by(id: session[:user_id])
  else
    @current_user
  end
使用前面节中介绍的“或”运算符 || ,可以把这段代码改写成:
  @current_user = @current_user || User.find_by(id: session[:user_id])
User 对象是真值,所以仅当 @current_user 没有赋值时才会执行 find_by 方法。
述代码虽然可以使用,但并不符合 Ruby 的习惯。 @current_user 赋值语句的正确写法是这样:
  @current_user ||= User.find_by(id: session[:user_id])
这种写法用到了容易让人困惑的 ||= (或等)运算符, 参见下图的说明:

(1).在会话中查找当前用户

打开文件:app/helpers/sessions_helper.rb

注:定义好 current_user 方法之后,可以根据用户的登录状态修改应用的布局了。

4.修改布局中的链接

修改网站布局中的链接时要在 ERb 中使用 if-else 语句,用户登录时显示一组链接,未登录时显示另一组链接:
<% if logged_in? %>
# 登录用户看到的链接
<% else %>
# 未登录用户看到的链接
<% end %>
为了编写这种代码,我们要定义 logged_in? 方法,返回布尔值。
用户登录后,当前用户存储在会话中,即 current_user 不是 nil 。检测会话中有没有当前用户要使用“非”运算符。“非”运算符写做 ! ,经常读作“bang”。logged_in? 方法的定义如下图所示:

(1).logged_in? 辅助方法

打开文件:app/helpers/sessions_helper.rb

(2).修改布局中的链接

打开文件:app/views/layouts/_header.html.erb

Ruby Rails学习中:登陆的更多相关文章

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

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

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

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

  3. Ruby Rails学习中:添加安全密码

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

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

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

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

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

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

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

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

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

  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. js off动画事件

    每个假期都过得如此快10月一是2017年最后一个假期.不由感叹时间过得真快.我已上个月离职,一直在家休整,今天得空吧前几天学习的知识真理一下. 今天主要整理关于,offset系列的,动画是咱们全都工作 ...

  2. session机制、cookie机制

    一.Cookie机制 在web程序中是使用HTTP协议来传输数据的,因为http是无状态协议,一旦数据交换完毕,客户端和服务器端的连接就会关闭,再次交换数据需要建立新的连接,所以无法实现会话跟踪,co ...

  3. deep sort

    目录   1. 准备代码与数据 deep_sort开源代码 克隆到本地服务器 git clone https://github.com/nwojke/deep_sort.git 下载MOT16数据集( ...

  4. centos7下面ruby的升级

    背景 在做redis集群时,所需要的使用ruby工具进行操作,发现在线安装的Ruby版本过低,redis支持的版本最少为2.2.2. 在线安装ruby 使用yum在线安装ruby,安装的版本为2.0. ...

  5. BigDecimal常用的加减乘除算法、比较大小、不展示多余的零、保存两位小数点

    项目中涉及到了BigDecimal的加.减.乘.比较大小.精确度的问题.所以在此总结一下,方便以后复习. //加法 BigDecimal coins = new BigDecimal("0& ...

  6. 使用 usb 调试的时候,连接上电脑没反应

    使用 usb 调试的时候,连接上电脑没反应 原因: 手机上没有信任本计算机的授权,请在手机上信任该授权 解决方法: 原因就是手机上会有一个弹话框,让我们信任该计算机,我们才可以进行 usb调试 我们的 ...

  7. Css设置字体

    另,考虑到文件编码问题,在css中推荐使用中文字体的英文表示法,以下附常见中文字体的英文名:Mac OS的一些:Georgia  数字高低起伏Comic Sans MS 好看的英文字体华文细黑:STH ...

  8. Rsa加密类

    需要导入Base64.jar包 import java.io.ByteArrayOutputStream; import java.security.Key; import java.security ...

  9. [Jenkins]局域网内可访问配置方法 -windows

    如果是把jenkins.war放在tomcat中运行,则当jenkins宿主机,启动tomcat服务之后,则直接可以通过局域网访问jenkins  下面这种情况是,直接通过jenkins.exe安装的 ...

  10. 使用IEDriverServer.exe驱动IE,实现自动化测试

    1. 下载IEDriverServer: https://www.nuget.org/packages?q=IEDriver 2. 解压缩得到IEDriverServer.exe和IEDriverSe ...