php实现JWT(json web token)鉴权实例详解


Posted in PHP onNovember 05, 2019

JWT是什么

JWT是json web token缩写。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证。基于token的身份验证可以替代传统的cookie+session身份验证方法。

JWT由三个部分组成:header.payload.signature

以下示例以JWT官网为例

header部分:

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

对应base64UrlEncode编码为:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

说明:该字段为json格式。alg字段指定了生成signature的算法,默认值为 HS256,typ默认值为JWT

payload部分:

{
 "sub": "1234567890",
 "name": "John Doe",
 "iat": 1516239022
}

对应base64UrlEncode编码为:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

说明:该字段为json格式,表明用户身份的数据,可以自己自定义字段,很灵活。sub 面向的用户,name 姓名 ,iat 签发时间。例如可自定义示例如下:

{
  "iss": "admin",     //该JWT的签发者
  "iat": 1535967430,    //签发时间
  "exp": 1535974630,    //过期时间
  "nbf": 1535967430,     //该时间之前不接收处理该Token
  "sub": "www.admin.com",  //面向的用户
  "jti": "9f10e796726e332cec401c569969e13e"  //该Token唯一标识
}

signature部分:

HMACSHA256(
 base64UrlEncode(header) + "." +
 base64UrlEncode(payload),
 123456
)

对应的签名为:keH6T3x1z7mmhKL1T3r9sQdAxxdzB6siemGMr_6ZOwU

最终得到的JWT的Token为(header.payload.signature):eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.keH6T3x1z7mmhKL1T3r9sQdAxxdzB6siemGMr_6ZOwU
说明:对header和payload进行base64UrlEncode编码后进行拼接。通过key(这里是123456)进行HS256算法签名。

JWT使用流程

  1. 初次登录:用户初次登录,输入用户名密码
  2. 密码验证:服务器从数据库取出用户名和密码进行验证
  3. 生成JWT:服务器端验证通过,根据从数据库返回的信息,以及预设规则,生成JWT
  4. 返还JWT:服务器的HTTP RESPONSE中将JWT返还
  5. 带JWT的请求:以后客户端发起请求,HTTP REQUEST
  6. HEADER中的Authorizatio字段都要有值,为JWT
  7. 服务器验证JWT

PHP如何实现JWT

作者使用的是PHP 7.0.31,不废话,直接上代码,新建jwt.php,复制粘贴如下:

<?php
/**
 * PHP实现jwt
 */
class Jwt {

  //头部
  private static $header=array(
    'alg'=>'HS256', //生成signature的算法
    'typ'=>'JWT'  //类型
  );

  //使用HMAC生成信息摘要时所使用的密钥
  private static $key='123456';

  /**
   * 获取jwt token
   * @param array $payload jwt载荷  格式如下非必须
   * [
   * 'iss'=>'jwt_admin', //该JWT的签发者
   * 'iat'=>time(), //签发时间
   * 'exp'=>time()+7200, //过期时间
   * 'nbf'=>time()+60, //该时间之前不接收处理该Token
   * 'sub'=>'www.admin.com', //面向的用户
   * 'jti'=>md5(uniqid('JWT').time()) //该Token唯一标识
   * ]
   * @return bool|string
   */
  public static function getToken(array $payload)
  {
    if(is_array($payload))
    {
      $base64header=self::base64UrlEncode(json_encode(self::$header,JSON_UNESCAPED_UNICODE));
      $base64payload=self::base64UrlEncode(json_encode($payload,JSON_UNESCAPED_UNICODE));
      $token=$base64header.'.'.$base64payload.'.'.self::signature($base64header.'.'.$base64payload,self::$key,self::$header['alg']);
      return $token;
    }else{
      return false;
    }
  }

  /**
   * 验证token是否有效,默认验证exp,nbf,iat时间
   * @param string $Token 需要验证的token
   * @return bool|string
   */
  public static function verifyToken(string $Token)
  {
    $tokens = explode('.', $Token);
    if (count($tokens) != 3)
      return false;

    list($base64header, $base64payload, $sign) = $tokens;

    //获取jwt算法
    $base64decodeheader = json_decode(self::base64UrlDecode($base64header), JSON_OBJECT_AS_ARRAY);
    if (empty($base64decodeheader['alg']))
      return false;

    //签名验证
    if (self::signature($base64header . '.' . $base64payload, self::$key, $base64decodeheader['alg']) !== $sign)
      return false;

    $payload = json_decode(self::base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY);

    //签发时间大于当前服务器时间验证失败
    if (isset($payload['iat']) && $payload['iat'] > time())
      return false;

    //过期时间小宇当前服务器时间验证失败
    if (isset($payload['exp']) && $payload['exp'] < time())
      return false;

    //该nbf时间之前不接收处理该Token
    if (isset($payload['nbf']) && $payload['nbf'] > time())
      return false;

    return $payload;
  }

  /**
   * base64UrlEncode  https://jwt.io/ 中base64UrlEncode编码实现
   * @param string $input 需要编码的字符串
   * @return string
   */
  private static function base64UrlEncode(string $input)
  {
    return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
  }

  /**
   * base64UrlEncode https://jwt.io/ 中base64UrlEncode解码实现
   * @param string $input 需要解码的字符串
   * @return bool|string
   */
  private static function base64UrlDecode(string $input)
  {
    $remainder = strlen($input) % 4;
    if ($remainder) {
      $addlen = 4 - $remainder;
      $input .= str_repeat('=', $addlen);
    }
    return base64_decode(strtr($input, '-_', '+/'));
  }

  /**
   * HMACSHA256签名  https://jwt.io/ 中HMACSHA256签名实现
   * @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload)
   * @param string $key
   * @param string $alg  算法方式
   * @return mixed
   */
  private static function signature(string $input, string $key, string $alg = 'HS256')
  {
    $alg_config=array(
      'HS256'=>'sha256'
    );
    return self::base64UrlEncode(hash_hmac($alg_config[$alg], $input, $key,true));
  }
}

  //测试和官网是否匹配begin
  $payload=array('sub'=>'1234567890','name'=>'John Doe','iat'=>1516239022);
  $jwt=new Jwt;
  $token=$jwt->getToken($payload);
  echo "<pre>";
  echo $token;

  //对token进行验证签名
  $getPayload=$jwt->verifyToken($token);
  echo "<br><br>";
  var_dump($getPayload);
  echo "<br><br>";
  //测试和官网是否匹配end

  //自己使用测试begin
  $payload_test=array('iss'=>'admin','iat'=>time(),'exp'=>time()+7200,'nbf'=>time(),'sub'=>'www.admin.com','jti'=>md5(uniqid('JWT').time()));;
  $token_test=Jwt::getToken($payload_test);
  echo "<pre>";
  echo $token_test;

  //对token进行验证签名
  $getPayload_test=Jwt::verifyToken($token_test);
  echo "<br><br>";
  var_dump($getPayload_test);
  echo "<br><br>";
  //自己使用时候end

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
如何在PHP中使用Oracle数据库(3)
Oct 09 PHP
使用 php4 加速 web 传输
Oct 09 PHP
PHP加Nginx实现动态裁剪图片方案
Mar 10 PHP
PHP中使用sleep造成mysql读取失败的案例和解决方法
Aug 21 PHP
php清空(删除)指定目录下的文件,不删除目录文件夹的实现代码
Sep 04 PHP
浅析iis7.5安装配置php环境
May 10 PHP
PHP基于socket实现客户端和服务端通讯功能
Jul 13 PHP
[原创]PHP实现SQL语句格式化功能的方法
Jul 28 PHP
PHP5.6.8连接SQL Server 2008 R2数据库常用技巧分析总结
May 06 PHP
php反射学习之不用new方法实例化类操作示例
Jun 14 PHP
laravel异步监控定时调度器实例详解
Jun 21 PHP
ThinkPHP5分页paginate代码实例解析
Nov 10 PHP
详解Laravel服务容器的绑定与解析
Nov 05 #PHP
php+laravel依赖注入知识点总结
Nov 04 #PHP
PHP保存Base64图片base64_decode的问题整理
Nov 04 #PHP
详解laravel passport OAuth2.0的4种模式
Nov 04 #PHP
laravel返回统一格式错误码问题
Nov 04 #PHP
php 中self,this的区别和操作方法实例分析
Nov 04 #PHP
PHP 文件写入和读取操作实例详解【必看篇】
Nov 04 #PHP
You might like
php列出一个目录下的所有文件的代码
2012/10/09 PHP
Yii学习总结之安装配置
2015/02/22 PHP
php简单实现多字节字符串翻转的方法
2015/03/31 PHP
PHP new static 和 new self详解
2017/02/19 PHP
Javascript 面向对象(一)(共有方法,私有方法,特权方法)
2012/05/23 Javascript
JavaScript高级程序设计(第3版)学习笔记12 js正则表达式
2012/10/11 Javascript
选择器中含有空格在使用示例及注意事项
2013/07/31 Javascript
jQuery中复合属性选择器用法实例
2014/12/31 Javascript
JavaScript使用Math.Min返回两个数中较小数的方法
2015/04/06 Javascript
angularjs学习笔记之双向数据绑定
2015/09/26 Javascript
Javascript实现通过选择周数显示开始日和结束日的实现代码
2016/05/30 Javascript
JavaScript中三个等号和两个等号的区别(== 和 ===)浅析
2016/09/22 Javascript
基于Three.js插件制作360度全景图
2016/11/29 Javascript
使用openSpeDiv方法实现Ecshop登录弹窗框效果
2017/03/13 Javascript
Vue.Draggable拖拽功能的配置使用方法
2020/07/29 Javascript
详解Vue项目部署遇到的问题及解决方案
2019/01/11 Javascript
Vue最新防抖方案(必看篇)
2019/10/30 Javascript
vue 清空input标签 中file的值操作
2020/07/21 Javascript
Python计算三维矢量幅度的方法
2015/06/15 Python
Python中文竖排显示的方法
2015/07/28 Python
Python数据结构与算法之图的广度优先与深度优先搜索算法示例
2017/12/14 Python
python常用库之NumPy和sklearn入门
2019/07/11 Python
美国鞋类购物网站:Shiekh Shoes
2016/08/21 全球购物
Sneaker Studio乌克兰:购买运动鞋
2018/03/26 全球购物
J2SDK1.5与J2SDK5.0有什么区别
2012/09/19 面试题
数据库笔试题
2013/05/09 面试题
应届毕业生个人自我评价
2013/09/20 职场文书
园林资料员岗位职责
2013/12/30 职场文书
国庆65周年演讲稿:回首往昔,展望未来
2014/09/21 职场文书
乡镇党的群众路线教育实践活动总结报告
2014/10/30 职场文书
2014年加油站站长工作总结
2014/12/23 职场文书
多人股份制合作协议书
2016/03/19 职场文书
公文写作:教你写“建议书”
2019/05/07 职场文书
golang 如何用反射reflect操作结构体
2021/04/28 Golang
MySQL 聚合函数排序
2021/07/16 MySQL
如何解决php-fpm启动不了问题
2021/11/17 PHP