JWTのRubyでの使用概要とSinatraフレームワークの解読とインタフェースの構築
14760 ワード
JSON Web TokenはJSON Web Tokenの基本原理を紹介し,次にこの文章はRuby分析と結びつけて実際のプロジェクトでの使用をまとめた.Rubyには対応するバージョンのGem ruby-jwtがあり、この論文ではJWTのRubyプログラミング言語での使用を議論し、Sinatraフレームワークを簡単に解析します.
ruby-jwt紹介
ruby-jwtはRFC 7519 OAuth JSON Web Token規格を実現し、文書と結びつけてRubyでJWTをどのように使用するかをまとめた.
暗号化とチェック
jwtでは通常、RSAおよびHMACにそれぞれ対応する2つの暗号化方式RS 256およびHS 256を使用する.サードパーティにHTTP APIインタフェースを提供する場合、通常はJWTを使用して明文の検証と暗号化の特性を使用します.RS 256を使用する場合は両方とも相手に自分のOpenSSL公開鍵を提供する必要があり、HS 256を使用する場合は同じ鍵列を使用することを協議する.
RS 256を使用するプロセスは:甲のサーバーは自分の秘密鍵でデータに対して署名を行って、それから乙は甲の提供した公開鍵を使って署名を検査してそして明文を解析してそして自分の秘密鍵を使って署名をプラスして乙のサーバーの明文に返して、甲のサーバーは更に乙の提供した公開鍵を使って署名を解除します.
HS 256を使用するプロセスは、双方が合意した鍵で転送する必要がある明文を復号し、相手から送信されたJWT token ruby-jwt暗号化方法のソースコードを検証し、署名することである.
encodeとdecodeのソースコードを組み合わせて、署名と検証の例を学びます.
いくつかのよく使われるClaim Expiration Time Claim(exp)失効時間 Issued At Claim(iat)は、このJWT tokenの発行時間 を表す JWT ID Claim JWT token一意ID
Sinatra原理の解釈、使用
このセクションでは、Sinatraの基本的な実装原理と、Sinatra拡張原理を実装する方法について説明します.そして完全なSinatraアプリケーションを構築します.仮定したシナリオは,我々がサードパーティにHTTP APIを提供し,JWTの2つの特性を用いた:明文暗号化とチェックアウトである.実際の開発では双方ともOpenSSL生成の公開鍵を提供する必要がある.
Sinatraの基本原理
記述規則 top-level DSLはSinatraにおけるルーティング(Railsにおけるcontroller) を指す. classic style文で古典的なスタイルを記述するSinatraアプリケーション module style文では、モジュール化されたスタイルのSinatraアプリケーション として記述されている. Sinatra.helpersが実装した拡張子は、「ヘルプメソッド拡張」 です. Sinatra.registerが実現する拡張は「ルーティング拡張」 である.
クラシックスタイルとモジュール化スタイルのSinatra応用
通常、Sinatraを使用してプロジェクトを構築するには、クラシックスタイルとモジュール化スタイルの2つの方法があります.
1つは古典的なスタイル(classic style)です.このモードは通常、HTTPリクエストを受け入れるすべてのルーティングが1つのファイルにのみ適用されます(app.rb).app.rbファイルには
もう1つは,モジュール化スタイル(Modular style)というモデルも本論文で主に紹介する方法であり,通常,クラスRailsのMVC構造に拡張できる大型Rack−baseアプリケーションに適用され,多重化可能なRackミドルウェアを記述する.このような足場により,比較的複雑なインタフェースアプリケーションを実現できる.
モジュール化されたスタイルを実現するSinatraアプリケーションには,Sinatra::BaseとSinatra::Applicationの2つの方式を継承し,両者の違いを説明する.
Sinatra::ApplicationとSinatra::Baseの関係
通常、モジュール化されたスタイルのSinatraは
Applicationクラスのコメントにこんな一節がありました
アプリケーションクラスを継承するとSinatraのデフォルトの設定、ルーティングがオンになることを説明するのに十分です.
クラシックスタイルとモジュール化スタイルSinatraアプリケーションの関係
コードと組み合わせて、上述した古典的なスタイルを実現するSinatraアプリケーションは、
クラシックスタイルのSinatra応用本質も最も基本的なモジュール化スタイルであり、その実現原理は
各モジュラースタイルのSinatraは
Sinatra拡張
エレガントなコードは多重化とモジュール化にこだわり,よく使われる機能モジュールを抽象化してSinatra拡張を行う.この章では、Sinatra拡張を実現するために必要な3つの点について引き続き議論します.Sinatra.register、Sinatra.helpers、Module.registeredです.
拡張は、通常、
Extending The DSL (class) Context with Sinatra.register
Sinatra.registerによって実現される拡張は、
ルーティング拡張は、Sinatraアプリケーションによって使用されるために、Sinatraモジュールに登録され、
Extending The Request Context with Sinatra.helpers
ヘルプメソッドモジュールは、ルーティング、ビュー、またはヘルプメソッドの追加メソッドに拡張されます.次の例では,hヘルプメソッドというメソッドを実現する.
上記の例では、
クラシックスタイルで拡張方法を引用するには、
しかし、
registeredモジュールメソッドを使用してルーティング拡張を構成する
モジュール内で
簡単なログイン検証拡張例を実現
最後に書く
Railsフレームワークを使いたくないRuby開発者にとって、Sinatraは軽量でプロジェクトを迅速に開発できる良い代替フレームワークです.筆者はこれを用いて,APIと各種H 5フロントエンドページのレンダリングを提供する支払いチャネル中台プロジェクトを開発した.この文章は3ヶ月前から書き始めましたが、遅延と惰性の断続的な続編のため、書くたびに以前所蔵していた文章をもう一度見て考えを整理しなければなりません.異常に時間と苦痛を浪費しています.自分に一つの説明をして、一つのプロジェクトをまとめて反省しなければ、間もなく忘れてしまうと自分に言った.また最近はGoを勉強しているので、これからもっとRuby開発を使うことになると思います.
リンクを参照して読む価値のある記事 writing sinatra extensionsはSinatra拡張の素晴らしい文章を書くことを教えてくれました.まず, を記述する方法について説明します. Sinatra Best Practices:Part Two sinatraのrspec の構成方法プロジェクトプロファイル についてプロジェクトファイルロード についてプロジェクトディレクトリ構造組織 について Sinatraでrakeとactive record を使用プロジェクトファイル構造構築詳細ステップ Sinatra applications with RSpec ネーミングスペース 完全Sinatraプロジェクトdemo
ruby-jwt紹介
ruby-jwtはRFC 7519 OAuth JSON Web Token規格を実現し、文書と結びつけてRubyでJWTをどのように使用するかをまとめた.
暗号化とチェック
jwtでは通常、RSAおよびHMACにそれぞれ対応する2つの暗号化方式RS 256およびHS 256を使用する.サードパーティにHTTP APIインタフェースを提供する場合、通常はJWTを使用して明文の検証と暗号化の特性を使用します.RS 256を使用する場合は両方とも相手に自分のOpenSSL公開鍵を提供する必要があり、HS 256を使用する場合は同じ鍵列を使用することを協議する.
RS 256を使用するプロセスは:甲のサーバーは自分の秘密鍵でデータに対して署名を行って、それから乙は甲の提供した公開鍵を使って署名を検査してそして明文を解析してそして自分の秘密鍵を使って署名をプラスして乙のサーバーの明文に返して、甲のサーバーは更に乙の提供した公開鍵を使って署名を解除します.
HS 256を使用するプロセスは、双方が合意した鍵で転送する必要がある明文を復号し、相手から送信されたJWT token ruby-jwt暗号化方法のソースコードを検証し、署名することである.
def encode(payload, key, algorithm = 'HS256', header_fields = {})
encoder = Encode.new payload, key, algorithm, header_fields
encoder.segments
end
def decode(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
merged_options = DEFAULT_OPTIONS.merge(custom_options)
decoder = Decode.new jwt, verify
header, payload, signature, signing_input = decoder.decode_segments
decode_verify_signature(key, header, payload, signature, signing_input, merged_options, &keyfinder) if verify
Verify.verify_claims(payload, merged_options)
raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
[payload, header]
end
encodeとdecodeのソースコードを組み合わせて、署名と検証の例を学びます.
require 'jwt'
payload = { data: 'test' }
# HS256 encode
hmac_secret = 'my$ecretK3y'
token = JWT.encode payload, hmac_secret, 'HS256'
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.ZxW8go9hz3ETCSfxFxpwSkYg_602gOPKearsf6DsxgY
# HS256 decode
decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' }
# [{"data"=>"test"}, {"typ"=>"JWT", "alg"=>"HS256"}]
rsa_private = OpenSSL::PKey::RSA.generate 2048
rsa_public = rsa_private.public_key
# RS256 encode
token = JWT.encode payload, rsa_private, 'RS256'
# eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.vtkMG9wCJRWc8lwOQJxnV8vRWRiBfvIzsE-vnM168Pe4jXszc2p9_2upAi8SI5EuwR7CsVAPFO_SqNsJLCb_55srqyBxPAyy97gy-44VyFR-dsnt9xpt2meJ4DyolXwhWxHTF9WkmQPHoFlu_2ssOOszBj9MO1X7KhmgkrX9h9yBTTzT9qvZQkesAbZz1RrF3ZRhihwbBdtGCCbJvlGBI6NAoMf_b3vNqeaHawc5hMS9nfoN-5Sc9CleJdPPnWnN7OYXeI_xdhCNTok0b7nMPvgDuIj9nTW4_u3Fv-rq9IiM-62LU1JFdqFVWQU-f72nkbT4bVy_SJr11mJ9Q6pXNQ
# RS decode
decoded_token = JWT.decode token, rsa_public, true, { :algorithm => 'RS256' }
[{"data"=>"test"}, {"typ"=>"JWT", "alg"=>"RS256"}]
いくつかのよく使われるClaim
hmac_secret = 'my$ecretK3y'
exp = Time.now.to_i + 4 * 3600
iat = Time.now.to_i
jti_raw = [hmac_secret, iat].join(':').to_s
jti = Digest::MD5.hexdigest(jti_raw)
payload = { :data => 'data', :exp => exp, :iat => iat, :jti => jti}
token = JWT.encode payload, hmac_secret, 'HS256'
# decode
begin
decoded_token = JWT.decode token, hmac_secret, true, { :verify_iat => true, :verify_jti => true, :algorithm => 'HS256' }
rescue JWT::ExpiredSignature
# Handle expired token, e.g. logout user or deny access
rescue JWT::InvalidIatError
# Handle invalid token, e.g. logout user or deny access
rescue JWT::InvalidJtiError
# Handle invalid token, e.g. logout user or deny access
end
Sinatra原理の解釈、使用
このセクションでは、Sinatraの基本的な実装原理と、Sinatra拡張原理を実装する方法について説明します.そして完全なSinatraアプリケーションを構築します.仮定したシナリオは,我々がサードパーティにHTTP APIを提供し,JWTの2つの特性を用いた:明文暗号化とチェックアウトである.実際の開発では双方ともOpenSSL生成の公開鍵を提供する必要がある.
Sinatraの基本原理
記述規則
クラシックスタイルとモジュール化スタイルのSinatra応用
通常、Sinatraを使用してプロジェクトを構築するには、クラシックスタイルとモジュール化スタイルの2つの方法があります.
1つは古典的なスタイル(classic style)です.このモードは通常、HTTPリクエストを受け入れるすべてのルーティングが1つのファイルにのみ適用されます(app.rb).app.rbファイルには
require 'sinatra'
を導入し、残りは直接ルーティングを書く必要があります.# app.rb
require 'sinatra'
get '/' do
'Hello world'
end
もう1つは,モジュール化スタイル(Modular style)というモデルも本論文で主に紹介する方法であり,通常,クラスRailsのMVC構造に拡張できる大型Rack−baseアプリケーションに適用され,多重化可能なRackミドルウェアを記述する.このような足場により,比較的複雑なインタフェースアプリケーションを実現できる.
# sinatra/base sinatra
require 'sinatra/base'
require "sinatra/config_file"
class MyApp < Sinatra::Base
register Sinatra::ConfigFile
config_file 'path/to/config.yml'
get '/' do
@greeting = settings.greeting
haml :index
end
# The rest of your modular application code goes here...
end
モジュール化されたスタイルを実現するSinatraアプリケーションには,Sinatra::BaseとSinatra::Applicationの2つの方式を継承し,両者の違いを説明する.
Sinatra::ApplicationとSinatra::Baseの関係
通常、モジュール化されたスタイルのSinatraは
sinatra::base
を継承しますが、session、flashなどのSinatraフレームワークの内部でデフォルトで実装されている拡張はオフです.デフォルトの拡張を開く最も簡単な方法は、require 'sinatra'
が古典的なスタイルのSinatraアプリケーションを実現することです.require 'sinatra'
が実現した古典的なスタイルのSinatra応用の原理はSinatra::Application
を継承し、Sinatra::Application
もSinatra::Base
を継承した.sinatraソースコードを見るとSinatra::Application
がSinatra::Base
を継承していることがわかります.# https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1902
class Application < Base
set :logging, Proc.new { !test? }
set :method_override, true
set :run, Proc.new { !test? }
set :app_file, nil
def self.register(*extensions, &block) #:nodoc:
added_methods = extensions.flat_map(&:public_instance_methods)
Delegator.delegate(*added_methods)
super(*extensions, &block)
end
end
Applicationクラスのコメントにこんな一節がありました
# Execution context for classic style (top-level) applications. All
# DSL methods executed on main are delegated to this class.
#
# The Application class should not be subclassed, unless you want to
# inherit all settings, routes, handlers, and error pages from the
# top-level. Subclassing Sinatra::Base is highly recommended for
# modular applications.
アプリケーションクラスを継承するとSinatraのデフォルトの設定、ルーティングがオンになることを説明するのに十分です.
Sinatra::Base
で実装された一連のクラスメソッド(e.g.,get,post,before,configure,set,etc.)クラシックスタイルとモジュール化スタイルSinatraアプリケーションの関係
require 'sinatra'
get '/' do
...
end
コードと組み合わせて、上述した古典的なスタイルを実現するSinatraアプリケーションは、
require 'sinatra'
であればよい.Sinatraのソースコードを見てみましょう.# sinatra/lib/sinatra.rb
require 'sinatra/main'
enable :inline_templates
# sinatra/lib/sinatra/main.rb
# https://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb
require 'sinatra/base'
module Sinatra
class Application < Base
# we assume that the first file that requires 'sinatra' is the
# app_file. all other path related options are calculated based
# on this path by default.
set :app_file, caller_files.first || $0
...
end
end
クラシックスタイルのSinatra応用本質も最も基本的なモジュール化スタイルであり、その実現原理は
Sinatra::Application
類を継承している.各モジュラースタイルのSinatraは
Sinatra::Base
を継承する必要があります.Sinatra::Application
もSinatra::Base
を継承しているので、クラシックスタイルは最も基本的なモジュール化Sinatraアプリケーションです.Sinatra拡張
エレガントなコードは多重化とモジュール化にこだわり,よく使われる機能モジュールを抽象化してSinatra拡張を行う.この章では、Sinatra拡張を実現するために必要な3つの点について引き続き議論します.Sinatra.register、Sinatra.helpers、Module.registeredです.
拡張は、通常、
Sinatra.helpers
を用いて実装されるヘルプメソッドモジュール拡張、Sinatra.register
を用いて実装されるルーティングモジュール拡張の2つである.ヘルプメソッド拡張の方法はリクエストコンテキストで使用されます:view,routes,helper、通常は多重化できる論理コードをヘルプメソッドモジュール拡張にカプセル化します.例えば、ページレンダリングヘルプメソッド、ログインするかどうかを判断する論理コードなどです.ルーティング拡張におけるメソッドはSinatra::Application
のクラスメソッドであり、ある機能特性のルーティングを実現し、通常、ある機能モジュール、例えば、システム登録機能モジュール、インタフェースアクセスチェック機能モジュールをカプセル化するために使用される.# lib/sinatra/base.rb
# Extend the top-level DSL with the modules provided.
def self.register(*extensions, &block)
Delegator.target.register(*extensions, &block)
end
# Include the helper modules provided in Sinatra's request context.
def self.helpers(*extensions, &block)
Delegator.target.helpers(*extensions, &block)
end
Extending The DSL (class) Context with Sinatra.register
Sinatra.registerによって実現される拡張は、
Sinatra::Application
のクラスメソッドを提供する.ルーティングの拡張例:require 'sinatra/base'
module Sinatra
module LinkBlocker
def block_links_from(host)
before {
halt 403, "Go Away!" if request.referer.match(host)
}
end
end
register LinkBlocker
end
Sinatra.register
は、Sinatra::Application
にクラスメソッドを追加した.古典的なスタイルとモジュール化されたスタイルで私たちのこの拡張を使用すると、次のようになります.# classic style
require 'sinatra'
require 'sinatra/linkblocker'
block_links_from 'digg.com'
get '/' do
"Hello World"
end
# modular style
require 'sinatra/base'
require 'sinatra/linkblocker'
class Hello < Sinatra::Base
register Sinatra::LinkBlocker
block_links_from 'digg.com'
get '/' do
"Hello World"
end
end
ルーティング拡張は、Sinatraアプリケーションによって使用されるために、Sinatraモジュールに登録され、
Sinatra.register
によって登録されなければならない.また、クラシックスタイルのsinatraアプリケーションは、最上位レベルのrequireに対応する拡張moduleで直接可能であり、モジュール化スタイルのSinatraアプリケーションは、継承クラス内部でSinatra.register
を介してルーティング拡張を再登録する必要がある.Extending The Request Context with Sinatra.helpers
ヘルプメソッドモジュールは、ルーティング、ビュー、またはヘルプメソッドの追加メソッドに拡張されます.次の例では,hヘルプメソッドというメソッドを実現する.
require 'sinatra/base'
module Sinatra
module HTMLEscapeHelper
def h(text)
Rack::Utils.escape_html(text)
end
end
helpers HTMLEscapeHelper
end
上記の例では、
helpers HTMLEscapeHelper
メソッドは、拡張で定義されたすべてのモジュールメソッドをSinatra::Application
に追加した.これらの方法は古典的なスタイルで使用できますrequire 'sinatra'
require 'sinatra/htmlescape'
get "/hello" do
h "1 < 2" # => "1 < 2"
end
クラシックスタイルで拡張方法を引用するには、
require 'sinatra/htmlescape'
だけです.しかし、
sinatra.helpers
で実装された拡張メソッドをモジュール化スタイルで使用するには、最上位レベルのrequireに対応する拡張モジュールのほかに、helpersメソッドを使用して導入する必要があります.require 'sinatra/base'
require 'sinatra/htmlescape'
class HelloApp < Sinatra::Base
helpers Sinatra::HTMLEscapeHelper
get "/hello" do
h "1 < 2"
end
end
registeredモジュールメソッドを使用してルーティング拡張を構成する
モジュール内で
registered
モジュールメソッドを実装することによって、ルーティング拡張のオプション、ルーティング、フィルタ、および例外処理を設定することができる.モジュールが参照されると、拡張モジュールに定義されたregistered
メソッドがSinatra::Base
モジュールに追加されます.registered
メソッドにはapp
パラメータがあり、ルーティング拡張は登録が実装された後(ルーティング拡張に反映されるregister ExtensionsName)、Sinatraは現在のアプリケーションのコンテキストをregisteredメソッド(すなわちデフォルトのappパラメータ)に渡し、appパラメータによってSinatra::Base
で定義されたDSLメソッドを呼び出すことができる.簡単なログイン検証拡張例を実現
# lib/sinatra/auth.rb
require 'sinatra/base' # require 'sinatra/base'
require 'sinatra/flash'
module Sinatra
module Auth
module Helpers
def authorized?
session[:admin]
end
def protected!
halt 401,slim(:unauthorized) unless authorized?
end
end
def self.registered(app)
app.helpers Helpers #
app.enable :sessions # Sinatra session
app.set :username => 'frank', :password => 'sinatra' #
# ```Sinatra::Base DSL
app.get '/login' do
slim :login
end
app.post '/login' do
if params[:username] == settings.username && params[:password] == settings.password
session[:admin] = true
flash[:notice] = "You are now logged in as #{settings.username}"
redirect to('/')
else
flash[:notice] = "The username or password you entered are incorrect"
redirect to('/login')
end
end
app.get '/logout' do
session[:admin] = nil
flash[:notice] = "You have now logged out"
redirect to('/')
end
end
end
register Auth #
end
# config/application.rb
require 'sinatra/base'
require_relative '../sinatra/auth'
class Application < Sinatra::Base
set :root, File.dirname(__FILE__) #
set :views, "#{settings.root}/../app/views" #
set :public_folder, 'public' #
register Sinatra::Auth # Sinatra Auth
end
#
# Auth Helpers :
# authorized? protected!
# 、 、 。
# : ,
footer
- if authorized?
a href="/logout" log out
- else
a href="/login" log in
最後に書く
Railsフレームワークを使いたくないRuby開発者にとって、Sinatraは軽量でプロジェクトを迅速に開発できる良い代替フレームワークです.筆者はこれを用いて,APIと各種H 5フロントエンドページのレンダリングを提供する支払いチャネル中台プロジェクトを開発した.この文章は3ヶ月前から書き始めましたが、遅延と惰性の断続的な続編のため、書くたびに以前所蔵していた文章をもう一度見て考えを整理しなければなりません.異常に時間と苦痛を浪費しています.自分に一つの説明をして、一つのプロジェクトをまとめて反省しなければ、間もなく忘れてしまうと自分に言った.また最近はGoを勉強しているので、これからもっとRuby開発を使うことになると思います.
リンクを参照して読む価値のある記事
Sinatra::Base
とSinatra::Application
の役割と両者の相違を解析した.次に、Sinatra拡張の4つのルールについて説明します.次に、Sinatra.helpersを使用してSinatraリクエストコンテキスト(コントローラ、ビュー、ヘルプメソッドで使用できる方法をカスタマイズする)を追加し、Sinatra.registerを使用してDSLコンテキストを追加する方法について説明します.(本質的にはSinatra::Applicationにクラスメソッドを追加します.シーンは通常Railsのxx_actionのように使用されます)最後に、module.registered
を介して拡張設定(設定、フィルタ、コントローラの例外処理を定義する方法を含む)