PHP JWT 实践

Posted: 2018-07-03  By  vilay  |  Views(416)

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);
        }
}