PHP JWT初識

舊夢發癲發表於2019-02-16

一直沒有好好看過jwt,直到前兩天要做web驗證,朋友給我推薦了jwt。才發現jwt已經被大家廣泛的應用了。看來我有點out了。哈哈,趁著這個世界來好好看看這個。

JWT(JSON Web Token), 顧名思義就是可以在Web上傳輸的token,這種token是用JSON格式進行format的。它是一個開源標準(RFC 7519),定義了一個緊湊的自包含的方式在不同實體之間安全的用JSON格式傳輸資訊。

由於現在很多專案都是前後端分離,restful api模式。所以傳統的session模式就沒有辦法滿足認證需求,這個時候jwt的作用就來了。可以說 restful api認證是jwt的一個很好的應用場景。

下面是一個很小的demo

<?php
require_once `src/JWT.php`;
header(`Content-type:application/json`);
//定義Key
const KEY = `dasjdkashdwqe1213dsfsn;p`;

$user = [
    `uid`=>`dadsa-12312-vsd1s1-fsds`,
    `account`=>`daisc`,
    `password`=>`123456`
];
$redis = redis();
$action  =  $_GET[`action`];
switch ($action)
{
    case `login`:
        login();
        break;
    case `info`:
        info();
        break;

}
//登陸,寫入驗證token
function login()
{
    global  $user;
    $account = $_GET[`account`];
    $pwd = $_GET[`password`];
    $res = [];
    if($account==$user[`account`]&&$pwd==$user[`password`])
    {
        unset($user[`password`]);
        $time = time();
        $token = [
            `iss`=>`http://test.cc`,//簽發者
            `iat`=>$time,
            `exp`=>$time+60,
            `data`=>$user
        ];
        $jwt = FirebaseJWTJWT::encode($token,KEY);
        $res[`code`] = 200;
        $res[`message`] = `登入成功`;
        $res[`jwt`] = $jwt;

    }
    else
    {
        $res[`message`]= `使用者名稱或密碼錯誤`;
        $res[`code`] = 401;
    }
    exit(json_encode($res));
}





function info()
{
   $jwt = $_SERVER[`HTTP_AUTHORIZATION`] ?? false;
   $res[`code`] = 200;
   if($jwt)
   {
        $jwt = str_replace(`Bearer `,``,$jwt);
        if(empty($jwt))
        {
            $res[`code`] = 401;
            $res[`msg`] = `You do not have permission to access.`;
            exit(json_encode($res));
        }
        try{
            $token = (array) FirebaseJWTJWT::decode($jwt,KEY, [`HS256`]);
            if($token[`exp`]<time())
            {
                $res[`code`] = 401;
                $res[`msg`] = `登入超時,請重新登入`;
            }
            $res[`data`]= $token[`data`];
        }catch (Exception $E)
        {
            $res[`code`] = 401;
            $res[`msg`] = `登入超時,請重新登入.`;
        }
   }
   else
   {
       $res[`code`] = 401;
       $res[`msg`] = `You do not have permission to access.`;
   }
    exit(json_encode($res));
}



//連線redis
function redis()
{
    $redis = new  Redis();
    $redis->connect(`127.0.0.1`);
    return $redis;
}

這個dmeo裡面用jwt做了一個簡單的認證。 其中用到了一個php-jwt的加密包https://github.com/firebase/php-jwt

其中KEY為定義的私鑰也就是jwt裡面的 sign部分,這個一定要儲存好。
header部分php-jwt包裡面已經幫我們完成了,加密程式碼如下

    */
    public static function encode($payload, $key, $alg = `HS256`, $keyId = null, $head = null)
    {
        $header = array(`typ` => `JWT`, `alg` => $alg);
        if ($keyId !== null) {
            $header[`kid`] = $keyId;
        }
        if ( isset($head) && is_array($head) ) {
            $header = array_merge($head, $header);
        }
        $segments = array();
        $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
        $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
        $signing_input = implode(`.`, $segments);

        $signature = static::sign($signing_input, $key, $alg);
        $segments[] = static::urlsafeB64Encode($signature);

        return implode(`.`, $segments);
    }

可以看出預設的加密的方式是HS256。這也是說jwt安全的原因。現階段HS256加密還是很安全的。
這個包裡面也支援證照加密。

加密解密的過程這個包已經幫我們完成了。所以我們只需要定義jwt中的 poyload部分就可以了。也就是demo裡面的token部分。加密成功會得到一個加密的Jwt字串,下次前端在請求api的時候需要攜帶這個jwt字串作為認證。
在header頭裡面增加Authorization。在服務端驗證的時候回通過取得這個值來驗證回話的有效。

下面是poyload的一些常用配置

 $token   = [
            #非必須。issuer 請求實體,可以是發起請求的使用者的資訊,也可是jwt的簽發者。
            "iss"       => "http://example.org",
            #非必須。issued at。 token建立時間,unix時間戳格式
            "iat"       => $_SERVER[`REQUEST_TIME`],
            #非必須。expire 指定token的生命週期。unix時間戳格式
            "exp"       => $_SERVER[`REQUEST_TIME`] + 7200,
            #非必須。接收該JWT的一方。
            "aud"       => "http://example.com",
            #非必須。該JWT所面向的使用者
            "sub"       => "jrocket@example.com",
            # 非必須。not before。如果當前時間在nbf裡的時間之前,則Token不被接受;一般都會留一些餘地,比如幾分鐘。
            "nbf"       => 1357000000,
            # 非必須。JWT ID。針對當前token的唯一標識
            "jti"       => `222we`,
            # 自定義欄位
            "GivenName" => "Jonny",
            # 自定義欄位
            "name"   => "Rocket",
            # 自定義欄位
            "Email"     => "jrocket@example.com",
         
        ];

裡面包含的配置可以自由配置,也可以自己新增一些其他的。這些都是網上大家常用的,可以說是一種約定吧。

對於jwt還有很多有疑問的地方,下來在慢慢研究,比如續期以及退出的問題

檢視原文