路由
在 config/routes
里添加 resources :sessions, only: [:create, :destroy]
只需要这两个 API
Modal
手动创建 app/modal/session.rb
,因为 session 并不需要存在数据库中,内容1 2 3 4 5 6 7 8 9 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
使用命令创建 controller ,bin/rails g controller sessions
,追加两个方法1 2 3 4 5 6 7 8 9 10 11 12 13 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
追加一个自定义的验证,验证邮箱是否存在 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 做了啥
@xxx
def xxx 获取 @xxx 的值
def xxx= 赋值给 @xxx
为了避免出错,完善一下上面的代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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
1 2 3 config.session_store :cookie_store, key: '_monery_session_id' config.middleware .use ActionDispatch::Cookies config.middleware .use config.session_store , config.session_options
增加 session
1 2 3 4 5 6 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
1 get '/current_user_info' , to : 'user#current_user_info'
app/controllers/users_controller.rb
1 2 3 4 5 def current_user_info @user_id = session[:current_user_id ] @user = User .find_by_id @user_id render_resource @user end
记住密码 切记不可把用户密码直接存在 localStorage Rails 官方没有提供最佳实践 因为做法各不相同思路 :
当用户记住密码时,带上相关字段发给服务器
服务器接受以后,下发一个随机数(r)并存在数据库和 session
当过了一段时间,session 过期以后,只剩下 r 被带上发送给服务器
服务器检查 r 是否有效且正确(比如记住密码7天有效),有效中就直接派发一个新的 session,
而数据库需要在 user 表中多增加两个字段 login_token 和 login_token_expired_at 即可
但是如果需要做多个设备的记住密码,上面方案就不够用了,因为新设备登陆,会覆盖数据库,所以我们就需要新增一张 user 的关联表,用来记录多个 login_token
三句话实现注销功能 config/routes,由于 Rails 定义的 destory 方法需要传一个 id,所以我们需要重新定义一个方法
1 delete 'sessions' , to : 'sessions#destroy'
1 2 3 4 def destroy session[:current_user_id ] = nil head 200 end