美文网首页
在Laravel中使用JWT

在Laravel中使用JWT

作者: dongshixiao | 来源:发表于2019-04-07 20:24 被阅读0次

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中的应用。

相关文章

网友评论

      本文标题:在Laravel中使用JWT

      本文链接:https://www.haomeiwen.com/subject/opqeiqtx.html