php 后端实现JWT认证方法示例


Posted in PHP onSeptember 04, 2018

JWT是什么

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

它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。它具备两个特点:

简洁(Compact):可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快

自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库

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的json为(header.payload.signature):eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.keH6T3x1z7mmhKL1T3r9sQdAxxdzB6siemGMr_6ZOwU
说明:对header和payload进行base64UrlEncode编码后进行拼接。通过key(这里是123456)进行HS256算法签名。

JWT使用流程

  • 初次登录:用户初次登录,输入用户名密码
  • 密码验证:服务器从数据库取出用户名和密码进行验证
  • 生成JWT:服务器端验证通过,根据从数据库返回的信息,以及预设规则,生成JWT
  • 返还JWT:服务器的HTTP RESPONSE中将JWT返还
  • 带JWT的请求:以后客户端发起请求,HTTP REQUEST
  • HEADER中的Authorizatio字段都要有值,为JWT
  • 服务器验证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存储过程
Oct 09 PHP
php array_filter除去数组中的空字符元素
Jun 21 PHP
php隐藏IP地址后两位显示为星号的方法
Nov 21 PHP
php的GD库imagettftext函数解决中文乱码问题
Jan 24 PHP
php数组键值用法实例分析
Feb 27 PHP
YII Framework框架教程之缓存用法详解
Mar 14 PHP
PHP的Yii框架中View视图的使用进阶
Mar 29 PHP
关于ThinkPhp 框架表单验证及ajax验证问题
Jul 19 PHP
PHP获取HTTP body内容的方法
Dec 31 PHP
详解PHP神奇又有用的Trait
Mar 25 PHP
关于Laravel-admin的基础用法总结和自定义model详解
Oct 08 PHP
php反序列化长度变化尾部字符串逃逸(0CTF-2016-piapiapia)
Feb 15 PHP
PHP利用Mysql锁解决高并发的方法
Sep 04 #PHP
TP5(thinkPHP5)框架基于ajax与后台数据交互操作简单示例
Sep 03 #PHP
PHP实现的AES双向加密解密功能示例【128位】
Sep 03 #PHP
PHP实现的日历功能示例
Sep 01 #PHP
thinkPHP5框架分页样式类完整示例
Sep 01 #PHP
php操作mongodb封装类与用法实例
Sep 01 #PHP
thinkphp集成前端脚手架Vue-cli的教程图解
Aug 30 #PHP
You might like
php中大括号作用介绍
2012/03/22 PHP
简单的php文件上传(实例)
2013/10/27 PHP
zf框架的db类select查询器join链表使用示例(zend框架)
2014/03/14 PHP
thinkPHP5.0框架整体架构总览【应用,模块,MVC,驱动,行为,命名空间等】
2017/03/25 PHP
php获取数据库中数据的实现方法
2017/06/01 PHP
如何实现浏览器上的右键菜单
2006/07/10 Javascript
将CKfinder整合进CKEditor3.0的新方法
2010/01/10 Javascript
如何使用jquery动态加载js,css文件实现代码
2013/04/03 Javascript
nodejs分页类代码分享
2014/06/17 NodeJs
用jquery实现动画跳到顶部和底部(这个比较简单)
2014/09/01 Javascript
JS实现数字格式千分位相互转换方法
2016/08/01 Javascript
react.js CMS 删除功能的实现方法
2017/04/17 Javascript
angularjs2中父子组件的数据传递的实例代码
2017/07/05 Javascript
js实现鼠标拖拽多选功能示例
2017/08/01 Javascript
babel之配置文件.babelrc入门详解
2018/02/22 Javascript
vue2.0 + ele的循环表单及验证字段方法
2018/09/18 Javascript
详解小程序云开发数据库
2019/05/20 Javascript
JS数组push、unshift、pop、shift方法的实现与使用方法示例
2020/04/29 Javascript
JavaScript find()方法及返回数据实例
2020/04/30 Javascript
[04:22]DOTA2上海特级锦标赛主赛事第四日TOP10
2016/03/06 DOTA
忘记ftp密码使用python ftplib库暴力破解密码的方法示例
2014/01/22 Python
Django之使用celery和NGINX生成静态页面实现性能优化
2019/10/08 Python
Pandas-Cookbook 时间戳处理方式
2019/12/07 Python
Python3 把一个列表按指定数目分成多个列表的方式
2019/12/25 Python
django教程如何自学
2020/07/31 Python
Python中BeautifulSoup通过查找Id获取元素信息
2020/12/07 Python
HTML5通过调用canvas对象的getContext()方法来获取绘图环境
2014/06/23 HTML / CSS
HTML5页面无缝闪开的问题及解决方案
2020/06/11 HTML / CSS
Monnier Freres中文官网:法国领先的奢侈品配饰在线零售商
2017/11/01 全球购物
诺心蛋糕官网:LE CAKE
2018/08/25 全球购物
Shopee新加坡:东南亚与台湾电商平台
2019/01/25 全球购物
linux面试题参考答案(6)
2014/08/29 面试题
资深地理教师自我评价
2013/09/21 职场文书
弘扬焦裕禄精神践行三严三实心得体会
2014/10/13 职场文书
报案材料怎么写
2015/05/25 职场文书
周一给客户的问候语
2015/11/10 职场文书