JWTというJSONデータを署名してトークンとして扱う仕組みがあります。 これをRubyで扱う方法を調べてみたので、備忘録がてら書いておきます。
JWTはJSON Web Tokenの略。
JWTが総本山で、 RFC 7519 - JSON Web Token (JWT)に規格が定義されている。
RubyでJWTを扱うためのgemがいくつかある模様。
この中ではruby-jwtがスタンダードなようなのでこれの使い方を調べてみた。
MITライセンスのオープンソース。
https://github.com/jwt/ruby-jwtでコードが公開されている。
gem install jwt
するか、Gemfileに
gem 'jwt'
を追記して、bundle install
する。
署名なしのJWTを生成するには、
require 'jwt'
payload = { data: 'test' }
token = JWT.encode payload, nil, 'none'
#=> "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoiZGF0YSJ9."
とする。
JWT.encode
メソッドの第2引数にnil
を指定する。
署名なしのJWTをデコードするには、
decoded = JWT.decode token, nil, false
#=> [{"data"=>"data"}, {"typ"=>"JWT", "alg"=>"none"}]
とする。 元のペイロードが復元されている。
HMACで署名したJWTを生成するには、
require 'jwt'
payload = { data: 'test' }
secret = 'my_secret'
token = JWT.encode payload, secret, 'HS256'
#=> "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.RbmQ6qKdj8y6GeTBX0EGbLst3wrHuGrNGGOuT5pRPls"
とする。
JWT.encode
メソッドの第2引数にシークレット、第3引数にアルゴリズムを指定する。
デコードするには、
decoded = JWT.decode token, secret, true, { algorithm: 'HS256' }
#=> [{"data"=>"test"}, {"typ"=>"JWT", "alg"=>"HS256"}]
とする。
JWTをRSAで署名したJWTを生成するには、
require 'jwt'
payload = { data: 'test' }
rsa_private = OpenSSL::PKey::RSA.generate 2048
rsa_public = rsa_private.public_key
token = JWT.encode payload, rsa_private, 'RS256'
#=> "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.HwhiXp2sZuGf_ZyZV5yCPBQ4Bp_L87XJf4WTK3GmmlEKUMKi_Aro7iLYLvmHNqQyQGt0-d4UKuUGqHQcxbiiktd9YMrES5lJA6ttLKtnrBL7kBA4uM0Y0mLZ6QyiUfXEYMnyEdgiqLguIE50i2nZzALVXVj4PCcySQyhtM0vvblyLxyzmk1fX6tTaok6oNHm2wQO871sYjZKvt8PpFoFhZM81RHyQfezheH6gjcRmfKi8fvPkeTsaTXIKfnkD5cpoDttpOdVTukDxTwGNRfvD-mdw3E9tJcl1ymS6usFsfGwM3HaLNP9DenhmRaicJt5JFCZ_gZty_Cx32BBalfyBw"
とする。RSAの秘密鍵を使って署名する。 デコードするには、
decoded = JWT.decode token, rsa_public, true, { algorithum: 'RS256' }
#=> [{"data"=>"test"}, {"typ"=>"JWT", "alg"=>"RS256"}]
とする。RSAの公開鍵を使ってデコードする。
JWTのペイロードには、exp
(expiration time=有効期限)を含めることができる。
ruby-jwtでは、JWTのデコード時に、有効期限をチェックすることができる。
(有効期限が満了していたら例外を送出する。)
require 'jwt'
secret = 'my_secret'
exp = Time.now.to_i # 現在時刻を期限とする
payload = { data: 'test', exp: exp }
token = JWT.encode payload, secret, 'HS256'
sleep 1 # 確実に満了させる
decoded = JWT.decode token, secret, true, { :algorithm => 'HS256' }
# => JWT::ExpiredSignature: Signature has expired
exp
クレームで指定した有効期限を超過したトークンをデコードすると、JWT::ExpiredSignature
例外が送出される。
有効期限以外の予約済みクレームに対する検証も簡単に行える模様。 ruby-jwtのREADMEにクレーム単位の検証方法が書いてある。
以上、備忘録です。 JWTって認証関連以外の用途って何かあるのだろうか・・・