助手内部的Sinatra自定义异常处理不起作用

问题描述

我正在使用JWT token创建一个中间件来保护sinatra后端应用程序中的特定路由。我将中间件代码移至帮助程序模块,以便可以将其添加到需要保护的路由中。问题是当引发Exception时。它会忽略rescue块中的begin

JWT.decode举起JWT::ExpiredSignature时,它不会落在rescue JWT::ExpiredSignature中的byebug中,甚至不会落在rescue Exception中。它直接作为ERROR Rack::Lint::LintError: Body yielded non-string value [:status,401]用于sinatra / base异常处理。是什么导致这种奇怪的行为?

我的代码

require 'sinatra'
require 'jwt'

helpers do 
    def protected!
        begin
          byebug
          bearer = request.env.fetch('HTTP_AUTHORIZATION').slice(7..-1)
          key = OpenSSL::PKey::RSA.new ENV['PUBLIC_KEY']
          payload = JWT.decode bearer,key,true,{ algorithm: 'RS256'}
          claims = payload.first # email,if claims['iss'] == 'user'
            user = User.find_by_email(claims['email'])
            user = User.create({email: claims['email'],role: :user}) if user.nil?
            env[:user] = user
          end
            
        rescue JWT::DecodeError
          halt status: 401,message: 'A token must be passed.'
        rescue JWT::ExpiredSignature 
          byebug # does not get here
          halt status: 403,message: 'The token has expired.'
        rescue JWT::InvalidissuerError
          halt status: 403,message: 'The token does not have a valid issuer.'
        rescue JWT::InvalidIatError
          halt status: 403,message: 'The token does not have a valid "issued at" time.'
        rescue Pundit::NotAuthorizedError
          halt status: 401,message: 'Unauthorized access.'
        rescue Exception
          byebug # not even here
        end
    end    
end



get '/test' do
    protected!
    
    response = { message: 'Hello world'}
    json response
end

stacktrace:

ERROR Rack::Lint::LintError: Body yielded non-string value [:status,401]
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-2.2.2/lib/rack/lint.rb:21:in `assert'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-2.2.2/lib/rack/lint.rb:756:in `block in each'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-2.2.2/lib/rack/body_proxy.rb:41:in `each'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-2.2.2/lib/rack/body_proxy.rb:41:in `method_missing'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-2.2.2/lib/rack/lint.rb:754:in `each'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-2.2.2/lib/rack/body_proxy.rb:41:in `method_missing'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-2.2.2/lib/rack/content_length.rb:26:in `call'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-2.2.2/lib/rack/handler/webrick.rb:95:in `service'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/2.7.0/webrick/httpserver.rb:140:in `service'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/2.7.0/webrick/httpserver.rb:96:in `run'
        /usr/local/var/rbenv/versions/2.7.1/lib/ruby/2.7.0/webrick/server.rb:307:in `block in start_thread'

解决方法

我在代码中有2个错误。

  1. 将错误的哈希发送到halt。这就提出了一个例外。正确的格式是

halt 403,{ 'Content-Type' => 'application/json' },'The token has expired.'

  1. 由于异常层次结构,异常订单被弄乱了:JWT::DecodeError是比JWT::ExpiredSignature更高的层次结构。举起的JWT::ExpiredSignatureJWT::DecodeError抓住。最后,我将代码更改为:

    begin
    
    rescue JWT::ExpiredSignature 
      halt 403,'The token has expired.'
    rescue JWT::DecodeError
      halt 401,'A token must be passed.'
    rescue JWT::InvalidIssuerError
      halt 403,'The token does not have a valid issuer.'
    rescue JWT::InvalidIatError
      halt 403,'The token does not have a valid "issued at" time.'
    rescue Pundit::NotAuthorizedError
      halt 401,'Unauthorized access.'
    
    end