rails中注册与登录的实现步骤

rails注册与登录的实现步骤

注册

  1. 首先肯定是要有User资源的:rails g scaffold Users name email password_digest,其中password_digest是为了对密码做hash保证不会明文存储。

  2. 对于User资源,首先有个show视图来显示个人信息,在Users_controller#show方法只需要根据用户请求的url中的id值在数据库中进行查找,然后将该@user实例变量在show视图中显示即可:def show;@user = User.find params[:id];end

  3. 完成注册表单,首先在Users_controller#new中新建一个@user实例变量,然后在new视图中用form_for辅助方法对@user进行赋值,默认情况是form_for提交的方式是对/users进行post请求,也就是对应了Users_controller#create动作,符合REST架构。

    <%= form_for(@user) do |f| %>
     <%= f.label :name %>
     <%= f.text_field :name %>
    
     <%= f.label :email %>
     <%= f.email_field :email %>
    
     <%= f.label :password %>
     <%= f.password_field :password %>
    
     <%= f.label :password_confirmation, "Confirmation" %>
     <%= f.password_field :password_confirmation %>
    
     <%= f.submit "Create my account" %>
    <% end %>
  4. form_for会将用户的输入封装到params[:user]中,如果直接用这一个参数来create user的话可能会导致用户能自己提交许多不必要的参数,因此需要使用健壮参数,只允许某几个属性用来create user

    def user_params
     params.require(:user).permit(:name, :email, :password,
                                :password_confirmation)
    end
  5. 于是User_controller#create动作的整体方案就是用健壮参数来创建@user,如果@user保存成功,即各属性都通过validation,则转到show视图,如果失败则重新渲染new视图并且显示错误信息。

    class UsersController < ApplicationController
     def create
       @user = User.new(user_params)
       if @user.save
         # 处理注册成功的情况
         redirect_to @user
       else
         render 'new'
       end
     end
    end
  6. 对注册失败的情况进行处理:注册失败的原因一般是邮箱已存在,或者注册的字段validation没通过之类的。在注册失败之后需要重新渲染new视图,并且显示错误信息,错误信息可以存放在partial中,然后在new视图里面渲染,通过 @user.errors.full_messages来访问错误信息。

  7. 如果注册成功了,则将用户信息存库,并且重定向到show视图,并显示欢迎信息。显示欢迎信息可以用flash来实现,flash是一个hash表,键有很多,通常表示操作成功的是:success,表示操作成功,值就是显示的信息。

登录

  1. 登录和退出实际上就是session的创建和销毁的过程,所以首先要创建Session控制器:rails g controller Sessions new,这里只创建new动作是因为只有new动作需要视图,create和destroy都不需要。

  2. Sessions的各个动作分配具名路由,如:get '/login', to: 'sessions#new'postdelete对应createdestroy动作。

  3. new动作对应的视图是一个登录表单,登录表单和注册表单有很大的区别,注册表单使用form_for @user来对/users进行post请求,但是登录表单因为session并不是Activerecord模型,所以要指定资源的名称和请求的urlform_for(:session, url: login_path)

    <%= form_for(:session, url: login_path) do |f| %>
     <%= f.label :email %>
     <%= f.email_field :email %>
    
     <%= f.label :password %>
     <%= f.password_field :password %>
    
     <%= f.submit "Log in" %>
    <% end %>
  4. 之后需要定义Sessions#create动作,与Users#create类似,首先通过params中的参数查询数据库中是否有记录,如果记录存在并且params中的密码能authenticate正确,则登陆成功,否则登录失败

    def create
     user = User.find_by(email: params[:session][:email].downcase)
     if user && user.authenticate(params[:session][:password])
        # 登入用户,然后重定向到用户的资料页面
     else
        # 创建一个错误消息
        render 'new'
     end
    end
  5. 当登录失败时,需要重新渲染new视图,并且显示错误信息,此处的错误信息和之前注册user的不一样,因为UserActiveRecord模型,所以错误信息会保存在@user.errors方法中,Session并不是,所以此处错误信息必须通过flash来传递,因为是登录失败,所以键是:danger :flash.now[:danger] = 'Invalid email/password combination'。一般来说flash在一个请求过程是一直存在的,重新render一个页面还是在原来的请求上下文中,所以flash会一直显示,flash.now则专门用于重新渲染的页面中显示信息。

  6. 对于登入,可以再Session_Helper中定义一个log_in方法,将接受的userid属性存入session:session[:user_id] = user.id `,session方法创建的临时cookie会自动加密 ;于是在sessions_controller#create动作中如果用户存在且通过验证,则调用log_in方法将该用户的id存入session

  7. Session_Helper中也可以定义一个current_user方法,如果session中已经保存了该用户的id,则该用户为当前用户:@current_user ||= User.find_by(id: session[:user_id]),如果@current_user不为空表示用户已登录。

  8. 为了保证用户注册之后要直接登录,所以在users_controller#create动作中,如果@user保存成功,则调用log_in方法保存其id(为了在别的controller中能使用session#helper的方法,需要在application_controllerinclude模块)

  9. 登出对应的Sessions_controller#destroy方法,主要操作就是将session中的user_id删除,并将@current_user清空,之后重定向到root页面。

  10. 以上的实现基于的是session,当用户关闭浏览器后再次访问网站时,就不是登录状态了,并且现在主流应用都有记住用户登录状态的功能,所以需要使用持久性cookie来处理。

  11. 创建cookie的流程:

    1. 生成随机字符串作为remember_token
    2. 把这个token存入浏览器的cookie中,并把过期时间设为未来的某个日期
    3. 在数据库中存储令牌的remember_digest
    4. 在浏览器的 cookie 中存储加密后的用户ID
    5. 如果cookie中有用户的ID,就用这个ID在数据库中查找用户,并且检查cookie中的remember_token和数据库中的remember_digest是否匹配
  12. 首先,rails generate migration add_remember_digest_to_users remember_digest:string,给Users添加remember_digest这一属性。然后使用Ruby 标准库中SecureRandom 模块的urlsafe_base64方法来创建随机字符串。

  13. 由于remember_digest是数据库中真实存在的列,而remember_token其实只是虚拟属性,所以需要给User类创建remember_token属性:attr_accessor :remember_token,类似于has_secure_password,我们需要定义一个remember方法,用于创建用户的remember_token,然后更新数据库中的remember_digest,将remember_token加密后的内容赋给它:

    def remember
      self.remember_token = SecureRandom.urlsafe_base64
      update_attribute(:remember_digest, User.digest(remember_token))
    end
  14. 定义好user.remember方法之后,就能创建持久会话了,即把加密之后的用户idremember_token存入cookie,并且设置过期时间;之后类似于判断passwordpassword_digest是否匹配的authenticate方法,我们要定义判断remember_tokenremember_digest是否匹配的authenticate?方法:

    def authenticated?(remember_token)
      return false if remember_digest.nil?
      BCrypt::Password.new(remember_digest).is_password?(remember_token)
    end
  15. User_helper中定义remember方法,首先调用user.remember来创建remember_tokenremember_digest,然后将useridremember_token都加密过后存入cookies

    def remember(user)
      user.remember
      cookies.permanent.signed[:user_id] = user.id
      cookies.permanent[:remember_token] = user.remember_token
    end
  16. 然后是要修改原有的current_user方法,因为它只会从session中取user_id,应该改为如果session中有user_id的话就从session中取,不然就从cookies中取:

    def current_user
      if (user_id = session[:user_id])
        @current_user ||= User.find_by(id: user_id)
      elsif (user_id = cookies.signed[:user_id])
        user = User.find_by(id: user_id)
        if user && user.authenticated?(cookies[:remember_token])
          log_in user
          @current_user = user
        end
      end
    end
  17. 对于忘记用户的话,只需要给User模型添加forget方法,将数据库中的remember_digest置空,然后再cookies中删除user_idremember_token,最后在log_out方法中先将当前用户忘记登录即可。别忘了在sessions_controller#destroy动作中在log_out之前先要判断是否是登入状态。

  18. 最后一步是在登录页面创建复选框,这一步其实很简单,首先在视图中添加复选框的代码,然后根据复选框四否被选中来决定是remember还是forget

    <%= f.label :remember_me, class: "checkbox inline" do %>
      <%= f.check_box :remember_me %>
      <span>Remember me on this computer</span>
    <% end %>
    
    
    #在sessions_controller#create动作中,log_in之后,重定向之前
    
    params[:session][:remember_me] == '1' ? remember(user) : forget(user)
  19. 完成!

参考书籍:《Ruby on Rails 教程 通过 Rails 学习 Web 开发》,Michael Hartl 著 ,安道 译

猜你喜欢

转载自blog.csdn.net/lengyuedanhen/article/details/65651890