传统互联网项目在实现保持登录状态、退出登录、接口请求等功能时会使用Session,但是众所周知Session数据在产生后会存储与服务器端,所以当用户量达到一定程度会相应影响到服务器的性能,且Session在前后端分离的项目中或是多服务器项目中的支持不是很好。但是Token不会产生这些问题,服务器端对Token只有生成和验证操作,不会存放数据,针对前后端分离的项目,包括手机APP和当前热门的小程序的支持都很不错,所以Token成为了用于验证的极好选择。
JWT全称为Json Web Token,是一种基于Token的身份认证方案,JWT的安全特性可以使Token不被修改和伪造。
那么接下来就具体说说JWT如何在ThinkPHP5.1框架中得到应用:
首先我们需要使用Composer来为我们的TP5.1项目安装JWT。在TP5.1项目中使用命令行运行以下代码即可自动下载安装:
composer require firebase/php-jwt
运行后出现以下字样并在框架vendor下可以找到firebase即代表下载安装成功。
首先我们可以进入JWT的官网,首页有一个Debugger可以用于查看生成与验证Token,除了官网我更推荐使用JWT的浏览器插件,不论哪种方法,最终使用效果都是一样的,我这里使用JWT插件来演示,首先打开Debugger就可以看到官方生成用于展示的Token:
可以清楚看到整个Token分为三个部分,每个部分之间有一个 . 作为隔断,首先红色的部分为头部(Header):说明此串码为JWT和加密所用的算法信息,紫色的部分为负载(Payload):用于添加想要传递的信息和Token基本设置等,最后蓝色的部分为验证签名(verify signature):用于密匙加密解密Token。知道了Token基本的格式后我们就可以开始使用TP5.1生成一个JWT看看效果了,为了方便演示,我将生成的函数放在了公共函数文件common.php中。
function createToken($adminId=666)
{
$secret = "THIS_IS_SECRET"; //密匙
$payload = [
'iss'=>'sol', //签发人(官方字段:非必需)
'exp'=>time()+3600*24*7, //过期时间(官方字段:非必需)
'aud'=>'admin', //受众(官方字段:非必需)
'nbf'=>time(), //生效时间(官方字段:非必需)
'iat'=>time(), //签发时间(官方字段:非必需)
'admin_id'=>$adminId, //自定义字段
'admin'=>true //自定义字段
];
$token = JWT::encode($payload,$secret,'HS256');
return $token;
}
代码中使用JWT生成Token需要三个参数,分别是负载(Payload)、密匙(secret)、加密算法(algorithm),其中密匙是由自己规定的,之后接收到传来的Token时需要使用此密匙来验证接收到的Token,负载部分为想要传输的信息,其中有一些字段为官方定义的,但是并不是必需的,除此之外我们还可以定义一些自己的私有字段用来传输额外信息,例如以上实例中将签发时间和生效时间设置为当前时间,Token过期时间为当前时间的七天后,并加上了自己定义的管理员ID字段和是否是管理员的字段。
定义好这些信息后就可以使用JWT的静态方法encode来生成一个Token,这里我使用halt来获取一下使用此函数生成的Token:
string(225) "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJpc3MiOiJzb2wiLCJleHAiOjE1NjU1OTYyNzYsImF1ZCI6ImFkbWluIiwibmJmIjoxNTY0OTkxNDc2LCJpYXQiOjE1NjQ5OTE0NzYsImFkbWluX2lkIjoiNjY2IiwiYWRtaW4iOnRydWV9.
jCVh-13ZOBEiIORdmw9Ye191X9ZJNyjo4fN96umG878"
然后就可以复制一下这段Token使用官方的Debugger来测试一下是否正确:
可以看到右侧PAYLOAD里的信息和我们函数中定义的信息一致,但是下方显示验证失败,这是因为Debugger自动生成的Secret并不正确,将黑框内的secret改为我们定义的密匙:THIS_IS_SECRET后便验证成功了。
同样在common.php中写入验证的函数:
function checkToken($token)
{
try{
$Result = JWT::decode($token,'THIS_IS_SECRET',['HS256']);
return '验证成功';
}
catch (Exception $e)
{
return '验证失败';
}
}
验证Token需要的静态方法decode同样需要三个参数,第一个是需要验证的Token,第二个是验证的密匙,第三个是加密算法。
如果验证成功则$Result的值为Token的负载(Payload)部分。如果验证失败的话则会抛出异常,所以Token验证环节需要用try catch
,这里对异常捕捉没有具体细分,不论是密匙错误还是Token过期还是其他错误统统都是验证失败,如果想将异常信息获取的具体一点可以使用firebase中的三个异常信息来捕获,请自己测试。
在具体业务中生成Token往往用于首次登录或者修改密码等信息后重登陆或者同账号异地登陆顶号操作,在用户进行操作时可以延长Token过期时间避免正在操作时突然提示Token过期重登陆破坏使用体验。后台生成Token和其他信息一起返回前端后需要将Token信息保存在本地存储或Cookie中,在下一次请求发送时将Token放在HTTP请求头中一起发送,后台在接收到后先验证Token是否正确可用在进行之后操作。
请求头添加Token只需在Ajax请求中添加以下字段即可:
headers:{ token:'HTTP HEADER AJAX TOKEN TEST'}
在后台获取只需使用Request静态方法单独获取头部token字段即可获取Token内容并进行后续的验证操作:
$token = Request::header('token');