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等等)。
例如:
JWT的第二部分是payload,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。
Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
Public claims : 可以随意定义。
Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。
下面是一个例子:
对payload进行Base64编码就得到JWT的第二部分
注意,不要在JWT的payload或header中放置敏感信息,除非它们是加密的。
signature
为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。
例如:
HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)
签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的token,它还可以验证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.****.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();
//放在任意方法都可以
//然后带着这个token去路由中请求就会获得