【问题标题】:Why is current_user called on render in controller?为什么在控制器中渲染时调用 current_user?
【发布时间】:2016-08-25 03:53:36
【问题描述】:

尝试访问我的sessions 控制器的登录方法时出现以下错误:

JWT::DecodeError (Nil JSON web token):

lib/json_web_token.rb:11:in `decode'
app/helpers/sessions_helper.rb:15:in `current_user'
app/controllers/api/sessions_controller.rb:11:in `create'

如果我在我的控制器响应中注释掉我的render json: user,一切都很好,除了我需要与用户响应...为什么到底是通过sessions_controller.rb 的第 11 行调用的current_user 方法.以下是相关代码:

lib/json_web_token.rb

require 'jwt'

class JsonWebToken
  def self.encode(payload, expiration = 24.hours.from_now)
    payload = payload.dup
    payload['exp'] = expiration.to_i
    JWT.encode(payload, Rails.application.secrets.json_web_token_secret)
  end

  def self.decode(token)
    JWT.decode(token, Rails.application.secrets.json_web_token_secret).first
  end
end

sessions_helper.rb

require 'json_web_token'

module SessionsHelper
  def create_session(user)
    session[:user_id] = user.id
  end

  def current_user
    auth_token = request.headers["Authorization"]
    if auth_token
      auth_token = auth_token.split(" ").last
      begin
        decoded_token = JsonWebToken.decode auth_token
      rescue JWT::ExpiredSignature
        return
      end
      @current_user ||= User.find_by(auth_token: auth_token)
    end
  end

  def log_out(user)
    logged_in? ? user.generate_authentication_token! : user.destroy_token!
    auth_token = user.auth_token
    user.update_attribute(:auth_token, auth_token)
  end

  def logged_in?
    current_user.present?
  end

  def authenticate_with_token!
    render json: { errors: "Not authenticated" }, status: :unauthorized unless logged_in?
  end

  def log_in(user)
    create_session(user)
    user.generate_authentication_token!
    user.update_attribute(:auth_token, user.auth_token)
  end

  def authenticate_as_self_or_admin!
    render json: { errors: "Not authorized" }, status: :unauthorized unless is_self? || is_admin?
  end

  def is_self?
    user = User.find(params[:id])
    auth_token = request.headers["Authorization"]
    auth_token = auth_token.split(" ").last if auth_token
    user.auth_token != auth_token
  end

  def is_admin?
    if logged_in? && current_user.authenticate(params[:password])
      current_user.admin
    end
  end
end

sessions_controller.rb

class Api::SessionsController < ApplicationController
  before_action :authenticate_with_token!, only: [:destroy]
  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)

    if user && user.authenticate(params[:session][:password])
      log_in user
      render json: user, status: :created
    else
      render json: user, status: :unprocessable_entity
    end
  end

  def destroy
    log_out current_user
    render status: 204
  end
end

user.rb

require 'json_web_token'

class User < ApplicationRecord
  attr_reader :current_password

  before_save { email.downcase! }
  before_create :generate_authentication_token!
  before_update :reset_confirmed!, :if => :email_changed?
  has_secure_password
  has_many :posts
  has_many :comments
  has_many :votes
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
  validates :username, presence: true, length: { maximum: 24 }, uniqueness: { case_sensitive: false }
  validates :password, presence: true, length: { minimum: 8 }
  validates :auth_token, uniqueness: true

  def generate_authentication_token!
    begin
      self.auth_token = JsonWebToken.encode('id' => self.id, 'username' => self.username, 'email' => self.email, 'bio' => self.bio, 'confirmed' => self.confirmed, 'admin' => self.admin, 'points' => self.points)
    end while self.class.exists?(auth_token: auth_token)
  end

  def destroy_token!
    self.auth_token = nil
  end

  def reset_confirmed!
    self.confirmed = false
  end

  def upvotes
    self.votes.where(polarity: 1)
  end

  def downvotes
    self.votes.where(polarity: -1)
  end

  def update_with_password(user_params)
    current_password = user_params.delete(:current_password)
    user_params[:password] = current_password if user_params[:password].nil?

    if self.authenticate(current_password)
      self.update(user_params)
    else
      self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
      false
    end
  end
end

不,我没有使用设计。 我真的希望我的眼睛在这里只是累了......

【问题讨论】:

  • Rails.application.secrets.json_web_token_secret 定义在哪里?
  • 该秘密在 local_env.yml 中定义,被隐藏的 git 忽略。在 Rails 控制台中,如果我尝试通过 Rails.application.secrets 访问变量,它就可以正常工作。
  • @LouisCruz,你能把代码贴在你的user.rb 模型中吗?
  • before_actions 在ApplicationController 中是什么类型的?
  • 嗯。我猜想某处正在调用logged_in?。将puts caller 添加到您的current_user 方法的顶部,看看这是否提供了任何清晰性?

标签: ruby-on-rails ruby ruby-on-rails-5


【解决方案1】:

事实证明 current_user 实际上被调用了,因为它是 Active Model Serializers 的默认 scope_name。我更改了current_user 方法的名称以避免这种冲突。 Here are the relevant docs.

【讨论】:

  • 或者,您可以将serialization_scope :view_context 添加到您不想调用 current_user 的控制器中。我在登录时尝试创建身份验证令牌时遇到了这个问题。我什至还没来得及让某人登录,它就打电话给current_user
猜你喜欢
  • 1970-01-01
  • 2015-08-13
  • 2017-09-30
  • 2018-06-11
  • 2013-01-27
  • 2015-07-31
  • 2016-03-17
  • 1970-01-01
相关资源
最近更新 更多