在Laravel中使用JWT

php版本要求:7.0+
之前寫過一篇原生的php操作jwt的demo http://www.lxweimin.com/p/1f6d2804b674
平時工作中一直在使用laravel,所以整理一下在laravel中應該怎么使用jwt來開發(fā)api接口。

大體思路:
用戶登錄拿到token,之后需要登錄的請求拿著token,并使用中間件解密。之后的業(yè)務層面和使用session一樣處理即可。

1、首先安裝laravel5.5

composer create-project --prefer-dist laravel/laravel=5.5.* jwt

2、安裝jwt的包,此處安裝的是 https://github.com/lcobucci/jwt ^3.2

composer require lcobucci/jwt

安裝完成后就可以直接擼了。
3、創(chuàng)建 JwtAuth.php 類,此類是為了加解密token

<?php
/**
 * Created by PhpStorm.
 * User: season
 * Date: 2019/4/7
 * Time: 15:33
 */

namespace App\Lib;

use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\ValidationData;


/**
 *
 * 單例模式 一次請求只針對一個用戶.
 * Class JwtAuth
 * @package App\Lib
 */
class JwtAuth
{
    private static $instance;

    // 加密后的token
    private $token;
    // 解析JWT得到的token
    private $decodeToken;
    // 用戶ID
    private $uid;
    // jwt密鑰
    private $secrect = 'cSWI7BXwInlDsvdSxSQjAXcE32STE6kD';

    // jwt參數(shù)
    private $iss = 'http://example.com';//該JWT的簽發(fā)者
    private $aud = 'http://example.org';//配置聽眾
    private $id = '4f1g23a12aa';//配置ID(JTI聲明)

    /**
     * 獲取token
     * @return string
     */
    public function getToken()
    {
        return (string)$this->token;
    }

    /**
     * 設置類內(nèi)部 $token的值
     * @param $token
     * @return $this
     */
    public function setToken($token)
    {
        $this->token = $token;
        return $this;
    }


    /**
     * 設置uid
     * @param $uid
     * @return $this
     */
    public function setUid($uid)
    {
        $this->uid = $uid;
        return $this;
    }

    /**
     * 得到 解密過后的 uid
     * @return mixed
     */
    public function getUid()
    {
        return $this->uid;
    }

    /**
     * 加密jwt
     * @return $this
     */
    public function encode()
    {
        $time = time();
        $this->token = (new Builder())
            ->setIssuer($this->iss)// Configures the issuer (iss claim)
            ->setAudience($this->aud)// Configures the audience (aud claim)
            ->setId($this->id, true)// Configures the id (jti claim), replicating as a header item
            ->setIssuedAt($time)// Configures the time that the token was issued (iat claim)
            ->setNotBefore($time + 60)// Configures the time that the token can be used (nbf claim)
            ->setExpiration($time + 3600)// Configures the expiration time of the token (exp claim)
            ->set('uid', $this->uid)// Configures a new claim, called "uid"
            ->sign(new Sha256(), $this->secrect)// creates a signature using secrect as key
            ->getToken(); // Retrieves the generated token

        return $this;
    }


    /**
     * 解密token
     * @return \Lcobucci\JWT\Token
     */
    public function decode()
    {

        if (!$this->decodeToken) {
            $this->decodeToken = (new Parser())->parse((string)$this->token);
            $this->uid = $this->decodeToken->getClaim('uid');
        }

        return $this->decodeToken;

    }


    /**
     * 驗證令牌是否有效
     * @return bool
     */
    public function validate()
    {
        $data = new ValidationData();
        $data->setAudience($this->aud);
        $data->setIssuer($this->iss);
        $data->setId($this->id);
        return $this->decode()->validate($data);
    }

    /**
     * 驗證令牌在生成后是否被修改
     * @return bool
     */
    public function verify()
    {
        $res = $this->decode()->verify(new Sha256(), $this->secrect);
        return $res;
    }


    /**
     * 該類的實例
     * @return JwtAuth
     */
    public static function getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * 單例模式 禁止該類在外部被new
     * JwtAuth constructor.
     */
    private function __construct()
    {
    }

    /**
     * 單例模式 禁止外部克隆
     */
    private function __clone()
    {
        // TODO: Implement __clone() method.
    }

}

4、自定義api異常錯誤處理類 ApiException.php

<?php
/**
 * Created by PhpStorm.
 * User: season
 * Date: 2019/4/7
 * Time: 17:46
 */

namespace App\Exceptions;


use Throwable;

class ApiException extends \Exception
{
    public function __construct(array $apiErrConst, Throwable $previous = null)
    {
        parent::__construct($apiErrConst[1], $apiErrConst[0], $previous);
    }
}

5、幫助函數(shù)類Helper.php

<?php
/**
 * Created by PhpStorm.
 * User: season
 * Date: 2019/4/7
 * Time: 17:30
 */

namespace App\Lib;


/**
 * 幫助函數(shù)
 * Class Helper
 * @package App\Lib
 */
class Helper
{
    public static function jsonData($code = 0, $msg = 'Success', $data = [])
    {
        return response()->json([
            'code' => $code,
            'msg' => $msg,
            'data' => $data
        ]);
    }

    public static function jsonSuccessData($data = [])
    {
        $code = 0;
        $msg = 'Success';
        return response()->json([
            'code' => $code,
            'msg' => $msg,
            'data' => $data
        ]);
    }

}

6、重寫\app\Exceptions\Handler.php 中的render方法,手動處理ApiException類型的錯誤

/**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Exception $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        //api接口主動拋出的異常
        if ($exception instanceof ApiException) {
            return Helper::jsonData($exception->getCode(), $exception->getMessage());
        }
        // 暫時不處理非主動異常
        // 如果是純接口系統(tǒng)可以放開此注釋
        /*else{
            dd($exception->getCode());
            $code = $exception->getCode();
            if (!$code || $code<0){
                $code = ApiErr::UNKNOWN_ERR[0];
            }
            $msg =$exception->getMessage()?: ApiErr::UNKNOWN_ERR[1];
            return Helper::jsonData($code,$msg);
        }*/

        return parent::render($request, $exception);
    }

7、api返回狀態(tài)碼描述類 ApiErr.php ,該類在實際項目中要認真定義,此處就按照順序描述了一下。

<?php
/**
 * Created by PhpStorm.
 * User: season
 * Date: 2019/4/7
 * Time: 17:25
 */

namespace App\Lib;


/**
 * 接口錯誤文檔
 * Class ApiErr
 * @package App\Lib
 */
class ApiErr
{
    const SUCCESS = [0,'Success'];
    const UNKNOWN_ERR = [1,'未知錯誤'];
    const ERR_URL = [2,'訪問接口不存在'];
    const TOKEN_ERR = [3,'TOKEN錯誤'];
    const NO_TOKEN_ERR = [4,'TOKEN不存在'];
    const USER_NOT_EXIST = [5,'用戶不存在'];

    //TODO ...
}

8、創(chuàng)建解密jwt的中間件

php artisan make:middleware JwtAuth
<?php

namespace App\Http\Middleware;

use App\Exceptions\ApiException;
use App\Lib\ApiErr;
use Closure;

class JwtAuth
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        //中間件中不能用json_encode
        $token = $request->token;

        if ($token) {
            $jwtAuth = \App\Lib\JwtAuth::getInstance();
            $jwtAuth->setToken($token);
            if ($jwtAuth->validate() && $jwtAuth->verify()) {
                return $next($request);
            } else {
                throw new ApiException(ApiErr::TOKEN_ERR);
            }
        } else {
            throw new ApiException(ApiErr::NO_TOKEN_ERR);
        }
    }
}

之后在app/Http/Kernel.php 文件 $routeMiddleware數(shù)組中增加應用程序的路由中間件。

 'jwt_auth' => JwtAuth::class,

9、在 routes\api.php 中增加兩條路由,并讓user/info路由使用jwt_auth中間件。

Route::get('/user/login', 'JwtTestController@login');

Route::middleware(['jwt_auth'])->group(function () {
    Route::get('user/info', 'JwtTestController@info');
});

10、最后還需要一個業(yè)務控制器 JwtTestController.php

<?php
/**
 * Created by PhpStorm.
 * User: season
 * Date: 2019/4/7
 * Time: 16:01
 */

namespace App\Http\Controllers;


use App\Exceptions\ApiException;
use App\Lib\ApiErr;
use App\Lib\Helper;
use App\Lib\JwtAuth;
use Illuminate\Http\Request;

class JwtTestController
{

    public function login(Request $request)
    {
        //查詢數(shù)據(jù)庫  查詢出對應的用戶信息的uid
        $user = [
            'id' => 1,
            'name' => 'season',
            'email' => "1208082622@qq.com"
        ];

        //如果不存在 user
        if (empty($user))
            throw new ApiException(ApiErr::USER_NOT_EXIST);

        $jwtAuth = JwtAuth::getInstance();
        $token = $jwtAuth->setUid($user['id'])->encode()->getToken();
        return Helper::jsonSuccessData(['token' => $token]);
    }


    public function info(Request $request)
    {
        //也可以放入構(gòu)造方法中  $this->uid= JwtAuth::getInstance()->getUid();
        $uid  = JwtAuth::getInstance()->getUid();
        //查詢數(shù)據(jù)庫  此處模擬數(shù)據(jù)
        $user = [
            'id' => 1,
            'name' => 'season',
            'email' => "1208082622@qq.com"
        ];
        // 用戶不存在
        if (empty($user)) throw new ApiException(ApiErr::USER_NOT_EXIST);
        return Helper::jsonSuccessData([
            'name' => $user['name'],
            'email' => $user['email'],
        ]);

    }
}

為了方便演示,上述代碼沒有分開到兩個控制器書寫。如果該類所有方法都使用jwt_auth中間件,則可以在放入構(gòu)造方法中

$this->uid= JwtAuth::getInstance()->getUid();

之后在方法中直接使用$this->uid即可。
在postman中模擬測試:
用戶登錄接口:


用戶登錄接口

獲取用戶信息接口:


獲取用戶信息接口

以上就是jwt在laravel中的應用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。