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 上一篇,下一篇文章实现代码与原理说明
May 09 PHP
配置php网页显示各种语法错误
Sep 23 PHP
PHP对象递归引用造成内存泄漏分析
Aug 28 PHP
php使用$_POST或$_SESSION[]向js函数传参
Sep 16 PHP
推荐一款MAC OS X 下php集成开发环境mamp
Nov 08 PHP
php生成zip文件类实例
Apr 07 PHP
php集成套件服务器xampp安装使用教程(适合第一次玩PHP的新手)
Jun 03 PHP
PHP简单获取随机数的常用方法小结
Jun 07 PHP
深入理解PHP中mt_rand()随机数的安全
Oct 12 PHP
Laravel模型事件的实现原理详解
Mar 14 PHP
PHP开发api接口安全验证操作实例详解
Mar 26 PHP
基于PHP实现发微博动态代码实例
Dec 11 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数组相加 array(“a”)+array(“b”)结果还是array(“a”)
2012/09/19 PHP
开源php中文分词系统SCWS安装和使用实例
2014/04/11 PHP
php验证手机号码
2015/11/11 PHP
PHP解决中文乱码
2017/04/28 PHP
javascript动画效果类封装代码
2007/08/28 Javascript
jquery中的sortable排序之后的保存状态的解决方法
2010/01/28 Javascript
图片onload事件触发问题解决方法
2011/07/31 Javascript
jQuery把表单元素变为json对象
2013/11/06 Javascript
jquery ajax 局部刷新小案例
2014/02/08 Javascript
Extjs grid添加一个图片状态或者按钮的方法
2014/04/03 Javascript
jQuery学习笔记之toArray()
2014/06/09 Javascript
js添加select下默认的option的value和text的方法
2014/10/19 Javascript
如何解决ligerUI布局时Center中的Tab高度大小
2015/11/24 Javascript
jQuery简单实现列表隐藏和显示效果示例
2016/09/12 Javascript
jQuery实现遍历复选框的方法示例
2017/03/06 Javascript
node.js+jQuery实现用户登录注册AJAX交互
2017/04/28 jQuery
jquery插件开发之选项卡制作详解
2017/08/30 jQuery
vuejs点击class变化的实例
2018/09/05 Javascript
angular4应用中输入的最小值和最大值的方法
2019/05/17 Javascript
Vue+Element实现网页版个人简历系统(推荐)
2019/12/31 Javascript
python3.5 + PyQt5 +Eric6 实现的一个计算器代码
2017/03/11 Python
Python中with及contextlib的用法详解
2017/06/08 Python
Python一键查找iOS项目中未使用的图片、音频、视频资源
2019/08/12 Python
Django集成celery发送异步邮件实例
2019/12/17 Python
python3+opencv 使用灰度直方图来判断图片的亮暗操作
2020/06/02 Python
pytorch下的unsqueeze和squeeze的用法说明
2021/02/06 Python
亚马逊中国官方网站:amazon.cn
2017/05/25 全球购物
德国旅游网站:weg.de
2018/06/03 全球购物
致裁判员加油稿
2014/02/08 职场文书
2014迎新年晚会策划方案
2014/02/23 职场文书
行政秘书工作自我鉴定
2014/09/15 职场文书
二手房购房协议书范本
2014/10/05 职场文书
2016新年慰问信范文
2015/03/25 职场文书
普希金诗歌赏析(6首)
2019/08/22 职场文书
详解前端任务构建利器Gulp.js使用指南
2021/04/30 Javascript
SpringBoot整合minio快速入门教程(代码示例)
2022/04/03 Java/Android