ไลบรารีง่ายๆ สำหรับเข้ารหัสและถอดรหัส JSON Web Tokens (JWT) ใน PHP ซึ่งสอดคล้องกับ RFC 7519
ใช้ผู้แต่งเพื่อจัดการการพึ่งพาของคุณและดาวน์โหลด PHP-JWT:
composer require firebase/php-jwt
หรืออีกทางหนึ่ง ให้ติดตั้งแพ็คเกจ paragonie/sodium_compat
จากผู้แต่ง หาก php env ของคุณไม่ได้ติดตั้ง 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 ' ));
ไม่แนะนำให้ถอดรหัสส่วนหัว JWT โดยไม่ตรวจสอบ JWT ก่อน และไลบรารีนี้ไม่รองรับ เนื่องจากหากไม่มีการตรวจสอบ JWT ค่าส่วนหัวอาจถูกแก้ไข ค่าใดๆ ที่ดึงมาจากส่วนหัวที่ไม่ได้รับการยืนยันควรได้รับการปฏิบัติเสมือนว่าเป็นสตริงใดๆ ที่ส่งเข้ามาจากผู้โจมตี หากนี่คือสิ่งที่คุณยังต้องการทำในแอปพลิเคชันของคุณไม่ว่าด้วยเหตุผลใดก็ตาม คุณสามารถถอดรหัสค่าส่วนหัวด้วยตนเองได้ง่ายๆ โดยการเรียก json_decode
และ base64_decode
บนส่วนหัวของ 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 ));
คลาส CachedKeySet
สามารถใช้เพื่อดึงข้อมูลและแคช JWKS (ชุดคีย์เว็บ JSON) จาก URI สาธารณะ สิ่งนี้มีข้อดีดังต่อไปนี้:
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
คือ stdClass
วัตถุ PHP ทั่วไป หากคุณต้องการจัดการกับอาร์เรย์แทน คุณสามารถทำดังต่อไปนี้:
// 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
ตรวจสอบให้แน่ใจว่าได้ใส่เครื่องหมายคำพูดคู่ ""
และไม่ใช่เครื่องหมายคำพูดเดี่ยว ''
เพื่อตีความอักขระที่ใช้ Escape อย่างถูกต้อง
BSD 3 ข้อ