jwt
goer ... 2022-01-15 大约 5 分钟
# jwt
jwt:
(Json Web Token)
用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
jwt介绍:(jwt&session)[https://www.jianshu.com/p/576dbf44b2ae]
# 流程
用户 用户名密码来 请求服务器
服务器进行验证用户的信息
服务器通过验证发送给用户一个 token
客户端存储token,并在每次请求时附送上这个token值
服务端验证token值,并返回数据
保护好secret私钥,该私钥非常重要
这样服务器也不用存储session这些了
// 服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了
Access-Control-Allow-Origin: *;
1
2
2
# 应用
test.onclick = function(){
var user = $('#user').val();
var pass = $('#pass').val();
$.ajax({
type:"post",
url:'http://tp.cc/login2', //后端接口
data:{
user:user,
pass:pass,
},
dataType:'json',
success(data){
if(data.code == 404){
alert(data.msg);
}
if(data.code == 1){
// 请求成功,返回数据,存储到 缓存中 后端得到的token
localStorage.setItem('user',data.data['acctoken']);
console.log(localStorage.user);
alert(data.msg);
}
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
后端登录验证
// 路由加一个
'login2' => 'Home/Login/login2', //home控制器的
1
2
2
// 公共的 返回函数
function JsonRes($msg="获取失败",$code=0,$data=null){
$arr = [
'msg' => $msg,
'code' => $code,
'data'=> $data
];
echo json_encode($arr);
exit;
}
// login2方法
public function login2()
{
$user = request()->post('user');
$pass = request()->post('pass');
$where['user'] = $user;
$where['pass'] = $pass;
// 查询数据库 判断用户名和密码
$link = Db::name('user')->where($where)->select();
if(!$link){
JsonRes('用户名密码不正确',404);
}
$jwt_data = [ // 建议但不强制使用
'iss' =>'admin', //该JWT的签发者
'iat' => time(), //签发时间
'exp' => time()+60, //过期时间
'nbf' => time(), //该时间之前不接收处理该Token
'sub' => 'aaaaaa', //面向的用户
'jti' => md5(uniqid('Jwt')).time(), //该Token唯一标识
'user_info' => [ //自己定义的内容
'id' => $link[0]['id'],
'name' => $link[0]['user'],
],
];
$jwt = new Jwt;
$tokne = $jwt->getToken($jwt_data); //颁发签名
$data = ['acctoken'=> $tokne]; // token存到数组内
JsonRes('登录成功',1,$data);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
JWT
类
// jwt密钥不要放到这里 放到配置文件config
<?
namespace app\Home\controller;
/**
* PHP实现jwt
*/
class Jwt {
//头部
private static $header=array(
'alg'=>'HS256', //生成signature的算法
'typ'=>'JWT' //类型
);
//使用HMAC生成信息摘要时所使用的密钥 jwt密钥不要放到这里 config
private static $key='2315srwqe';
/**
* 获取jwt token
* @param array $payload jwt载荷 格式如下非必须
* [
* 'iss'=>'jwt_admin', //该JWT的签发者
* 'iat'=>time(), //签发时间
* 'exp'=>time()+7200, //过期时间
* 'nbf'=>time()+60, //该时间之前不接收处理该Token
* 'sub'=>'www.admin.com', //面向的用户
* 'jti'=>md5(uniqid('JWT').time()) //该Token唯一标识
* ]
* @return bool|string
*/
public static function getToken($payload)
{
if(is_array($payload))
{
$base64header=self::base64UrlEncode(json_encode(self::$header,JSON_UNESCAPED_UNICODE));
$base64payload=self::base64UrlEncode(json_encode($payload,JSON_UNESCAPED_UNICODE));
$token=$base64header.'.'.$base64payload.'.'.self::signature($base64header.'.'.$base64payload,self::$key,self::$header['alg']);
return $token;
}else{
return false;
}
}
/**
* 验证token是否有效,默认验证exp,nbf,iat时间
* @param string $Token 需要验证的token
* @return bool|string
*/
public static function verifyToken($Token)
{
$tokens = explode('.', $Token);
if (count($tokens) != 3)
return false;
list($base64header, $base64payload, $sign) = $tokens;
//获取jwt算法
$base64decodeheader = json_decode(self::base64UrlDecode($base64header), JSON_OBJECT_AS_ARRAY);
if (empty($base64decodeheader['alg']))
return false;
//签名验证
if (self::signature($base64header . '.' . $base64payload, self::$key, $base64decodeheader['alg']) !== $sign)
return false;
$payload = json_decode(self::base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY);
//签发时间大于当前服务器时间验证失败
if (isset($payload['iat']) && $payload['iat'] > time())
return false;
//过期时间小宇当前服务器时间验证失败
if (isset($payload['exp']) && $payload['exp'] < time())
return false;
//该nbf时间之前不接收处理该Token
if (isset($payload['nbf']) && $payload['nbf'] > time())
return false;
return $payload;
}
/**
* base64UrlEncode https://jwt.io/ 中base64UrlEncode编码实现
* @param string $input 需要编码的字符串
* @return string
*/
private static function base64UrlEncode($input)
{
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}
/**
* base64UrlEncode https://jwt.io/ 中base64UrlEncode解码实现
* @param string $input 需要解码的字符串
* @return bool|string
*/
private static function base64UrlDecode($input)
{
$remainder = strlen($input) % 4;
if ($remainder) {
$addlen = 4 - $remainder;
$input .= str_repeat('=', $addlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
/**
* HMACSHA256签名 https://jwt.io/ 中HMACSHA256签名实现
* @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload)
* @param string $key
* @param string $alg 算法方式
* @return mixed
*/
private static function signature($input, $key, $alg = 'HS256')
{
$alg_config=array(
'HS256'=>'sha256'
);
return self::base64UrlEncode(hash_hmac($alg_config[$alg], $input, $key,true));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
Base类,基础类,要有token才能访问 所以要==认证的页面都可以继承这个控制器==
namespace app\Home\controller;
use think\Controller;
use think\Request;
class Base extends Controller
{
// thinkphp _initialize()方法是在任何方法执行之前,都要执行的
// php __construct构造函数仅仅在创建对象的时候调用一次
public function _initialize()
{
// 父类初始化有内容时候,子类会继承。如果子类也有初始化,
// 如果要保留父类初始化内容就要加入parent::_initialize()
parent::_initialize();
// 获取请求头tokne
$this->checkToken();
}
private function checkToken(){
// 获取head头 authorization
$header = Request::instance()->header();
if(empty($header['authorization']) || $header['authorization'] == null){
echo json_encode([
'status' => 0,
'msg' => 'token 不存在,拒绝访问',
]);
exit;
}else{
// 如果有token 进行验证
$chekJwtToken = $this->verKey($header['authorization']);
//dump($token);exit;
if(!$chekJwtToken || $chekJwtToken['exp']<time()){
JsonRes('1001','会话失效,请刷新一下页面进行登录');
}
}
}
// 判断token是否正确
private function Verkey($token)
{
$jwt = new Jwt;
$getpload = $jwt->verifyToken($token);
return $getpload;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
打印获取到的请求头, 如果header()方法获取不到token
在.htaccess加一个
#增加下面这项 SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
1
2
sex.onclick = function(){
var str = localStorage.getItem("user"); //string
// var currUser = JSON.parse(str);
// var token = 'Bearer ' + currUser;
var token = localStorage.getItem('user');
$.ajax({
type:'post',
url:'http://tp.cc/sex',
data:{
id:'11',
token:localStorage.user,
},
// 只要继承了Base的控制器都必须带有这个请求头部
// haders头,必须要带着
headers:{Authorization:localStorage.getItem('user')},
dataType:'json',
success(data){
if(data.id == 11){
alert(1);
}
},
error(data){
console.log(data);
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 都可以加请求头部 // beforeSend: function(request) { // request.setRequestHeader("Authorization", '111'); // }, // headers: { // 'top':'test', // },
1
2
3
4
5
6
7
ok了,只要继承了Base控制器,就必须带有凭证