内容简介:在很多计算机系统里面都少不了用户认证这一步骤,最常见的认证就是账号密码认证,也就是注册、登录这一流程。在现实生活中,人也需要认证,大家应该都有个很多Web系统里面token就类似于身份证,账号密码就相当于咱的户口本和本人,需要核对账号密码后获取,拿到token之后就可以使用一些需要认证的服务,而且token也有有效期,和身份证一样,理论上token必须是唯一。
在很多计算机系统里面都少不了用户认证这一步骤,最常见的认证就是账号密码认证,也就是注册、登录这一流程。
在现实生活中,人也需要认证,大家应该都有个 身份证 ,回想一下这个身份证是从哪里来的呢? 办过身份证的应该都知道,一般情况下,身份证需要本人带着 户口本 去 公安局 (不知道现在改了木有?)办理,工作人员在核对了相关信息,确认无误的情况下会给你颁发一个身份证, 有效期 一般是10-20年,在一些需要认证的时候,你就可以拿出身份证 校验 核对身份,比如买火车票,出国,或者办理其它证件.
很多Web系统里面token就类似于身份证,账号密码就相当于咱的户口本和本人,需要核对账号密码后获取,拿到token之后就可以使用一些需要认证的服务,而且token也有有效期,和身份证一样,理论上token必须是唯一。
2.常见的Web认证方式
1.HTTP Basic Auth
这种方式在早期一些Web系统比较常见,就是那种在浏览器弹出一个框让你输账号密码那种,简单易用,但是缺点一个不安全,其账号密码其实是明文(base64encode)传输的,而且每次都得带上。另外就是太丑了。。。
2.Cookies\Session
这种认证方式其实就是类似我们最开始说的身份证这种,只需要输入一次账号密码,认证成功后,系统会将用户信息存入 session ,session是服务器的本地存储功能,然后系统根据session生成一个唯一的 sessionid 以cookies的形式发送给浏览器。
cookies是浏览器本地存储,在这套机制里面的作用是用来存储sessionid,你也可以不使用cookies存储,早期有些网站在一些不支持cookies的浏览器上面会把sessionid追加到url上面。
cookies里面存储的sessionid其实就是相当于身份证编号,每次访问网站里面我们带着这个编号,服务器拿着编号就可以找到对应的session里面存储的信息,一般情况下里面会存储一些用户信息,比如uid。
讲道理这套机制其实问题并不大,大部分时候都管用,但是cookies有一个毛病就是无法跨域,很多大公司有很多网站,这些网站域名可能还不一样。而且cookies对现在的手机APP支持不好,原生并不支持cookies。最后,就是服务器存储session也需要一些开销,特别是用户特别多的情况下。还有其它缺点这里就不列出来了,很多文章都有写到。
但是其实我想说这套机制大部分情况下是够用的,特别是对于一些中小型网站来说,简单易用,快速开发。
3.JWT
一般说到JWT都会提到token,在我的理解里面token其实就是一个字符串,它可以是jwt token,也可以是sessionid token,token就是是一个携带认证信息的字符串。
网上关于介绍JWT的文章特别多,大同小异,我们这里也懒的再说一遍了,贴一个大神的教程,我觉得讲的挺清晰了, JSON Web Token 入门教程 。
简单的说,JWT本质上是一种解决方案标准,该方案下一个token应该有3部分组成: Header、Payload、Signature , 其中前2部分差不多就是明文的,都是 json 对象,里面存了一些信息,使用 base64urlencode 编码成一个字符串。最后的 Signature 是前面2个元素和 secret 一起加密之后的结果,加密算法默认是 SHA256 , 这个 secret 应该只有服务器知道,解密的时候需要用到。
最后生成的token是一个比较长的字符串,当用户登录成功之后可以把这个串返回给浏览器,浏览器下次请求的时候带着这个串就行了,问题来了,怎么带?很多文章说放到cookies里面,讲道理放到cookies里面那和sessionid有啥区别? 标准做法是放到HTTP请求的头信息Authorization字段里面。
服务器拿到这个串,首先把前面2段的Header和Payload使用 base64urldecode 解码出来,然后使用刚才使用的加密算法和secret校验一下是否和第3段的signature一样,如果不一样,则说明这个Token是伪造的,如果一样,就可以相信Payload里面的信息了,一般Payload里面会存放一些用户信息,比如uid,如果Payload里面需要存放一些敏感信息,比如手机号,建议先加密Payload。
PHP实战
下面我将使用PHP构建一个简单的例子:
JWT类:
<?php namespace App; class Jwt { private $alg = 'sha256'; private $secret = "123456"; /** * alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT */ public function getHeader() { $header = [ 'alg' => $this->alg, 'typ' => 'JWT' ]; return $this->base64urlEncode(json_encode($header, JSON_UNESCAPED_UNICODE)); } /** * Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用,这里可以存放私有信息,比如uid * @param $uid int 用户id * @return mixed */ public function getPayload($uid) { $payload = [ 'iss' => 'admin', //签发人 'exp' => time() + 600, //过期时间 'sub' => 'test', //主题 'aud' => 'every', //受众 'nbf' => time(), //生效时间 'iat' => time(), //签发时间 'jti' => 10001, //编号 'uid' => $uid, //私有信息,uid ]; return $this->base64urlEncode(json_encode($payload, JSON_UNESCAPED_UNICODE)); } /** * 生成token,假设现在payload里面只存一个uid * @param $uid int * @return string */ public function genToken($uid) { $header = $this->getHeader(); $payload = $this->getPayload($uid); $raw = $header . '.' . $payload; $token = $raw . '.' . hash_hmac($this->alg, $raw, $this->secret); return $token; } /** * 解密校验token,成功的话返回uid * @param $token * @return mixed */ public function verifyToken($token) { if (!$token) { return false; } $tokenArr = explode('.', $token); if (count($tokenArr) != 3) { return false; } $header = $tokenArr[0]; $payload = $tokenArr[1]; $signature = $tokenArr[2]; $payloadArr = json_decode($this->base64urlDecode($payload), true); if (!$payloadArr) { return false; } //已过期 if (isset($payloadArr['exp']) && $payloadArr['exp'] < time()) { return false; } $expected = hash_hmac($this->alg, $header . '.' . $payload, $this->secret); //签名不对 if ($expected !== $signature) { return false; } return $payloadArr['uid']; } /** * 安全的base64 url编码 * @param $data * @return string */ private function base64urlEncode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } /** * 安全的base64 url解码 * @param $data * @return bool|string */ private function base64urlDecode($data) { return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT)); } } 复制代码
测试:
<?php $jwt = new \App\Jwt(); //获取token $token = $jwt->genToken(1); //解密token $uid = $jwt->verifyToken($token); var_dump($uid); 复制代码
以上代码仅供参考,实际应用的话最好找个现成的库,不推荐重复造轮子,jwt的思想是通用的,不分语言,github上面有很多。。。这里贴一个PHP的库: firebase/php-jwt 。
最后再说说session和jwt的选择问题,网上随便搜搜就可以看到很多文章比较这2者优劣,总结就是各有利弊,实际上很多公司既不是session,也不是jwt,可能就是自己搞的类似jwt token这样的一个字符串,然后放在cookies里面,只要这个串能够代表一个用户都可以。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 聊聊 Nacos 配置隔离和分类的使用
- 聊聊在 Go 语言里使用继承的翻车经历
- 聊聊在Go语言里使用继承的翻车经历
- 服务端指南 数据存储篇 | 聊聊 MongoDB 使用场景
- 服务端指南 数据存储篇 | 聊聊 Redis 使用场景
- 从Memcache转战Redis,聊聊缓存使用填过的“坑”
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Rationality for Mortals
Gerd Gigerenzer / Oxford University Press, USA / 2008-05-02 / USD 65.00
Gerd Gigerenzer's influential work examines the rationality of individuals not from the perspective of logic or probability, but from the point of view of adaptation to the real world of human behavio......一起来看看 《Rationality for Mortals》 这本书的介绍吧!