You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Rails Devise中实现用户成功登录后记录最后登录时间

问题描述

我开发了一个Rails应用,用Devise实现用户注册和登录功能。现在需要给User模型存储最后一次成功登录的时间。

原本打算重写Session控制器的create方法,但这样会在密码输入错误时也记录错误的时间戳。想问:

  1. 有没有办法让Rails在成功登录后调用特定操作或代码块?能不能直接加after_action?
  2. 是否必须在当前SessionsController的after_sign_in_path_for方法里加代码?有没有办法通过修改或新增Model来实现?

附当前Users::SessionsController代码:

class Users::SessionsController < Devise::SessionsController
  skip_before_action :verify_authenticity_token, only: :create

  def new
    login = I18n.t('header.menu.not_logged_in.login')
    @title = login
    @meta_description = login
    @noindex = true
    super
  end

  def create
    super
  end

  protected

  def after_sign_in_path_for(resource_or_scope)
    set_location_path(resource_or_scope) || stored_location_for(resource_or_scope) || signed_in_root_path(resource_or_scope)
  end

  private

  def set_location_path resource
    if resource&.fitaf_employee?
      admin_root_path
    elsif resource&.non_fitaf_employee?
      if current_user.accounting_company.present?
        home_company_path(company_id: current_user.accounting_company.id, id: current_user.accounting_company.id)
      else
        home_companies_path
      end
    end
  end
end
解决方案

方法1:利用Devise内置的登录回调(推荐)

Devise提供了专门的成功登录触发机制,完全避开登录失败的场景。两种实现方式:

  • 如果你的User模型已经启用trackable模块(Devise默认可选),它会自动记录last_sign_in_at、current_sign_in_at等字段,不需要额外代码,直接用就行。
  • 如果没启用trackable,可以手动添加字段和回调:

首先生成迁移添加字段:

rails generate migration add_last_sign_in_at_to_users last_sign_in_at:datetime
rails db:migrate

然后在app/models/user.rb里添加登录后回调:

class User < ApplicationRecord
  # 你的Devise配置...
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  # 成功登录后更新时间戳
  after_login :update_last_sign_in_at

  private

  def update_last_sign_in_at
    update_columns(last_sign_in_at: Time.current)
    # 用update_columns跳过验证和其他回调,提升效率
  end
end

方法2:在after_sign_in_path_for中添加逻辑

这个方法只会在成功登录后执行,完全符合需求,直接在现有控制器代码里修改:

protected

def after_sign_in_path_for(resource_or_scope)
  # 先处理时间戳更新
  resource = resource_or_scope.is_a?(Symbol) ? send("current_#{resource_or_scope}") : resource_or_scope
  resource.update_columns(last_sign_in_at: Time.current) if resource.is_a?(User)
  
  # 保留原有的路径跳转逻辑
  set_location_path(resource_or_scope) || stored_location_for(resource_or_scope) || signed_in_root_path(resource_or_scope)
end

关于after_action的疑问

不建议用after_action :create,因为不管登录成功还是失败,create方法执行完都会触发这个回调,依然会记录错误的时间戳,不符合需求。


内容的提问来源于stack exchange,提问作者Julian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 09:12:34