jwt

goer ... 2022-01-15 Study
  • Jwt
  • Hack
大约 5 分钟

jwt

# 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

# 应用

前端登录 本地缓存 (opens new window)

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

后端登录验证

// 路由加一个
'login2'           =>  'Home/Login/login2',    //home控制器的
1
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

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

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

打印获取到的请求头, 如果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
// 都可以加请求头部
// beforeSend: function(request) {
//         request.setRequestHeader("Authorization", '111');
// },
// headers: {
//     'top':'test',
// },
1
2
3
4
5
6
7

ok了,只要继承了Base控制器,就必须带有凭证