RFC 7519를 준수하여 PHP에서 JWT(JSON 웹 토큰)를 인코딩 및 디코딩하는 간단한 라이브러리입니다.
작곡가를 사용하여 종속성을 관리하고 PHP-JWT를 다운로드하세요.
composer require firebase/php-jwt
선택적으로 PHP 환경에 libsodium이 설치되어 있지 않은 경우 작곡가에서 paragonie/sodium_compat
패키지를 설치하십시오.
composer require paragonie/sodium_compat
use Firebase JWT JWT ;
use Firebase JWT Key ;
$ key = ' example_key ' ;
$ payload = [
' iss ' => ' http://example.org ' ,
' aud ' => ' http://example.com ' ,
' iat ' => 1356999524 ,
' nbf ' => 1357000000
];
/**
* IMPORTANT:
* You must specify supported algorithms for your application. See
* https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
* for a list of spec-compliant algorithms.
*/
$ jwt = JWT :: encode ( $ payload , $ key , ' HS256 ' );
$ decoded = JWT :: decode ( $ jwt , new Key ( $ key , ' HS256 ' ));
print_r ( $ decoded );
// Pass a stdClass in as the third parameter to get the decoded header values
$ decoded = JWT :: decode ( $ jwt , new Key ( $ key , ' HS256 ' ), $ headers = new stdClass());
print_r ( $ headers );
/*
NOTE: This will now be an object instead of an associative array. To get
an associative array, you will need to cast it as such:
*/
$ decoded_array = ( array ) $ decoded ;
/**
* You can add a leeway to account for when there is a clock skew times between
* the signing and verifying servers. It is recommended that this leeway should
* not be bigger than a few minutes.
*
* Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
*/
JWT :: $ leeway = 60 ; // $leeway in seconds
$ decoded = JWT :: decode ( $ jwt , new Key ( $ key , ' HS256 ' ));
JWT를 먼저 확인하지 않고 JWT 헤더를 디코딩하는 것은 권장되지 않으며 이 라이브러리에서는 지원되지 않습니다. JWT를 검증하지 않으면 헤더 값이 변조될 수 있기 때문입니다. 확인되지 않은 헤더에서 가져온 모든 값은 공격자가 보낸 문자열일 수 있는 것처럼 처리되어야 합니다. 어떤 이유로든 애플리케이션에서 이 작업을 수행하려는 경우 JWT 헤더 부분에서 json_decode
및 base64_decode
호출하여 헤더 값을 수동으로 디코딩할 수 있습니다.
use Firebase JWT JWT ;
$ key = ' example_key ' ;
$ payload = [
' iss ' => ' http://example.org ' ,
' aud ' => ' http://example.com ' ,
' iat ' => 1356999524 ,
' nbf ' => 1357000000
];
$ headers = [
' x-forwarded-for ' => ' www.google.com '
];
// Encode headers in the JWT string
$ jwt = JWT :: encode ( $ payload , $ key , ' HS256 ' , null , $ headers );
// Decode headers from the JWT string WITHOUT validation
// **IMPORTANT**: This operation is vulnerable to attacks, as the JWT has not yet been verified.
// These headers could be any value sent by an attacker.
list ( $ headersB64 , $ payloadB64 , $ sig ) = explode ( ' . ' , $ jwt );
$ decoded = json_decode ( base64_decode ( $ headersB64 ), true );
print_r ( $ decoded );
use Firebase JWT JWT ;
use Firebase JWT Key ;
$ privateKey = <<
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuzWHNM5f+amCjQztc5QTfJfzCC5J4nuW+L/aOxZ4f8J3Frew
M2c/dufrnmedsApb0By7WhaHlcqCh/ScAPyJhzkPYLae7bTVro3hok0zDITR8F6S
JGL42JAEUk+ILkPI+DONM0+3vzk6Kvfe548tu4czCuqU8BGVOlnp6IqBHhAswNMM
78pos/2z0CjPM4tbeXqSTTbNkXRboxjU29vSopcT51koWOgiTf3C7nJUoMWZHZI5
HqnIhPAG9yv8HAgNk6CMk2CadVHDo4IxjxTzTTqo1SCSH2pooJl9O8at6kkRYsrZ
WwsKlOFE2LUce7ObnXsYihStBUDoeBQlGG/BwQIDAQABAoIBAFtGaOqNKGwggn9k
6yzr6GhZ6Wt2rh1Xpq8XUz514UBhPxD7dFRLpbzCrLVpzY80LbmVGJ9+1pJozyWc
VKeCeUdNwbqkr240Oe7GTFmGjDoxU+5/HX/SJYPpC8JZ9oqgEA87iz+WQX9hVoP2
oF6EB4ckDvXmk8FMwVZW2l2/kd5mrEVbDaXKxhvUDf52iVD+sGIlTif7mBgR99/b
c3qiCnxCMmfYUnT2eh7Vv2LhCR/G9S6C3R4lA71rEyiU3KgsGfg0d82/XWXbegJW
h3QbWNtQLxTuIvLq5aAryV3PfaHlPgdgK0ft6ocU2de2FagFka3nfVEyC7IUsNTK
bq6nhAECgYEA7d/0DPOIaItl/8BWKyCuAHMss47j0wlGbBSHdJIiS55akMvnAG0M
39y22Qqfzh1at9kBFeYeFIIU82ZLF3xOcE3z6pJZ4Dyvx4BYdXH77odo9uVK9s1l
3T3BlMcqd1hvZLMS7dviyH79jZo4CXSHiKzc7pQ2YfK5eKxKqONeXuECgYEAyXlG
vonaus/YTb1IBei9HwaccnQ/1HRn6MvfDjb7JJDIBhNClGPt6xRlzBbSZ73c2QEC
6Fu9h36K/HZ2qcLd2bXiNyhIV7b6tVKk+0Psoj0dL9EbhsD1OsmE1nTPyAc9XZbb
OPYxy+dpBCUA8/1U9+uiFoCa7mIbWcSQ+39gHuECgYAz82pQfct30aH4JiBrkNqP
nJfRq05UY70uk5k1u0ikLTRoVS/hJu/d4E1Kv4hBMqYCavFSwAwnvHUo51lVCr/y
xQOVYlsgnwBg2MX4+GjmIkqpSVCC8D7j/73MaWb746OIYZervQ8dbKahi2HbpsiG
8AHcVSA/agxZr38qvWV54QKBgCD5TlDE8x18AuTGQ9FjxAAd7uD0kbXNz2vUYg9L
hFL5tyL3aAAtUrUUw4xhd9IuysRhW/53dU+FsG2dXdJu6CxHjlyEpUJl2iZu/j15
YnMzGWHIEX8+eWRDsw/+Ujtko/B7TinGcWPz3cYl4EAOiCeDUyXnqnO1btCEUU44
DJ1BAoGBAJuPD27ErTSVtId90+M4zFPNibFP50KprVdc8CR37BE7r8vuGgNYXmnI
RLnGP9p3pVgFCktORuYS2J/6t84I3+A17nEoB4xvhTLeAinAW/uTQOUmNicOP4Ek
2MsLL2kHgL8bLTmvXV4FX+PXphrDKg1XxzOYn0otuoqdAQrkK4og
-----END RSA PRIVATE KEY-----
EOD ;
$ publicKey = <<
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzWHNM5f+amCjQztc5QT
fJfzCC5J4nuW+L/aOxZ4f8J3FrewM2c/dufrnmedsApb0By7WhaHlcqCh/ScAPyJ
hzkPYLae7bTVro3hok0zDITR8F6SJGL42JAEUk+ILkPI+DONM0+3vzk6Kvfe548t
u4czCuqU8BGVOlnp6IqBHhAswNMM78pos/2z0CjPM4tbeXqSTTbNkXRboxjU29vS
opcT51koWOgiTf3C7nJUoMWZHZI5HqnIhPAG9yv8HAgNk6CMk2CadVHDo4IxjxTz
TTqo1SCSH2pooJl9O8at6kkRYsrZWwsKlOFE2LUce7ObnXsYihStBUDoeBQlGG/B
wQIDAQAB
-----END PUBLIC KEY-----
EOD ;
$ payload = [
' iss ' => ' example.org ' ,
' aud ' => ' example.com ' ,
' iat ' => 1356999524 ,
' nbf ' => 1357000000
];
$ jwt = JWT :: encode ( $ payload , $ privateKey , ' RS256 ' );
echo " Encode: n" . print_r ( $ jwt , true ) . "n" ;
$ decoded = JWT :: decode ( $ jwt , new Key ( $ publicKey , ' RS256 ' ));
/*
NOTE: This will now be an object instead of an associative array. To get
an associative array, you will need to cast it as such:
*/
$ decoded_array = ( array ) $ decoded ;
echo " Decode: n" . print_r ( $ decoded_array , true ) . "n" ;
use Firebase JWT JWT ;
use Firebase JWT Key ;
// Your passphrase
$ passphrase = ' [YOUR_PASSPHRASE] ' ;
// Your private key file with passphrase
// Can be generated with "ssh-keygen -t rsa -m pem"
$ privateKeyFile = ' /path/to/key-with-passphrase.pem ' ;
// Create a private key of type "resource"
$ privateKey = openssl_pkey_get_private (
file_get_contents ( $ privateKeyFile ),
$ passphrase
);
$ payload = [
' iss ' => ' example.org ' ,
' aud ' => ' example.com ' ,
' iat ' => 1356999524 ,
' nbf ' => 1357000000
];
$ jwt = JWT :: encode ( $ payload , $ privateKey , ' RS256 ' );
echo " Encode: n" . print_r ( $ jwt , true ) . "n" ;
// Get public key from the private key, or pull from from a file.
$ publicKey = openssl_pkey_get_details ( $ privateKey )[ ' key ' ];
$ decoded = JWT :: decode ( $ jwt , new Key ( $ publicKey , ' RS256 ' ));
echo " Decode: n" . print_r (( array ) $ decoded , true ) . "n" ;
use Firebase JWT JWT ;
use Firebase JWT Key ;
// Public and private keys are expected to be Base64 encoded. The last
// non-empty line is used so that keys can be generated with
// sodium_crypto_sign_keypair(). The secret keys generated by other tools may
// need to be adjusted to match the input expected by libsodium.
$ keyPair = sodium_crypto_sign_keypair ();
$ privateKey = base64_encode ( sodium_crypto_sign_secretkey ( $ keyPair ));
$ publicKey = base64_encode ( sodium_crypto_sign_publickey ( $ keyPair ));
$ payload = [
' iss ' => ' example.org ' ,
' aud ' => ' example.com ' ,
' iat ' => 1356999524 ,
' nbf ' => 1357000000
];
$ jwt = JWT :: encode ( $ payload , $ privateKey , ' EdDSA ' );
echo " Encode: n" . print_r ( $ jwt , true ) . "n" ;
$ decoded = JWT :: decode ( $ jwt , new Key ( $ publicKey , ' EdDSA ' ));
echo " Decode: n" . print_r (( array ) $ decoded , true ) . "n" ;
use Firebase JWT JWT ;
use Firebase JWT Key ;
// Example RSA keys from previous example
// $privateKey1 = '...';
// $publicKey1 = '...';
// Example EdDSA keys from previous example
// $privateKey2 = '...';
// $publicKey2 = '...';
$ payload = [
' iss ' => ' example.org ' ,
' aud ' => ' example.com ' ,
' iat ' => 1356999524 ,
' nbf ' => 1357000000
];
$ jwt1 = JWT :: encode ( $ payload , $ privateKey1 , ' RS256 ' , ' kid1 ' );
$ jwt2 = JWT :: encode ( $ payload , $ privateKey2 , ' EdDSA ' , ' kid2 ' );
echo " Encode 1: n" . print_r ( $ jwt1 , true ) . "n" ;
echo " Encode 2: n" . print_r ( $ jwt2 , true ) . "n" ;
$ keys = [
' kid1 ' => new Key ( $ publicKey1 , ' RS256 ' ),
' kid2 ' => new Key ( $ publicKey2 , ' EdDSA ' ),
];
$ decoded1 = JWT :: decode ( $ jwt1 , $ keys );
$ decoded2 = JWT :: decode ( $ jwt2 , $ keys );
echo " Decode 1: n" . print_r (( array ) $ decoded1 , true ) . "n" ;
echo " Decode 2: n" . print_r (( array ) $ decoded2 , true ) . "n" ;
use Firebase JWT JWK ;
use Firebase JWT JWT ;
// Set of keys. The "keys" key is required. For example, the JSON response to
// this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
$ jwks = [ ' keys ' => []];
// JWK::parseKeySet($jwks) returns an associative array of **kid** to FirebaseJWTKey
// objects. Pass this as the second parameter to JWT::decode.
JWT :: decode ( $ payload , JWK :: parseKeySet ( $ jwks ));
CachedKeySet
클래스는 공개 URI에서 JWKS(JSON 웹 키 세트)를 가져오고 캐시하는 데 사용할 수 있습니다. 여기에는 다음과 같은 장점이 있습니다.
use Firebase JWT CachedKeySet ;
use Firebase JWT JWT ;
// The URI for the JWKS you wish to cache the results from
$ jwksUri = ' https://www.gstatic.com/iap/verify/public_key-jwk ' ;
// Create an HTTP client (can be any PSR-7 compatible HTTP client)
$ httpClient = new GuzzleHttp Client ();
// Create an HTTP request factory (can be any PSR-17 compatible HTTP request factory)
$ httpFactory = new GuzzleHttp Psr HttpFactory ();
// Create a cache item pool (can be any PSR-6 compatible cache item pool)
$ cacheItemPool = Phpfastcache CacheManager :: getInstance ( ' files ' );
$ keySet = new CachedKeySet (
$ jwksUri ,
$ httpClient ,
$ httpFactory ,
$ cacheItemPool ,
null , // $expiresAfter int seconds to set the JWKS to expire
true // $ rateLimit true to enable rate limit of 10 RPS on lookup of invalid keys
);
$ jwt = ' eyJhbGci... ' ; // Some JWT signed by a key from the $jwkUri above
$ decoded = JWT :: decode ( $ jwt , $ keySet );
JWT::decode
호출이 유효하지 않은 경우 다음 예외 중 하나가 발생합니다.
use Firebase JWT JWT ;
use Firebase JWT SignatureInvalidException ;
use Firebase JWT BeforeValidException ;
use Firebase JWT ExpiredException ;
use DomainException ;
use InvalidArgumentException ;
use UnexpectedValueException ;
try {
$ decoded = JWT :: decode ( $ payload , $ keys );
} catch ( InvalidArgumentException $ e ) {
// provided key/key-array is empty or malformed.
} catch ( DomainException $ e ) {
// provided algorithm is unsupported OR
// provided key is invalid OR
// unknown error thrown in openSSL or libsodium OR
// libsodium is required but not available.
} catch ( SignatureInvalidException $ e ) {
// provided JWT signature verification failed.
} catch ( BeforeValidException $ e ) {
// provided JWT is trying to be used before "nbf" claim OR
// provided JWT is trying to be used before "iat" claim.
} catch ( ExpiredException $ e ) {
// provided JWT is trying to be used after "exp" claim.
} catch ( UnexpectedValueException $ e ) {
// provided JWT is malformed OR
// provided JWT is missing an algorithm / using an unsupported algorithm OR
// provided JWT algorithm does not match provided key OR
// provided key ID in key/key-array is empty or invalid.
}
FirebaseJWT
네임스페이스의 모든 예외는 UnexpectedValueException
확장하며 다음과 같이 단순화할 수 있습니다.
use Firebase JWT JWT ;
use UnexpectedValueException ;
try {
$ decoded = JWT :: decode ( $ payload , $ keys );
} catch ( LogicException $ e ) {
// errors having to do with environmental setup or malformed JWT Keys
} catch ( UnexpectedValueException $ e ) {
// errors having to do with JWT signature and claims
}
JWT::decode
의 반환 값은 일반 PHP 객체 stdClass
입니다. 대신 배열로 처리하려면 다음을 수행할 수 있습니다.
// return type is stdClass
$ decoded = JWT :: decode ( $ payload , $ keys );
// cast to array
$ decoded = json_decode ( json_encode ( $ decoded ), true );
phpunit을 사용하여 테스트를 실행합니다.
$ pear install PHPUnit
$ phpunit --configuration phpunit.xml.dist
PHPUnit 3.7.10 by Sebastian Bergmann.
.....
Time: 0 seconds, Memory: 2.50Mb
OK (5 tests, 5 assertions)
개인 키에 n
문자가 포함된 경우 이스케이프된 문자를 올바르게 해석하려면 작은따옴표 ''
가 아닌 큰따옴표 ""
"로 묶어야 합니다.
3항 BSD.