Pustaka sederhana untuk menyandikan dan mendekode JSON Web Tokens (JWT) di PHP, sesuai dengan RFC 7519.
Gunakan komposer untuk mengelola dependensi Anda dan mengunduh PHP-JWT:
composer require firebase/php-jwt
Opsional, instal paket paragonie/sodium_compat
dari composer jika env php Anda belum menginstal libsodium:
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 ' ));
Mendekode header JWT tanpa memverifikasi JWT terlebih dahulu TIDAK disarankan, dan tidak didukung oleh perpustakaan ini. Hal ini karena tanpa memverifikasi JWT, nilai header dapat diubah. Nilai apa pun yang diambil dari header yang belum diverifikasi harus diperlakukan seolah-olah itu adalah string apa pun yang dikirim dari penyerang. Jika ini adalah sesuatu yang masih ingin Anda lakukan di aplikasi Anda karena alasan apa pun, Anda dapat mendekode nilai header secara manual hanya dengan memanggil json_decode
dan base64_decode
pada bagian header JWT:
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 ));
Kelas CachedKeySet
dapat digunakan untuk mengambil dan menyimpan JWKS (JSON Web Key Sets) dari URI publik. Ini memiliki keuntungan sebagai berikut:
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 );
Jika panggilan ke JWT::decode
tidak valid, salah satu pengecualian berikut akan muncul:
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.
}
Semua pengecualian di namespace FirebaseJWT
memperluas UnexpectedValueException
, dan dapat disederhanakan seperti ini:
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
}
Nilai kembalian JWT::decode
adalah objek PHP generik stdClass
. Jika Anda ingin menangani array, Anda dapat melakukan hal berikut:
// return type is stdClass
$ decoded = JWT :: decode ( $ payload , $ keys );
// cast to array
$ decoded = json_decode ( json_encode ( $ decoded ), true );
Jalankan tes menggunakan 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)
Jika kunci pribadi Anda berisi n
karakter, pastikan untuk membungkusnya dengan tanda kutip ganda ""
dan bukan tanda kutip tunggal ''
untuk menafsirkan karakter yang lolos dengan benar.
3-Klausul BSD.