1.理论 和 简介
1.1 简介
一种客户端 和 服务端的认证机制,让后台知道请求是来自于受信的客户端。
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT是由三段信息构成的,将这三段信息文本用 . 链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
这三段分别是:
- header 头部 ,默认是加密方式是 HS256
- playload 载荷 ,放官方字段 ,过期时间 ,私有字段等
- signature 签名
头部和载荷都是 通过base64 加密编码而成 , 目的是方便网络传输,减少出错
众所周知base64 是可以解密的,所以一些 诸如密码或银行账户的 敏感的信息尽量不要放在 playload 中传输
由于现在很多项目都是前后端分离,restful api模式 , 可以说 restful api认证是jwt的一个很好的应用场景
1.2 安全相关
- 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
- 保护好secret私钥,该私钥非常重要。
- 如果可以,请使用https协议
1.3 JWT 应用流程
1.4 项目github地址
https://github.com/pandamf0079/tptoken
2. 官网 和 资源下载方式
官网: JSON Web Token Introduction - jwt.io
官网中有各种语言的 github demo连接 ,现在我们使用的是
firebase/php-jwt
3. tp5中使用jwt例子
默认使用HS256 加密的方式 ,首先使用composer安装好依赖
composer require firebase/php-jwt
composer require firebase/php-jwt:"v6.0"
- 通过用户名和密码来授权中心获得token
- 将token保存在cookie中,返回到客户端
- 下次请求携带cookie发送到服务器,服务器解析出用户信息
服务端模型:
namespace app\index\model;
use think\Model;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT as JWTUtil;
use Firebase\JWT\Key;
class User extends Model
{
protected $jwt_secret_key = '123Abc#$';
protected $jwt_expire_time = 10;
public function findUser($username,$pass){
if($username=='dapeng' && $pass=='123456'){
return ['user_id'=>10,'username'=>'dapeng','sex'=>1];
}
}
public function createToken($user=[],$exptime=0,$alg='HS256'){
$key = $this->jwt_secret_key;
$time = time();
$expire = $exptime==0?$time+$this->jwt_expire_time:$time+$exptime;
$payload = [
$user,
"iss"=>"PengCompany",
"aud"=>"dapeng",
"iat"=>$time,
"nbf"=>$time,
"exp"=>$expire
];
$jwt = JWTUtil::encode($payload,$key,$alg);
return $jwt;
}
public function verifyToken($token){
$key = $this->jwt_secret_key;
try{
$jwtAuth = json_encode(JWTUtil::decode($token,new Key($key, 'HS256')));
$authInfo = json_decode($jwtAuth,true);
return ['msg'=>'token正常','status'=>1,'data'=>$authInfo[0]];
}catch(ExpiredException $e){
return ['msg'=>'token expire','status'=>2,'data'=>[]];
}catch(\Exception $e){
return ['msg'=>'token error','status'=>3,'data'=>[$e->getMessage()]];
}
}
}
需要注意的是过期时间exp 是时间戳,设置在payload里
控制器方法:
public function login(Request $request){
/*['user_id'=>10,'username'=>'dapeng','sex'=>1]*/
$username = $request->param('user');
$pass = $request->param('pass');
if($username!='' && $pass!=''){
//数据库查询
$userModel = new User();
$users = $userModel->findUser($username,$pass);
$token = $userModel->createToken($users,3600);
Cookie::set('jwt',$token,3600);
$this->success('登录成功','/ucenter');
//return $token;
}else{
$this->error('用户名密码不能为空');
}
}
public function getinfo(Request $request){
$header = Request::instance()->header();
if ($header['authorization'] == 'null'){
echo json_encode([
'status' => 1002,
'msg' => 'token不存在,拒绝访问'
]);
exit;
}else{
$userModel = new User();
echo json_encode($userModel->verifyToken($header['authorization']));
exit();
}
}
接收头部 authorization 的token 并认证
客户端:
<script>
$(document).ready(function(){
var token = getCookie('jwt');
//alert(token);
$.ajax({
url:'http://www.pengcms.com/getinfo',
type:'POST',
headers:{
'Authorization':token
},
dataType: "json",
success:function(res) {
//console.log(res);
var sex ='';
$("#id1").html(res.data.user_id);
$("#id2").html(res.data.username);
if(res.data.sex==1){
sex = '男';
}else{
sex = '女';
}
$("#id3").html(sex);
}
});
})
function getCookie(name)
{
var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)");
if(arr=document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
取cookie ,发送到服务端认证。
4. tp5中使用jwt例子 (使用RS256 openssl)
使用openssl工具:
openssl> genrsa -out rsa_private_key.pem 1024
openssl> rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
和上面不同的是模型层不一样
public function createToken($user=[],$exptime=0,$alg='RS256'){
$private_key = file_get_contents("./keys/rsa_private_key.pem");
if(openssl_pkey_get_private($private_key)){
$time = time();
$expire = $exptime==0?$time+$this->jwt_expire_time:$time+$exptime;
$payload = [
$user,
"iss"=>"PengCompany",//签发组织
"aud"=>"dapeng",//签发作者
"iat"=>$time,
"nbf"=>$time,
"exp"=>$expire
];
//设置过期时间
//JWTUtil::$leeway = 10;
$jwt = JWTUtil::encode($payload,$private_key,$alg);
return $jwt;
}else{
return false;
}
}
public function verifyToken($token){
$public_key = file_get_contents("./keys/rsa_public_key.pem");
try{
$jwtAuth = json_encode(JWTUtil::decode($token,new Key($public_key, 'RS256')));
$authInfo = json_decode($jwtAuth,true);
return ['msg'=>'token正常','status'=>1,'data'=>$authInfo[0]];
}catch(ExpiredException $e){
return ['msg'=>'token expire','status'=>2,'data'=>[]];
}catch(\Exception $e){
return ['msg'=>'token error','status'=>3,'data'=>[$e->getMessage()]];
}
}
5.参考
git地址:https://github.com/firebase/php-jwt