早之前大家常用的登录方式是客户端传输账号密码,后台通过比对后成功了,客户端存储在cookie中,每次刷新页面或请求服务端,服务端都需要从数据库中调取对比信息;对于服务端来说,对比登录状态是服务端请求最频繁的动作,所以如果每次都调用数据库请求的话,会降低服务端相应速度,增加服务端的压力。
所以现在很多项目开始使用token验证,其中token验证中有一种是无状态验证,那么什么是无状态呢?
1、服务端不保存任何客户端请求者信息;
2、客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份。
无状态有什么有点?
1、客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务;
2、服务端的集群和状态对客户端透明;
3、服务端可以任意的迁移和伸缩;
4、减小服务端存储压力。
实现代码(PHP):
/**
* 生成token
*/
public function setToken($name,$id){
//用户名、此时的时间戳,并将过期时间拼接在一起
$admin = $name; //获取前台传来的用户账号
$time = time();
$aging = 86400;
$end_time = time()+$aging;
$tTr=$this->number_encrypt($time); //自定义方法(给数字进行加密,可解密,下方有实例)
$tEtr=$this->number_encrypt($end_time);
$idS=$this->number_encrypt($id);
$info = $admin. '.' . $id . '.' .$time.'.'.$end_time;
$infoE= base64_encode($admin). '.' . $idS . '.' .$tTr.'.'.$tEtr;
//根据以上信息信息生成签名(密钥为 siasqr)
$signature = hash_hmac('md5',$info,'siasqr');
//最后将这两部分拼接起来,得到最终的Token字符串
$token = $infoE . '.' . $signature;
return [
'token'=>$token,
'name'=>$admin,
'id'=>$id,
'aging'=>$aging+10
];
}
/**
* 数字自定义加密
*/
public function number_encrypt($n){
$dictionary=['0'=>'aZ','1'=>'Bw','2'=>'cYq','3'=>'Dx','4'=>'eV','5'=>'FvU','6'=>'gT','7'=>'Hs','8'=>'iRp','9'=>'Jo','.'=>'kL'];
$nS=(string)$n;
$nA=str_split($nS);
$r='';
foreach ($nA as $val) {
$r.=isset($dictionary[$val])?$dictionary[$val]:'';
}
return $r;
}
/**
* 数字解密
*/
public function number_decrypt($s){
$dictionary=[0=>'/aZ/',1=>'/Bw/',2=>'/cYq/',3=>'/Dx/',4=>'/eV/',5=>'/FvU/',6=>'/gT/',7=>'/Hs/',8=>'/iRp/',9=>'/Jo/',10=>'/kL/'];
$rp=[0=>'0',1=>'1',2=>'2',3=>'3',4=>'4',5=>'5',6=>'6',7=>'7',8=>'8',9=>'9',10=>'.'];
$r=preg_replace($dictionary,$rp,$s);
return $r;
}
/**
* 检查token
*/
public function check_token($token)
{
/**** api传来的token ****/
if(!isset($token) || empty($token))
{
$msg['error']=1;
$msg['msg']='Illegal request';
return $msg;
}
//对比token
$explode = explode('.',$token);//以.分割token为数组
if(!empty($explode[0]) && !empty($explode[1]) && !empty($explode[2]) && !empty($explode[3]) && !empty($explode[4]))
{
$stTime=$this->number_decrypt($explode[2]);
$edTime=$this->number_decrypt($explode[3]);
$name=base64_decode($explode[0]);
$id=$this->number_decrypt($explode[1]);
if($edTime-$stTime>604800){
$msg['error']=1;
$msg['msg']='Illegal request';
return $msg;
}
if($stTime>time()){
$msg['error']=1;
$msg['msg']='Illegal request';
return $msg;
}
$info = $name.'.'.$id.'.'.$stTime.'.'.$edTime;//信息部分
$true_signature = hash_hmac('md5',$info,'siasqr');//正确的签名
if(time() > $this->number_decrypt($explode[3]))
{
$msg['error']=1;
$msg['msg']='Token已过期,请重新登录';
return $msg;
}
if ($true_signature == $explode[4])
{
//检查是否有刷新
$rf=$this->refresh_token(['name'=>$name,'id'=>$id,'stTime'=>$stTime]);
if($rf==false){
$msg['name']=$name;
$msg['id']=$id;
$msg['rfs']=0;
return $msg;
}
$msg['aging']=$rf['aging'];
$msg['token']=$rf['token'];
$msg['name']=$name;
$msg['id']=$id;
$msg['rfs']=1;
return $msg;
}
else
{
$msg['error']=1;
$msg['msg']='Signature failed';
return $msg;
}
}
else
{
$msg['error']=1;
$msg['msg']='Signature failed';
return $msg;
}
}
/**
* 刷新token
*/
private function refresh_token($exToken){
if(time()-$exToken['stTime']>=420){
return $this->setToken($exToken['name'],$exToken['id']);
}
return false;
}
结语:以上代码亲测可用,但不是最优,如您有更好的方式方法,欢迎一起交流。