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中的应用。
网友评论