【Rails学习笔记】登录和退出功能实现
来源:互联网 发布:朗诵配乐知乎 编辑:程序博客网 时间:2024/06/10 01:36
登录相对于前面几章来说是个相对复杂的流程,主要体现在必须记住用户,必须利用加密算法来保证安全性
1.分析下登录流程和需要的工作:
首先表单必须写好,界面部分需要处理用户为登录状态和登出状态两种情况
用户登录成功时,如何让系统记住用户,如何保证安全性
用户退出时,后台如何处理。
另外需要设置好路由规则
如何实现Session:
网络中常见的 session 处理方式有好几种:可以在用户关闭浏览器后清除 session;也可以提供一个“记住我”单选框让用户选择永远保存,直到用户退出后 session 才会失效。 我们选择使用第二种处理方式,即用户登录后,会永久的记住登录状态,直到用户点击“退出”链接之后才清除 session。
很显然,我们可以把 session 视作一个符合 REST 架构的资源,在登录页面中准备一个新的 session,登录后创建这个 session,退出则会销毁 session。不过 session 和 Users 资源有所不同,Users 资源使用数据库(通过 User 模型)持久的存储数据,而 Sessions 资源是利用 cookie 来存储数据的。cookie 是存储在浏览器中的简单文本。实现登录功能基本上就是在实现基于 cookie 的验证机制。
如何记住用户:
因为 HTTP 是无状态的协议,所以如果应用程序需要实现登录功能的话,就要找到一种方法记住用户的状态。维持用户登录状态的方法之一,是使用常规的 Rails session(通过 session 函数),把用户的 id 保存在“记忆权标(remember token)”中:
session[:remember_token] = user.id
session 对象把用户 id 保存在浏览器的 cookie 中,这样在网站的所有页面就都可以使用了。浏览器关闭后,cookie 也随之失效。在网站中的任何页面,只需调用 User.find(session[:remember_token]) 就可以取回用户对象了。Rails 在处理 session 时,会确保安全性。倘若用户企图伪造用户 id,Rails 可以通过每个 session 的 session id 检测到。
根据示例程序的设计目标,我们计划要实现的是持久保存的 session,即使浏览器关闭了,登录状态依旧存在,所以,登入的用户要有一个持久保存的标识符才行。为此,我们要为每个用户生成一个唯一而安全的记忆权标,长期存储,不会随着浏览器的关闭而消失。
如何存储权标?
我们计划在浏览器中存储base64 权标,在数据库中存储加密后的版本。如果要自动登入用户,就可以从 cookie 中取出记忆权标,加密后查询数据库。数据库之所以只保存加密后的权标是因为,即便整个数据库都泄露了,攻击者也无法使用记忆权标登入网站。为了让记忆权标更安全,我们计划每次会话都生成不一样的权标,这样即使会话被劫持了(攻击者偷取 cookie 伪装成某个用户登录),用户下次登录时前一个会话就会失效。
用户注册成功后会自动登录吗:
真实的应用程序都会自动登入刚注册的用户(这样做的一个副作用就是创建了一个新的记忆权标),但是我们不想这么做,我们要用一种更好的方式,确保从一开始用户就有可用的记忆权标。
2.将登录和退出的测试文件authentication_pages_spec列出:
- require 'spec_helper'
- describe "AuthenticationPages" do
- subject { page }
- describe "signin" do
- before {visit signin_path}
- describe "with invalid information" do
- before { click_button "Sign in" }
- it { should have_title('Sign in') }
- it { should have_selector('div.alert.alert-error', text: 'Invalid') }
- describe "after visiting another page" do
- before { click_link "Home" }
- it { should_not have_selector('div.alert.alert-error') }
- end
- end
- describe "with valid information" do
- let(:user) {FactoryGirl.create(:user)}
- before do
- fill_in "Email", with: user.email.upcase
- fill_in "Password", with: user.password
- click_button "Sign in"
- end
- it { should have_title(user.name)}
- it { should have_link('Profile', href: user_path(user)) }
- it { should have_link('Sign out', href: signout_path)}
- it { should_not have_link('Sign in', href: signin_path)}
- describe "followed by signout" do
- before {click_link "Sign out"}
- it { should have_link('Sign in')}
- end
- end
- end
- end
3.用户登录的表单如下:
- <div class="row">
- <div class="span6 offset3">
- <%= form_for(:session, url: sessions_path) do |f| %>
- <%= f.label :email %>
- <%= f.text_field :email %>
- <%= f.label :password %>
- <%= f.password_field :password %>
- <%= f.submit "Sign in", class: "btn btn-large btn-primary" %>
- <% end %>
- <p>New user? <%= link_to "Sign up now!", signup_path %></p>
- </div>
- </div>
4.用户认证需要的方法Sessions_Helps 如下:
- module SessionsHelper
- def sign_in(user)
- remember_token = User.new_remember_token
- cookies.permanent[:remember_token] = remember_token
- user.update_attribute(:remember_token, User.encrypt(remember_token))
- self.current_user = user
- end
- def sign_out
- self.current_user = nil
- cookies.delete(:remember_token)
- end
- def signed_in?
- !current_user.nil?
- end
- def current_user=(user)
- @current_user = user
- end
- def current_user
- remember_token =User.encrypt(cookies[:remember_token])
- @current_user ||= User.find_by(remember_token: remember_token)
- end
- end
对于signin方法有以下说明:
上述代码组找了设定的步骤:首先,创建新权标;随后,把未加密的权标存入浏览器的 cookie;然后,把加密后的权标存入数据库;最后,把制定的用户设为当前登入的用户。
注意,保存记忆权标使用的是 update_attribute 方法,这样可以跳过数据验证更新单个属性。我们必须用这个方法,因为我们无法提供用户的密码及密码确认。
因为数据库中保存的记忆权标是加密的,所以在用来查找用户之前要加密从 cookie 中读取的权标
注意最后一个方法,我们获取当前用户用的是remember_token,但是cookies中存储的是为加密的权标,所以必须加密一次。
对于代码 @current_user ||= User.find_by(remember_token: remember_token)
只有第一次调用时才会去执行后面的部分,这里利用了||运算的短路性质
5.处理登录流程的代码如下:
- class SessionsController < ApplicationController
- def new
- end
- def create
- user = User.find_by(email: params[:session][:email].downcase)
- if user && user.authenticate(params[:session][:password])
- sign_in user
- redirect_to user
- else
- flash.now[:error] = 'Invalid email/password combination' # Not quite right!
- render 'new'
- end
- end
- def destroy
- sign_out
- redirect_to root_path
- end
- end
6.路由规则如下:
- resources :sessions, only: [:new,:create,:destroy]
- #get "users/new"
- root 'static_pages#home'
- match '/signup', to: 'users#new', via:'get'
- match '/signin', to: 'sessions#new', via: 'get'
- match '/signout',to: 'sessions#destroy', via: 'delete'
注意signout对应的Http方法为delete
- class User < ActiveRecord::Base
- before_save { self.email = email.downcase }
- before_create :create_remember_token
- VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
- validates :name, presence: true, length: { maximum: 50}
- validates :email, presence:true,
- format: {with: VALID_EMAIL_REGEX},
- uniqueness: {case_sensitive: false}
- has_secure_password
- validates :password, length: {minimum: 6}
- def User.new_remember_token
- SecureRandom.urlsafe_base64
- end
- def User.encrypt(token)
- Digest::SHA1.hexdigest(token.to_s)
- end
- private
- def create_remember_token
- self.remember_token = User.encrypt(User.new_remember_token)
- end
- end
- 【Rails学习笔记】登录和退出功能实现
- 【Rails学习笔记】登录和退出功能实现
- rails 登录和退出
- Ruby on Rails Tutorial 学习笔记 --第八章 登录和退出
- Ruby on Rails Tutorial 学习笔记 --第八章 登录和退出
- rails 实现登录和注册功能
- 双击退出登录功能的实现
- Rails实现一个blog项目02-登录功能和session的实现
- Rails实现一个blog项目02-登录功能和session的实现
- 退出登录功能
- Android 退出登录功能
- shell退出和退出状态学习笔记
- java web 学习笔记二,注册和登录功能
- ajax实现用户登录注册和退出
- ajax开发:jqueryajax+php的登录与退出功能实现
- ASP.NET MVC4.0 登录、退出功能的简单实现
- 怎样登录和退出Linux系统----Linux学习笔记(1)
- 【学习笔记⑦】登录页面怎样实现验证码登录功能
- Android eclipse 项目依赖
- break跳出循环
- 【Rails学习笔记】用户与微博关联得到的方法
- 【Rails学习笔记】更新、显示和删除用户(涉及分页)(下)
- 【Rails学习笔记】更新、显示和删除用户(涉及分页)(上)
- 【Rails学习笔记】登录和退出功能实现
- 小马哥----高仿小米 note刷机拆机主板图 2015新版机型 主板型号Q8 6582芯片 多图展示 警惕
- 自己动手写一个Struts2
- 工作流——JBPM4.4之HelloWorld示例
- 连线错误
- 央妈不会告诉你的房贷真相:欠银行钱越多越久才越好!
- 【Rails学习笔记】用户注册的流程
- 割点、桥、点双连通、边双连通、强连通 题目
- Cetos编译安装MySQL5.6