Rails-session

2020-11-03  本文已影响0人  littleyu

路由

Modal

class Session
  include ActiveModel::Model // 不能像 user 那样继承 ActiveRecord,但是我们又需要用到 Rails 提供的 class 里面的便利方法,所以这里需要引入模块 ActiveModel
  attr_accessor :email, :password

  validates_presence_of :email, :password
  validates_format_of :email, with: /.+@.+/, if: :email
  validates_length_of :password, minimum: 6, on: [:create], if: :password

end

Controller

class SessionsController < ApplicationController
  def create
    session = Session.new create_params
    session.validate
    render_resource session
  end
  def destroy

  end
  def create_params
    params.permit(:email, :password)
  end
end

追加一个自定义的验证,验证邮箱是否存在

class Session
  include ActiveModel::Model
  attr_accessor :email, :password, :user

  validates_presence_of :email, :password
  validate :check_email_present, if: :email
  validates_format_of :email, with: /.+@.+/, if: :email
  validates_length_of :password, minimum: 6, if: :password
  validate :check_email_password_match, if: Proc.new {|s| s.email.present? and s.password.present?}

  def check_email_present
    user ||= User.find_by_email email
    if user.nil?
      errors.add :email, :not_present
    end
  end

  def check_email_password_match
    user ||= User.find_by_email email
    if user and not user.authenticate(password)
      errors.add :password, :not_match
    end
  end
end

attr_accessor :xxx 做了啥

为了避免出错,完善一下上面的代码
class Session
  include ActiveModel::Model
  attr_accessor :email, :password, :user

  validates_presence_of :email, :password
  validate :check_email_present, if: :email
  validates_format_of :email, with: /.+@.+/, if: :email
  validates_length_of :password, minimum: 6, if: :password
  validate :check_email_password_match, if: Proc.new {|s| s.email.present? and s.password.present?}

  def check_email_present
    @user ||= User.find_by_email email
    if @user.nil?
      errors.add :email, :not_present
    end
  end

  def check_email_password_match
    @user ||= User.find_by_email email
    if @user and not @user.authenticate(password)
      errors.add :password, :not_match
    end
  end
end

增加 session 中间件

config/application.rb
https://edgeguides.rubyonrails.org/api_app.html

    config.session_store :cookie_store, key: '_monery_session_id'
    config.middleware.use ActionDispatch::Cookies
    config.middleware.use config.session_store, config.session_options

增加 session

def create
    s = Session.new create_params
    s.validate
    render_resource s
    session[:current_user_id] = s.user.id
  end

再给 user 增加一个根据 session_id 获取当前用户的路由
config/routes

get '/current_user_info', to: 'user#current_user_info'

app/controllers/users_controller.rb

  def current_user_info
    @user_id = session[:current_user_id]
    @user = User.find_by_id @user_id
    render_resource @user
  end

记住密码

切记不可把用户密码直接存在 localStorage
Rails 官方没有提供最佳实践
因为做法各不相同
思路

  1. 当用户记住密码时,带上相关字段发给服务器
  2. 服务器接受以后,下发一个随机数(r)并存在数据库和 session
  3. 当过了一段时间,session 过期以后,只剩下 r 被带上发送给服务器
  4. 服务器检查 r 是否有效且正确(比如记住密码7天有效),有效中就直接派发一个新的 session,
  5. 而数据库需要在 user 表中多增加两个字段 login_token 和 login_token_expired_at 即可
  6. 但是如果需要做多个设备的记住密码,上面方案就不够用了,因为新设备登陆,会覆盖数据库,所以我们就需要新增一张 user 的关联表,用来记录多个 login_token

三句话实现注销功能

config/routes,由于 Rails 定义的 destory 方法需要传一个 id,所以我们需要重新定义一个方法

delete 'sessions', to: 'sessions#destroy'
def destroy
    session[:current_user_id] = nil
    head 200
  end
上一篇 下一篇

猜你喜欢

热点阅读