Rails 5 API自定义身份验证:GET路由上的“ skip_before_action”

时间:2019-05-07 12:14:38

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

我以this系列作为工作包网站的Rails后端的起点。对其进行改编通常非常简单,并且可以按照我想要的去做。一个大问题是,“ index”和“ show”(读操作)应该在没有身份验证的情况下可用,而“ create”,“ update”和“ delete”(写操作)则需要有效的JWT。

按照用于从身份验证中排除注册和登录路由的方法,我尝试了

skip_before_action :authorize_request, only: [:index, :show]

在适当的控制器中。但是,这会导致应用程序崩溃,

NoMethodError (undefined method `works' for nil:NilClass):
app/controllers/works_controller.rb:10:in `index'

问题似乎很明显-如果跳过身份验证操作,则不会实例化该类-至少对于我而言,此修复没有。谁能帮忙吗?

该项目的代码为here

应用程序控制器

class ApplicationController < ActionController::API
  include Response
  include ExceptionHandler

  # called before every action on controllers
  before_action :authorize_request
  attr_reader :current_user

  private

  # Check for valid request token and return user
  def authorize_request
    @current_user = (AuthorizeApiRequest.new(request.headers).call)[:user]
  end
end

“ Works”控制器

class WorksController < ApplicationController

  #skip_before_action :authorize_request, only: [:index, :show]

  before_action :set_work, only: [:show, :update, :destroy]

  # GET /works
  def index
    @works = current_user.works
    json_response(@works)
  end

  # POST /works
  def create
    @work = current_user.works.create!(work_params)
    json_response(@work, :created)
  end

  # GET /works/:id
  def show
    json_response(@work)
  end

  # PUT /works/:id
  def update
    @work.update(work_params)
    head :no_content
  end

  # DELETE /works/:id
  def destroy
    @work.destroy
    head :no_content
  end

  private

  def work_params
    # whitelist params
    params.permit(:title, :nature, :role, :client, :timeframe, :description, :images, :url, :blog_post)
  end

  def set_work
    @work = Work.find(params[:id])
  end
end

“用户”控制器

class UsersController < ApplicationController

  skip_before_action :authorize_request, only: :create

  def create
    user = User.create!(user_params)
    auth_token = AuthenticateUser.new(user.username, user.password).call
    response = { message: Message.account_created, access_token: auth_token }
    json_response(response, :created)
  end

  def show
    json_response(username: current_user.username)
  end

  private

  def user_params
    params.permit(
      :username,
      :password,
      :password_confirmation
    )
  end
end

“身份验证”控制器

class AuthenticationController < ApplicationController

  skip_before_action :authorize_request, only: :authenticate

  # return auth token once user is authenticated
  def authenticate
    auth_token =
      AuthenticateUser.new(auth_params[:username], auth_params[:password]).call
    json_response(access_token: auth_token)
  end

  private

  def auth_params
    params.permit(:username, :password)
  end
end

“ AuthenticateUser”助手

class AuthenticateUser
  def initialize(username, password)
    @username = username
    @password = password
  end

  # Service entry point
  def call
    JsonWebToken.encode(user_id: user.id) if user
  end

  private

  attr_reader :username, :password

  # verify user credentials
  def user
    user = User.find_by(username: username)
    return user if user && user.authenticate(password)
    # raise Authentication error if credentials are invalid
    raise(ExceptionHandler::AuthenticationError, Message.invalid_credentials)
  end
end

“ AuthorizeApiRequest”助手

class AuthorizeApiRequest
  def initialize(headers = {})
    @headers = headers
  end

  # Service entry point - return valid user object
  def call
    {
      user: user
    }
  end

  private

  attr_reader :headers

  def user
    # check if user is in the database
    # memoize user object
    @user ||= User.find(decoded_auth_token[:user_id]) if decoded_auth_token
    # handle user not found
  rescue ActiveRecord::RecordNotFound => e
    # raise custom error
    raise(
      ExceptionHandler::InvalidToken,
      ("#{Message.invalid_token} #{e.message}")
    )
  end

  # decode authentication token
  def decoded_auth_token
    @decoded_auth_token ||= JsonWebToken.decode(http_auth_header)
  end

  # check for token in `Authorization` header
  def http_auth_header
    if headers['Authorization'].present?
      return headers['Authorization'].split(' ').last
    end
      raise(ExceptionHandler::MissingToken, Message.missing_token)
  end
end

“ ExceptionHandler”帮助器

module ExceptionHandler
  extend ActiveSupport::Concern

  # Define custom error subclasses - rescue catches `StandardErrors`
  class AuthenticationError < StandardError; end
  class MissingToken < StandardError; end
  class InvalidToken < StandardError; end

  included do
    # Define custom handlers
    rescue_from ActiveRecord::RecordInvalid, with: :four_twenty_two
    rescue_from ExceptionHandler::AuthenticationError, with: :unauthorized_request
    rescue_from ExceptionHandler::MissingToken, with: :four_twenty_two
    rescue_from ExceptionHandler::InvalidToken, with: :four_twenty_two

    rescue_from ActiveRecord::RecordNotFound do |e|
      json_response({ message: e.message }, :not_found)
    end
  end

  private

  # JSON response with message; Status code 422 - unprocessable entity
  def four_twenty_two(e)
    json_response({ message: e.message }, :unprocessable_entity)
  end

  # JSON response with message; Status code 401 - Unauthorized
  def unauthorized_request(e)
    json_response({ message: e.message }, :unauthorized)
  end
end

1 个答案:

答案 0 :(得分:1)

错误消息指出:

NoMethodError (undefined method `works' for nil:NilClass):
app/controllers/works_controller.rb:10:in `index'

或者要翻译它,在works_controller.rb文件的第10行上,我们在works上调用了一个名为nil的方法,这会引发错误。

假设works_controller的第10行是

@works = current_user.works

然后错误消息告诉我们,我们在nil上调用works,即我们没有current_user

您在分配此代码的地方均无法正常工作,或者您正在登录的这部分代码未登录且未对此进行编码。无论哪种方式,current_user变量都将返回nil,并且不应返回。