在Laravel中使用JWT

php版本要求:7.0+
之前写过一篇原生的php操作jwt的demo https://www.jianshu.com/p/1f6d2804b674
平时工作中一直在使用laravel,所以整理一下在laravel中应该怎么使用jwt来开发api接口。

大体思路:
用户登录拿到token,之后需要登录的请求拿着token,并使用中间件解密。之后的业务层面和使用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、创建 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参数
    private $iss = 'http://example.com';//该JWT的签发者
    private $aud = 'http://example.org';//配置听众
    private $id = '4f1g23a12aa';//配置ID(JTI声明)

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

    /**
     * 设置类内部 $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、帮助函数类Helper.php

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

namespace App\Lib;


/**
 * 帮助函数
 * 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());
        }
        // 暂时不处理非主动异常
        // 如果是纯接口系统可以放开此注释
        /*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返回状态码描述类 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、创建解密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数组中增加应用程序的路由中间件。

 '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、最后还需要一个业务控制器 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)
    {
        //查询数据库  查询出对应的用户信息的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)
    {
        //也可以放入构造方法中  $this->uid= JwtAuth::getInstance()->getUid();
        $uid  = JwtAuth::getInstance()->getUid();
        //查询数据库  此处模拟数据
        $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中间件,则可以在放入构造方法中

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

之后在方法中直接使用$this->uid即可。
在postman中模拟测试:
用户登录接口:


用户登录接口

获取用户信息接口:


获取用户信息接口

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270