PHP JWT验证的实践

介绍

官网

JWT JSON Web Tokens

JWT 格式 header.payload.signature

header

{
"typ":"JWT",
"alg":"SHA256"
}

payload

待补充

示例

<?php
/**
 * api接口基类控制器
 * @authors vilay
 */

namespace api\controllers;

use Yii;
use yii\web\Controller;

class ApiController extends Controller
{
		public $data;//存储数据

		public $jwt;//jwt字符串

		/**
		 * 允许授权的域名
		 * @var [type]
		 */
		public $aud = [
				'app id'=>'app name',
		];

		private $secret_key = 'app secret';

		public $enableCsrfValidation = false;

		public function init()
		{
				if (!Yii::$app->request->headers->has('Authorization')) {
						return false;
				}
				$headers = Yii::$app->request->headers->get('Authorization');
				$this->data = json_decode(Yii::$app->request->post('data'),true);
		}


		/**
		 * 响应json数据
		 * @param  array      $data 数组
		 * @param  string      $msg  消息提示
		 * @param  int|integer $code 状态码  0 表示数据错误或者请求非法,200 请求成功
		 * @return [type]            [description]
		 * 基本数据返回
		 * {
						"code": 200,
						"data": {
								"token": "token"
						},
						"msg": ""
				}

				列表数据返回
				{
						"code":200,
						'data': {
								'lists':{

								}, //列表数据集
								'total':50,//总纪录数
								'page':2, //下一页页码,客户端请求需要返回
								'next':1,//是否有下一页
						},
						'msg':''
				}
		 */
		public function jsonData(array $data, string $msg='', int $code=200):array 
		{
				$response = [
						'code' => $code,
						'data' => $data,
						'msg' => $msg
				];

				return $response;
		}

		/**
		 *  客户端请求token
		 * @return [type] [description]
		 */
		public function actionGetToken()
		{
				//验证合法请求id
				if (!array_key_exists($this->data['aud'], $this->aud)) {
						return false;
				}
				//验证合法请求来源域名,测试阶段注释
				// $referer_url = Yii::$app->request->referrer;
				// if (false === strpos($referer_url, $this->aud[$this->data['aud']])) {
				//     return false;
				// }

				$data = [
						'aud' => $this->aud[$this->data['aud']],
						'jti' => $this->data['aud']
				];

				$token = $this->encode($data);
				Yii::$app->response->format = 'json';
				return $this->jsonData(['token'=>$token]);
		}


		/**
		 * 生成JWT数据
		 * @param  array  $params [description]
		 * $params = [
		 *     'aud' => 'receive',//接收方
		 *     'jti' => 'app id',//客户端id
		 * ]
		 * @return [type]         header.payload.signature
		 */
		private function encode(array $params):string
		{
				$secret_key = md5($this->secret_key);
				$alg = 'SHA256';
				$header = [
						'typ' => 'JWT',
						'alg' => $alg
				];
				$now = time();
				$payload = [
						'iss' => 'vilay',
						'iat' => $now, //token创建世界
						'exp' => $now + 3600*8,//token过期时间
						'aud' => $params['aud'],
						'jti' => $params['jti']
				];
				$jwt = $this->base64JsonEncode($header).'.'.$this->base64JsonEncode($payload);

				return $jwt.'.'.$this->signature($jwt, $secret_key, $alg);
		}


		/**
		 * base64,json编码
		 * @param  [type] $data [description]
		 * @return [type]       [description]
		 */
		private function base64JsonEncode($data)
		{
				return base64_encode(json_encode($data));
		}

		/**
		 * base64,json解码
		 * @param  [type] $data [description]
		 * @return [type]       [description]
		 */
		private function base64JsonDecode($data)
		{
				return json_decode(base64_decode($data),true);
		}


		private function decode(string $jwt)
		{
				$secret_key = md5($this->secret_key);
				$tokens = explode('.',$jwt);
				if (count($tokens) != 3) {
						return false;
				}

				list($header, $payload, $signature) = $tokens;

				$headers = $this->base64JsonDecode($header);
				if (empty($headers['alg']) || empty($headers['typ'])) {
						return false;
				}

				if ($signature != $this->signature($header.'.'.$payload, $secret_key, $headers['alg'])) {
						return false;
				}

				$payload = $this->base64JsonDecode($payload);

				$now = time();

				if (isset($payload['iat']) && $payload['iat'] > $now) {
						return false;
				}

				if (isset($payload['exp']) && $payload['exp'] < $now) {
						return false;
				}

				return $payload;
		}

		/**
		 * 签名字符串
		 * @return [type] [description]
		 */
		private function signature(string $jwt, string $secret_key, string $alg='SHA256'):string
		{
				return hash_hmac($alg, $jwt, $secret_key);
		}
}
}