使用场景
json web token 的介绍,网上有很多资料,就不详细解释了。我个人说下对它的理解和应用场景。很多资料喜欢解释它的一个应用场景是,比如在简书上,有人关注你了,会给你邮箱发送一个链接,然后登陆到简书网站上取查看关注者。这种情况下,如果你没有登陆网站,就需要自己先登陆。如果使用JWT,则就不需要你在登陆了。通过这个场景,我来说下认证用户这方面原理
认证用户一般在网站上是账号密码登陆,存入session。 很多App 接口还是先请求登陆接口,然后获取到 token,再去请求登陆后的操作。所以,不管是 session 还是 JWT,都是解决用户认证的问题。这两种方式,本质上都是通过一个 token,也就是不能让外人知道的值,来验证这条请求的合法性,因为理论上只有你自己的客户端才知道你的 token。
JWT 也是如此,只要服务端根据客户的请求数据,验证 token 值一致,就能够证明数据是正常渠道来的。因为加密的时候加密方式和加密的 key 值是只有你们自己才有的。如果需要什么数据,你直接再 post 或者 get 中加入自己的键值对即可。JWT 的优势
一、token 值可以直接根据加密跟则加密比对,不需要查询数据库。减少服务器压力。即使是 session 存储,也会占用服务器内存
二、payload 值可以自定义这条请求数据的有效时间(exp 字段),防止别人得到token后利用
三、hash 或者 rsa 加密,同样的数据,每次加密的值都是不同的,也就是token 值可变,提高了数据的安全性,但是登陆请求的那种,基本上 token 是固定的。
四、加密时候有个 key 值或者rsa加密的公私钥,是只有自己的服务端和客户端才知道的,只要这个值不泄露,加密比对就不会有误
五、JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。除了用户id之外,还可以存储其他的和用户相关的信息,例如该用户是否是管理员、用户所在的分层
数据库 token 加密的原理
一般的APP 接口中,都会先登录,然后返回给你 user_id 和 token 等用户信息。但是你请求登录后的接口时,一般会带上这两个参数。token 一般有个过期时间。token 可以理解成可以被外人捕捉到的密码,因为他会过期,所以即使别人抓取到了,token 会有过期时间,也比抓取到密码强。
其实传递 token 就可以获取到用户数据,根本不需要传递 user_id 。之所以传递 user_id , 是因为查询的时候,使用 user_id 查询会快,然后比对 user_id 对应的 token ,与请求的 token 是否匹配。但是直接比对 token ,也是可以的。 因为token 是唯一值,跟密码不一样。比如是在使用TeamView
找回密码时,人家就是直接带了 token 值。你点击就是直接登录的状态。
但是 JWT 的token 与 数据库的这种 token 不一样,JWT token 是认证这条数据的合法性,也就是你这个 token 只要匹配了,你就是正常的用户请求,可以做任何操作,都是安全的。但是数据库的这种 token 是你数据库中的一个固定值,是认证用户用的。
- index.php 生成加密数据
<?php
define('HASH_KEY', 123456);// 自定义 hash 加密得 key
// header 部分
$head_json = '{
"typ": "JWT",
"alg": "SHA256"
}';
$head = base64_encode($head_json);
// payload 部分
$payload = '{
"iss": "John Wu JWT",
"iat": 1441593502,
"exp": 144159472211,
"aud": "www.example.com",
"sub": "jrocket@example.com",
"from_user": "B",
"target_user": "A"
}';
$payload = base64_encode($payload);
// signature
$sign = hash_hmac(strtolower((json_decode($head_json, true))['alg']), $head . $payload, HASH_KEY);
echo $head;
echo '<hr>';
echo $payload;
echo '<hr>';
echo $sign;
echo '<hr>';
echo $head . '.' . $payload . '.' . $sign;
- server.php 中谭政 token ,获取数据
<?php
define('HASH_KEY', 123456);// 自定义 hash 加密得 key
/**
* 获取头部的 token
*/
function getBearerToken()
{
if (!isset($_SERVER['HTTP_' . strtoupper('Authorization')])) {
return false; // 头部不存在 Authorization
}
$auth = $_SERVER['HTTP_' . strtoupper('Authorization')];
if (substr($auth, 0, strlen('Bearer ')) !== 'Bearer ') {
return false; // token 不存在
}
$token = substr($auth, 7); // Bearer 字符串和空格之后的字符串,是从7开始的
return $token;
}
$token = getBearerToken();
// 1. 检测 token 值
if (!$token) {
echo '清闲登陆';
}
list($head, $body, $sign) = explode('.', $token);
$head_info = json_decode(base64_decode($head), true);
$body_info = json_decode(base64_decode($body), true);
// 2.检测 信息是否过去
if ($body_info['exp'] <= time()) {
echo 'token 已经过期';
}
$algo = $head_info['alg'];
$hash = hash_hmac(strtolower($algo), $head . $body, HASH_KEY);
$verify = hash_equals($sign, $hash);
echo $verify;