首頁 > 軟體

基於PHP實現JWT登入鑑權的範例程式碼

2022-04-15 13:00:02

一、什麼是JWT

1、簡介

JWT(JSON Web Token)是為了在網路應用環境間傳遞宣告而執行的一種基於JSON的開放標準。

簡單的說,JWT就是一種Token的編碼演演算法,伺服器端負責根據一個密碼和演演算法生成Token,然後發給使用者端,使用者端只負責後面每次請求都在HTTP header裡面帶上這個Token,伺服器負責驗證這個Token是不是合法的,有沒有過期等,並可以解析出subject和claim裡面的資料。

2、JWT的組成

第一部分為頭部(header),第二部分我們稱其為載荷(payload),第三部分是簽證(signature)。【中間用 . 分隔】

一個標準的JWT生成的token格式如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IjRmMWcyM2ExMmFhMTEifQ.eyJpc3MiOiJodHRwOlwvXC9leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHA6XC9cL2V4YW1wbGUub3JnIiwianRpIjoiNGYxZzIzYTEyYWExMSIsImlhdCI6MTY0MDE3MTA1MywibmJmIjoxNjQwMTcxMDU0LCJleHAiOjE2NDAxNzQ2NTMsInVpZCI6MjAsInVzZXJuYW1lIjoiTWFrZSJ9.bysUwNIyhqqEyL0JecSHdplSTfE6G6zuCsrAn6eyrQM

使用https://jwt.io/這個網站對JWT Token進行解析的結果如下

3、JWT驗證流程和特點

驗證流程:

① 在頭部資訊中宣告加密演演算法和常數, 然後把header使用json轉化為字串

② 在載荷中宣告使用者資訊,同時還有一些其他的內容;再次使用json 把載荷部分進行轉化,轉化為字串

③ 使用在header中宣告的加密演演算法和每個專案隨機生成的secret來進行加密, 把第一步分字串和第二部分的字串進行加密, 生成新的字串。詞字串是獨一無二的。

④ 解密的時候,只要使用者端帶著JWT來發起請求,伺服器端就直接使用secret進行解密。

特點:

① 三部分組成,每一部分都進行字串的轉化

② 解密的時候沒有使用資料庫,僅僅使用的是secret進行解密

③ JWT的secret千萬不能洩密!

④ 不依賴資料庫,而是直接根據token取出儲存的使用者資訊,以及對token可用性校驗,校驗方式更加簡單便捷化,單點登入更為簡單。

二、相關問題

  • JWT Token需要持久化在redis、Memcached中嗎?

不應該這樣做,無狀態的jwt變成了有狀態了,背離了JWT通過演演算法驗證的初心。

  • 在退出登入時怎樣實現JWT Token失效呢?

退出登入, 只要使用者端端把Token丟棄就可以了,伺服器端不需要廢棄Token。

  • 怎樣保持使用者端長時間保持登入狀態?

伺服器端提供重新整理Token的介面, 使用者端負責按一定的邏輯重新整理伺服器Token。

三、PHP實現

1、引入依賴

composer require lcobucci/jwt 3.*

2、功能實現

  • 簽發token, 設定簽發人、接收人、唯一標識、簽發時間、立即生效、過期時間、使用者id、使用者username、簽名。其中,使用者id、使用者username是特意儲存在token中的資訊,也可以增加一些其他資訊,這樣在解析的時候就可以直接獲取到這些資訊,不能是敏感資料
  • 驗證令牌驗證這個Token是不是合法的,有沒有過期等,並可以解析出subject和claim裡面的資料,傳遞jwt token的方式為Authorization中的Bearer Token,如下

3、封裝工具類如下

<?php
/**
 * Created by PhpStorm
 * @author sxd
 */

namespace AppUtils;


use LcobucciJWTConfiguration;
use LcobucciJWTParser;
use LcobucciJWTSignerHmacSha256;
use LcobucciJWTSignerKeyInMemory;
use LcobucciJWTTokenPlain;
use LcobucciJWTValidationConstraintIdentifiedBy;
use LcobucciJWTValidationConstraintIssuedBy;
use LcobucciJWTValidationConstraintPermittedFor;
use LcobucciJWTValidationData;

class JwtUtil
{
    // jwt github: https://github.com/lcobucci/jwt     https://jwt.io/
    protected $issuer = "http://example.com";
    protected $audience = "http://example.org";
    protected $id = "4f1g23a12aa11";
    // key 是絕對不允許洩露的
    protected static $key = "8swQsm1Xb0TA0Jw5ASPwClKVZPoTyS7GvhtaW0MxzKEihs1BNpcS2q3FYMJ11111";

    /**
     * 簽發令牌
     */
    public function getToken()
{
        $time = time();
        $config = self::getConfig();
        assert($config instanceof Configuration);

        // 簽發token, 設定簽發人、接收人、唯一標識、簽發時間、立即生效、過期時間、使用者id、使用者username、簽名
        $token = $config->builder()
            ->issuedBy($this->issuer) // Configures the issuer (iss claim)
            ->permittedFor($this->audience) // Configures the audience (aud claim)
            ->identifiedBy($this->id, true) // Configures the id (jti claim), replicating as a header item
            ->issuedAt($time) // Configures the time that the token was issue (iat claim)
            ->canOnlyBeUsedAfter($time + 1) // Configures the time that the token can be used (nbf claim)  簽發x秒鐘後生效
            ->expiresAt($time + 3600) // Configures the expiration time of the token (exp claim)
            ->withClaim('uid', 20) // Configures a new claim, called "uid"
            ->withClaim('username', "Make") // Configures a new claim, called "uid"
            ->getToken($config->signer(), $config->signingKey()); // Retrieves the generated token
        return $token->toString();
    }

    /**
     * 驗證 jwt token 並返回其中的使用者id
     * verify token
     */
    public function verifyToken_bak($token)
{
        try {
            $config = self::getConfig();
            assert($config instanceof Configuration);

            $token = $config->parser()->parse($token);
            assert($token instanceof Plain);

            //LcobucciJWTValidationConstraintIdentifiedBy: 驗證jwt id是否匹配
            //LcobucciJWTValidationConstraintIssuedBy: 驗證簽發人引數是否匹配
            //LcobucciJWTValidationConstraintPermittedFor: 驗證受眾人引數是否匹配
            //LcobucciJWTValidationConstraintRelatedTo: 驗證自定義cliam引數是否匹配
            //LcobucciJWTValidationConstraintSignedWith: 驗證令牌是否已使用預期的簽名者和金鑰簽名
            //LcobucciJWTValidationConstraintStrictValidAt: ::驗證存在及其有效性的權利要求中的iat,nbf和exp(支援餘地設定
            //LcobucciJWTValidationConstraintLooseValidAt: 驗證的權利要求iat,nbf和exp,當存在時(支援餘地設定)

            //驗證jwt id是否匹配
            $validate_jwt_id = new IdentifiedBy($this->id);
            //驗證簽發人url是否正確
            $validate_issued = new IssuedBy($this->issuer);
            //驗證使用者端url是否匹配
            $validate_aud = new PermittedFor($this->audience);
            $config->setValidationConstraints($validate_jwt_id, $validate_issued, $validate_aud);

            $constraints = $config->validationConstraints();

            if (!$config->validator()->validate($token, ...$constraints)) {
                die("token invalid!");
            }
        } catch (Exception $e) {
            die("error:" . $e->getMessage());
        }
        $jwtInfo = $token->claims();  // 這是jwt token中儲存的所有資訊
        return $jwtInfo->get("uid");  // 獲取uid
    }

    /**
     * 加密解密使用的設定
     * @return Configuration
     */
    public static function getConfig()
{
        $configuration = Configuration::forSymmetricSigner(
        // You may use any HMAC variations (256, 384, and 512)
            new Sha256(),
            // replace the value below with a key of your own!
            InMemory::base64Encoded(self::$key)
        // You may also override the JOSE encoder/decoder if needed by providing extra arguments here
        );
        return $configuration;
    }

    /**
     * 另一種驗證方法,但是已經棄用
     * verify token
     */
    public function verifyToken($token)
{
        $token = (new Parser())->parse((string)$token);
        //驗證token
        $data = new ValidationData();
        $data->setIssuer($this->issuer);//驗證的簽發人
        $data->setAudience($this->audience);//驗證的接收人
        $data->setId($this->id);//驗證token標識

        if (!$token->validate($data)) {
            //token驗證失敗
            die("token invalid!");
        }
        $jwtInfo = $token->claims();  // 這是jwt token中儲存的所有資訊
        return $jwtInfo->get("uid");  // 獲取uid
    }

}

以上就是基於PHP實現JWT登入鑑權的範例程式碼的詳細內容,更多關於PHP JWT登入鑑權的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com