PHP之JWT接口鉴权(一)

1.什么是JWT
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
2.什么时候应该用到JWT

  • Authorization (授权) :
    这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。

  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web
    Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。
    3.JWT的组成

  • Header

  • Payload

  • Signature

header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。
例如:
PHP之JWT接口鉴权(一)
JWT的第二部分是payload,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。

Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
Public claims : 可以随意定义。
Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。
下面是一个例子:
PHP之JWT接口鉴权(一)

对payload进行Base64编码就得到JWT的第二部分

注意,不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

signature
为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。

例如:

HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)

签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。

来一张官网的图吧:
PHP之JWT接口鉴权(一)
来一个JWT几个函数的解释

名词 解释
iss (issuer) issuer 请求实体,可以是发起请求的用户的信息,也可是jwt的签发者
sub (Subject) 设置主题,类似于发邮件时的主题
aud (audience) 接收jwt的一方
exp (expire) token过期时间
nbf (not before) 当前时间在nbf设定时间之前,该token无法使用
iat (issued at) token创建时间
//上面对JWT进行了说明,接下来就是代码了
//去JWT的官网https://jwt.io/#libraries-io 下载PHP的JWT包
//这里我使用的是laravel框架 在命令行里执行
composer require lcobucci/jwt  //这是官方提供的代码,在你下载JWT包的时候就可以看到
//运行成功后就会在你的项目vendor\下生成lcobucci\jwt文件
//接下来就是代码编写
//我们自己在app/Commend/Auth/下封装一个JwtAuth.php类
//JwtAuth.php类
<?php
namespace App\Common\Auth;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\ValidationData;
//链式调用
class JwtAuth{
    private static $instance;
    private $token;
    private $uid;
    private $secrect='*&%[email protected]#@#!#!^&^%*^';//这里是随便写的一串编码
    private $decodeToken;
    /**
     * 获取jwtAuth句柄
     * @return JwtAuth
     */
    public static function getInstance(){
        if(is_null(self::$instance)){
            self::$instance = new self();
        }
        return self::$instance;
    }
    private function __construct()
    {
        //构成单例
    }
    private function __clone()
    {
        //构成单例
    }
    public function encode(){
        $time=time();
        $this->token=(new \Lcobucci\JWT\Builder())
            ->setHeader('alg','HS256')
            ->setIssuer('www.laravel.com')//设置发行人
            ->setAudience('zjw')//设置接收人
            ->setIssuedAt($time)//设置生成token的时间
            ->setExpiration($time+3600)//设置过期
            ->set('uid',$this->uid)//给token设置一个ID
            ->sign(new \Lcobucci\JWT\Signer\Hmac\Sha256(),$this->secrect) //对上面的信息使用share256算法加密
            ->getToken();//获取token
        return $this;

    }
    //将token输出成字符串
    public function getToken(){
        return (string)$this->token;
    }

    /**
     * 设置TOKEN
     * @param $token
     * @return $this
     */
    public function setToken($token){
        $this->token=$token;
        return $this;
    }
    public function setUid($uid){
        $this->uid=$uid;
        return $this;
    }

    public function decode(){
        if(!$this->decodeToken){
            $this->decodeToken=(new Parser())->parse((string)$this->token);
            $this->uid = $this->decodeToken->getClaim('uid');
        }
        return $this->decodeToken;
    }
    //验证令牌
    public function validate(){
        $data= new ValidationData();
        $data->setIssuer('www.laravel.com');
        $data->setAudience($this->uid);
        return $this->decode()->validate($data);
    }
    //验证最后一串是否一致
    public function verify(){
        //echo $this->secrect;die;
        $result=$this->decode()->verify(new \Lcobucci\JWT\Signer\Hmac\Sha256(),$this->secrect);
        return $result;
    }
}

//这个类封装好之后,我们自己定义一个可访问的方法
public function author(){
	return $this->jsonSuccessData([
            'id'=>1,
            'name'=>'zhaojiawei'
        ]);
}
//定义好方法后我们需要用到laravel的中间件
//laravel的中间件用法,详情看https://blog.csdn.net/qq_42805749/article/details/88871363
//执行 php artisan make:middleware JwtAuthMiddleware
<?php

namespace App\Http\Middleware;

use App\Common\Auth\JwtAuth;
use App\Http\Controllers\Controller;
use App\Http\Response\ResponseJson;
use App\Model\permession\PermessionModel;
use Closure;

class JwtAuthMiddleware
{
    use ResponseJson;
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $token=$request->input('token');
        if(!empty($token)){
            $jwtauth=JwtAuth::getInstance();

            $jwtauth->setToken($token);
            if($jwtauth->validate() && $jwtauth->verify()){
                return $next($request);
            }else{
                return $this->jsonData('1','登录过期');
            }
        }else{
            return $this->jsonData('2','参数错误');
        }
    }
}
//定义好中间件后,记得在Kernel.php中添加定义的中间件,添加成功后在web.php中声名一个路由,路由的middleware指向Kernel.php定义的中间件名称
//JWT
Route::group(["prefix"=>"crontab","middleware"=>"jwt_auth"],function (){
    //获取token
    Route::get("index","crontab\[email protected]")->name("crontab.index");
});
//下面这个是我自己封装的一个转化json格式类,建议放在app\http\Response\下
<?php

namespace App\Http\Response;


trait ResponseJson{
    /**
     * 返回一个json
     * @param $code
     * @param $message
     * @param $data
     * @return string
     */
    public function jsonResponse($code,$message,$data){
        $content=[
            'code'=>$code,
            'msg'=>$message,
            'data'=>$data
        ];
        return json_encode($content,JSON_FORCE_OBJECT);
        //return response()->json($content);
    }

    /**
     * App接口调用成功时的返回
     * @param array $data
     * @return string
     */
    public function jsonSuccessData($data=[]){
        return $this->jsonResponse(0,'Success',$data);
    }

    /**
     * 出现异常的返回
     * @param $code
     * @param $message
     * @param array $data
     * @return string
     */
    public function jsonData($code,$message,$data=[]){
        //echo 123;die;
        return $this->jsonResponse($code,$message,[]);
    }
}
//获取token的方法
		$jwtauth=JwtAuth::getInstance();
        $token=$jwtauth->setUid(1)->encode()->getToken();
//放在任意方法都可以

PHP之JWT接口鉴权(一)
//然后带着这个token去路由中请求就会获得
PHP之JWT接口鉴权(一)