助手内部的 Sinatra 自定义异常处理不起作用
Sinatra custom Exception handling inside helpers not working
我正在使用 JWT token
创建一个中间件来保护我的 sinatra 后端应用程序中的特定路由。我将中间件代码移到了辅助模块中,这样我就可以将它添加到需要保护的路由中。问题是在引发 Exception
时。它忽略 begin
块中的 rescue
。
当JWT.decode
引发JWT::ExpiredSignature
时,它不会落入rescue JWT::ExpiredSignature
的byebug,甚至不会落入rescue Exception
。它直接进入 sinatra/base 异常处理,如 ERROR Rack::Lint::LintError: Body yielded non-string value [:status, 401]
。是什么导致了这种奇怪的行为?
我的代码:
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
堆栈跟踪:
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 个错误。
- 向
halt
发送了错误的哈希值。这引发了一个例外。正确的形式是
halt 403,{ 'Content-Type' => 'application/json' }, 'The token has expired.'
由于异常层次结构,异常顺序被打乱了:JWT::DecodeError
是比 JWT::ExpiredSignature
更高的层次结构。引发的 JWT::ExpiredSignature
将被 JWT::DecodeError
捕获。最后我将代码更改为:
begin
rescue JWT::ExpiredSignature
halt 403,{ 'Content-Type' => 'application/json' }, 'The token has expired.'
rescue JWT::DecodeError
halt 401,{ 'Content-Type' => 'application/json' }, 'A token must be passed.'
rescue JWT::InvalidIssuerError
halt 403,{ 'Content-Type' => 'application/json' }, 'The token does not have a valid issuer.'
rescue JWT::InvalidIatError
halt 403,{ 'Content-Type' => 'application/json' }, 'The token does not have a valid "issued at" time.'
rescue Pundit::NotAuthorizedError
halt 401,{ 'Content-Type' => 'application/json' }, 'Unauthorized access.'
end
我正在使用 JWT token
创建一个中间件来保护我的 sinatra 后端应用程序中的特定路由。我将中间件代码移到了辅助模块中,这样我就可以将它添加到需要保护的路由中。问题是在引发 Exception
时。它忽略 begin
块中的 rescue
。
当JWT.decode
引发JWT::ExpiredSignature
时,它不会落入rescue JWT::ExpiredSignature
的byebug,甚至不会落入rescue Exception
。它直接进入 sinatra/base 异常处理,如 ERROR Rack::Lint::LintError: Body yielded non-string value [:status, 401]
。是什么导致了这种奇怪的行为?
我的代码:
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
堆栈跟踪:
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 个错误。
- 向
halt
发送了错误的哈希值。这引发了一个例外。正确的形式是
halt 403,{ 'Content-Type' => 'application/json' }, 'The token has expired.'
由于异常层次结构,异常顺序被打乱了:
JWT::DecodeError
是比JWT::ExpiredSignature
更高的层次结构。引发的JWT::ExpiredSignature
将被JWT::DecodeError
捕获。最后我将代码更改为:begin rescue JWT::ExpiredSignature halt 403,{ 'Content-Type' => 'application/json' }, 'The token has expired.' rescue JWT::DecodeError halt 401,{ 'Content-Type' => 'application/json' }, 'A token must be passed.' rescue JWT::InvalidIssuerError halt 403,{ 'Content-Type' => 'application/json' }, 'The token does not have a valid issuer.' rescue JWT::InvalidIatError halt 403,{ 'Content-Type' => 'application/json' }, 'The token does not have a valid "issued at" time.' rescue Pundit::NotAuthorizedError halt 401,{ 'Content-Type' => 'application/json' }, 'Unauthorized access.' end