JWT实现原理,php实现JWT实例

JWT描述

  1. JWT 全程(json web token)
  2. JWT 就是一个字符串,经过加密处理与校对处理的字符串
  3. JWT 由header(头部),payload(主体),signature(签名)三部分组成
  4. JWT 组成形式 base64UrlHeader.base64UrlPayload.base64Url(hash(base64UrlHeader.base64UrlPayload))
  5. 生成样式:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

header组成

{
	"typ":"JWT",
	"alg":"HS256"
}
  • alg:加密算法
  • typ:默认JWT

payload组成(内容自定义)

{
	'alg' => 'sha256',//验证加密算法是否跟使用的一致
	'iat' => $dataTime,//签发时间
	'exp' => $dataTime + 6,//过期时间
	'username' =>'admin',//用户账号
}

signature组成

base64Url(hash(base64UrlHeader.base64UrlPayload))

JWT流程

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

JWT代码实现:获取token,验证token

class JwtByPhp
{
	private static $secret = '!@#$%^&'; //密钥 俗称盐
	//头部
	private static $header = [
		'alg' => 'sha256', 	//加密使用算法
		'typ' => 'JWT'		//类型
	];
	
	
	/**
	* 获取对应token信息
	* @param {object} payload 主体内容
	* 
	* @return
	*/
	public static function getToken(array $payload)
	{
		if(is_array($payload)){
			//第一步将头部数组转换成json字符串,将传递过来的内容转成json字符串
			$jsonHeader = json_encode(self::$header,JSON_UNESCAPED_UNICODE);//中文不转为unicode
			$jsonPayload = json_encode($payload,JSON_UNESCAPED_UNICODE);
			//第二步进行base64Url加密
			$base64UrlHeader = self::base64UrlEncode($jsonHeader);
			$base64UrlPayload = self::base64UrlEncode($jsonPayload);
			//第三部整合前两个部分以‘.’链接
			$signate = $base64UrlHeader.'.'.$base64UrlPayload;
			//第四步使用算法加密signate
			$hashSignate = self::signature($signate, self::$secret, self::$header['alg']);
			//第五步将加密后的signate再次base64加密
			$base64UrlSignate = self::base64UrlEncode($hashSignate);
			//第六步将base64加密后的3个部分以‘.’拼接
			$token = $base64UrlHeader.'.'.$base64UrlPayload.'.'.$base64UrlSignate;
			return $token;
		}
		return false;
	}
	
	
	/**
	* 验证token正确性
	* @param {object} token
	* 
	* @return
	*/
	public static function verifyToken(string $token)
	{
		//第一步将token分割
		$tokenArr = explode('.',$token);
		//去除数组中空值
		$tokenArr = array_filter($tokenArr);
		if(count($tokenArr) !== 3){
			return false;
		}
		list($base64UrlHeader,$base64UrlPayload,$base64UrlSignate) = $tokenArr;
		//第二步进行相应解密
		$base64UrlDecodeHeader = self::base64UrlDecode($base64UrlHeader);
		$base64UrlDecodePayloadr = self::base64UrlDecode($base64UrlPayload);
		$base64UrlDecodeSignate = self::base64UrlDecode($base64UrlSignate);
		//第三步转回原本数据格式
		$jsonDecodePayload = json_decode($base64UrlDecodePayloadr,true);
		$jsonDecodeHeader = json_decode($base64UrlDecodeHeader,true);
		//验证加密方式
		if(!isset($jsonDecodePayload['alg']) || $jsonDecodePayload['alg'] !== self::$header['alg']){
			return ['code'=>1001,'msg'=>'token规则错误'];
		}
		//验证token签发时间
		$time = time();//当前时间
		if(!isset($jsonDecodePayload['iat']) || $jsonDecodePayload['iat'] > $time){
			return ['code'=>1002,'msg'=>'时间错误,无效token'];
		}
		//验证token过期时间
		$time = time();//当前时间
		if(!isset($jsonDecodePayload['exp']) || $jsonDecodePayload['exp'] < $time){
			return ['code'=>1003,'msg'=>'时间过期,无效token'];
		}
		//验证token正确性
		$sign = $base64UrlHeader.'.'.$base64UrlPayload;
		$hashSign = self::signature($sign, self::$secret, self::$header['alg']);
		$base64UrlSign = self::base64UrlEncode($hashSign);
		if($base64UrlSignate !== $base64UrlSign){
			return ['code'=>1004,'msg'=>'无效token'];
		}
		return ['code'=>200,'msg'=>'token正确'];
	}
	
	
	/**
	* base64加密字符串去掉特殊字符
	* @param {object} string 需加密字符串
	* 
	* @return
	*/
	private static function base64UrlEncode(string $string)
	{
		//第一步base64加密字符串
		$base64Str = base64_encode($string);
		//第二步替换加密后的特殊字符;由于=、+、/在URL 里面有特殊含义
		/*$base64Str = str_replace('=','',$base64Str);
		$base64Str = str_replace('+','-',$base64Str);
		$base64Str = str_replace('/','_',$base64Str);*/
		return str_replace(['=','+','/'],['','-','_'],$base64Str);
	}
	
	
	/**
	* base64解密字符串还原特殊字符
	* @param {object} string 需还原字符串
	* 
	* @return
	*/
	private static function base64UrlDecode(string $string)
	{
		//解密原理:1,字符串长度除以3向上取整乘以4等于编码后的字符串长度
		$remainder = strlen($string) % 3; //计算出‘=’原本数量
		if($remainder !== 0){
			$string .= str_repeat('=',$remainder);
		}
		//替换原本base64特殊字符
		$string = str_replace(['-','_'],['+','/'],$string);
		return base64_decode($string);
	}
	
	
	/**
	* base64加密字符串去掉特殊字符
	* @param {object} string 需加密字符串
	* @param {object} secret 密钥,俗称盐
	* @param {object} algo 	加密方式
	* 
	* @return
	*/
	private static function signature(string $string,string $secret,string $algo)
	{
		return hash_hmac($algo,$string,$secret,true);
	}
}


//调用测试

$jwt = new JwtByPhp();

$dataTime = time();
//自定义主题内容
$arr = [
	'alg' => 'sha256',
	'iat' => $dataTime,
	'exp' => $dataTime + 6,
	'username' =>'admin',
];

$token = $jwt::getToken($arr);
echo '
'.$token.'
'
; //eyJhbGciOiJzaGEyNTYiLCJ0eXAiOiJKV1QifQ.eyJhbGciOiJzaGEyNTYiLCJpYXQiOjE1ODk1MjcwNzMsImV4cCI6MTU4OTUyNzA3OSwidXNlcm5hbWUiOiJhZG1pbiJ9.r4uhwtlBshX5BKgutoGqIFGvqzcICbvi3tqbsLLnFkM sleep(2); //验证 $a = $jwt::verifyToken($token); print_r($a); //Array ( [code] => 200 [msg] => token正确 )

你可能感兴趣的:(PHP)