如何阻止未验证邮箱的Devise用户登录?
看起来你的问题核心在于:你使用了自定义的JWT登录逻辑,跳过了Devise自带的身份验证过滤器,因此Devise Confirmable的默认登录限制没有生效。
Devise的confirmable模块确实会给用户模型添加邮箱验证的字段和方法,但它的登录限制是通过Devise自带的authenticate_user!过滤器实现的——而你的API登录用的是自己写的AuthenticationController#login方法,这个方法只检查了邮箱和密码的有效性,完全没验证用户是否已确认邮箱。
解决方案:在自定义登录逻辑中添加确认状态检查
修改你的Api::V1::AuthenticationController里的login方法,加入对用户确认状态的验证:
# POST /auth/login def login @user = User.find_by_email(params[:email]) if @user&.valid_password?(params[:password]) # 新增:检查用户是否已确认邮箱 if @user.confirmed? token = JsonWebToken.encode(user_id: @user.id) time = Time.now + 24.hours.to_i render json: { token: token, exp: time.strftime("%m-%d-%Y %H:%M"), username: @user.username, user_id: @user.id }, status: :ok else # 返回明确的错误提示,引导用户去确认邮箱 render json: { error: '请先确认你的邮箱地址,检查收件箱中的确认链接' }, status: :forbidden end else render json: { error: 'unauthorized' }, status: :unauthorized end end
额外配置(可选)
如果你的应用还有Web端登录(使用Devise默认的session登录),需要确保config/initializers/devise.rb中的Confirmable配置正确,彻底禁止未确认用户登录:
# 禁止未确认用户登录,0天表示立即限制 config.allow_unconfirmed_access_for = 0.days # 确认邮件的有效期(可选) config.confirm_within = 3.days
为什么之前的配置没生效?
你已经正确完成了Confirmable的基础配置:
- 在
user.rb中添加了:confirmable模块 - 更新了迁移文件添加确认相关字段
- 配置了SMTP并成功发送确认邮件
但因为你的API登录是完全自定义的,没有使用Devise提供的sign_in方法或authenticate_user!过滤器,所以Devise的Confirmable自动限制逻辑无法作用于这个登录流程,必须手动加入confirmed?的检查。
补充说明
如果你希望用户在注册后重新发送确认邮件,可以在UsersController中添加一个重新发送的接口,调用Devise提供的send_confirmation_instructions方法:
# 在Api::V1::UsersController中新增方法 def resend_confirmation @user = User.find_by_email(params[:email]) if @user && !@user.confirmed? @user.send_confirmation_instructions render json: { message: '确认邮件已重新发送,请检查你的邮箱' }, status: :ok else render json: { error: '用户不存在或已确认邮箱' }, status: :bad_request end end
记得在路由中添加对应的路由规则。
内容的提问来源于stack exchange,提问作者susie

