1. JWT介绍
本文是在TP6.0使用JWT的示例
JWT全称: JSON Web Token,以 token 的方式代替传统的cookie、session模式,用于各服务器、客户端传递信息及签名验证
2. 新增自定义函数 fault()
在 app/common.php
中新增以下函数
/**
* 抛出异常错误
*
* @param string $msg
* @param integer $code
*/
function fault(string $msg = "", $code = 201)
{
throw new \Exception($msg, $code);
}
3. 新增配置文件 jwt.php
在全局配置目录 config
目录下新建 jwt.php
文件,文件内容如下
<?php
// +----------------------------------------------------------------------
// | JWT (Json Web Token) 配置
// +----------------------------------------------------------------------
// | Author: liang 30041321@qq.com
// +----------------------------------------------------------------------
return [
'iss' => 'liang', // 签发者
'aud' => 'chen', // 接收者
'key' => 'yang', // 访问密钥
'prefix' => 'jwt_', // 缓存前缀
'exp' => 864000, // 过期时间,864000秒=10天
'single_sign_on' => false, // 单点登录 true 开启 false 关闭
];
4. JWT 功能封装类
安装扩展包
composer require firebase/php-jwt
<?php
// +----------------------------------------------------------------------
// | JWT (Json Web Token) 功能封装类
// +----------------------------------------------------------------------
// | Author: liang 30041321@qq.com
// +----------------------------------------------------------------------
declare(strict_types=1);
use Firebase\JWT\JWT;
class JwtAuth
{
// +------------------------------------------------------------------
// | 初始化配置
// +------------------------------------------------------------------
/**
* 初始化配置
*/
public function __construct()
{
$this->iss = config('jwt.iss'); //签发者 可选
$this->aud = config('jwt.aud'); //接收该JWT的一方,可选
$this->exp = config('jwt.exp'); //过期时间,864000秒 = 10天
$this->key = config('jwt.key'); //访问秘钥
$this->prefix = config('jwt.prefix'); //缓存前缀
}
// +------------------------------------------------------------------
// | 创建、解析 token
// +------------------------------------------------------------------
/**
* 创建token
*
* @param array $data
* @return string token
*/
public function encode(array $data)
{
$time = time(); //当前时间
$token = [
'iss' => $this->iss, //签发者 可选
'aud' => $this->aud, //接收该JWT的一方,可选
'iat' => $time, //签发时间
'nbf' => $time, //(Not Before):某个时间点后才能访问,比如设置time+30,表示当前时间30秒后才能使用
'exp' => $time + $this->exp, //过期时间
'data' => $data, //附加数据
];
$token = JWT::encode($token, $this->key); // 创建token
$this->cache($data['uid'], $token); // 将token存入缓存
return $token; // 返回token
}
/**
* 解析token
*
* @param string $token 前端请求携带的token
*/
public function decode(string $token)
{
try {
JWT::$leeway = 0; //当前时间减去60,把时间留点余地
return JWT::decode($token, $this->key, ['HS256']); //HS256方式,这里要和签发的时候对应
} catch (\Firebase\JWT\SignatureInvalidException $e) { //签名不正确
fault('签名不正确');
} catch (\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用
fault('登录未生效');
} catch (\Firebase\JWT\ExpiredException $e) { // token过期
fault('登录过期');
} catch (\Exception $e) { //其他错误
fault($e->getMessage());
}
}
// +------------------------------------------------------------------
// | 检测token
// +------------------------------------------------------------------
/**
* 将用户token存入缓存,用于单点登录校验
*
* @param int $id 用户id
* @param string $token 服务器端生成的token
*/
private function cache(int $uid, string $token)
{
// 缓存token
cache($this->prefix . $uid, $token);
}
/**
* 检测token是否已过期(单点登录)
*
* @param int $id 用户id
* @param string $token 前端请求携带的token
* @return boolean true token 有效 false 已过期
*/
public function checkToken(int $id, string $token)
{
// 判断是否开启单点登录校验 true 开启 false 关闭
if (config('jwt.single_sign_on')) {
// 获取缓存中该用户的token
$cacheToken = cache($this->prefix . $id);
// true 有效 false 已过期
return $token === $cacheToken;
} else {
return true; // 没有开启单点登录直接返回true
}
}
}
5. JWT 鉴权控制器
创建鉴权控制器,所有需要进行Token校验的控制器只需要继承该控制器即可
php think make:controller api@Auth --plain
鉴权控制器文件内容如下:
<?php
// +----------------------------------------------------------------------
// | JWT (Json Web Token)
// +----------------------------------------------------------------------
// | Author: liang 30041321@qq.com
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace app\api\controller;
use JwtAuth;
use app\BaseController;
/**
* JWT 鉴权控制器
*/
class Auth extends BaseController
{
/**
* 校验token是否有效
*/
protected function initialize()
{
// 接收请求头中的Token
$token = request()->header('token');
// 初步校验token
empty($token) && fault('token不能为空');
// 解析token,返回生成token时的附加数据
$this->jwt = app(JwtAuth::class)->decode($token)->data;
// 获取附加数据中的用户id并转为整型
$this->uid = (int) $this->jwt->uid;
// 携带的token和缓存中的token进行比对(单点登录校验)
if (!app(JwtAuth::class)->checkToken($this->uid, $token)) {
fault('登录状态已过期', 401);
}
}
}