网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
同样进行Base64编码后,字符串如下:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
>
> 请注意,**默认情况下JWT是未加密的,因为只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT中**,以防止信息泄露。**JWT只是适合在网络中传输一些非敏感的信息**
>
>
>
### Signature
签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名
Signature = HMACSHA256(base64Url(header)+.+base64Url(payload),secretKey)
一旦前面两部分数据被篡改,只要服务器加密用的密钥没有泄露,得到的签名肯定和之前的签名不一致
在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用`.`分隔,就构成整个JWT对象
![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/2831644abac59cffc5d203f6fb0636d1.png#pic_center)
注意JWT每部分的作用,在服务端接收到客户端发送过来的JWT token之后:
* header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据
* signature由于使用了不可逆的加密算法,无法解码出原文,它的作用是校验token有没有被篡改。服务端获取header中的加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一致。注意secretKey只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,secretKey实际上代表的是盐值
## 如何实现
`Token`的使用分成了两部分:
* 生成token:登录成功的时候,颁发token
* 验证token:访问某些资源或者接口时,验证token
### 生成 token
借助第三方库`jsonwebtoken`,通过`jsonwebtoken` 的 `sign` 方法生成一个 `token`:
const jwt = require(‘jsonwebtoken’);
jwt.sign(Payload, secretkey, option)
* 第一个参数指的是 Payload
* 第二个是秘钥,服务端特有
* 第三个参数是 option,可以定义 token 过期时间,单位是秒
### 校验token
使用 `jwt.verify(token, secretkey, (error, data)=>{})` 进行验证
* secret 必须和 sign 时候保持一致
### 登录注册实例
#### 服务端
##### 定义生成token和校验token的函数
创建jwt.js模块,定义生成token和校验token的函数
const jwt = require(‘jsonwebtoken’);
const secretkey = ‘howdoyoudo’; //密钥
// 生成token
const sign = (data={}) => {
return jwt.sign(data, secretkey, {
expiresIn: 60*60,
});
};
// 验证token
const verify = (req, res, next) => {
let authorization = req.headers.authorization || req.body.token || req.query.token || ‘’;
let token = ‘’;
if (authorization.includes(‘Bearer’)) {
token = authorization.replace('Bearer ', ‘’);
} else {
token = authorization;
}
jwt.verify(token, secretkey, (error, data) => {
if (error) {
res.json({ error: 1, data: ‘token验证失败’ });
} else {
req._id = data._id;
next();
}
});
};
module.exports = {
sign,
verify,
};
##### 登录成功生成token
const router = require(‘express’).Router();
const md5 = require(‘md5’);
const User = require(‘…/modules/user’);
const jwt = require(‘…/modules/jwt’);
// 登录成功生成token
router.post(‘/login’, (req, res)=>{
User
.findOne({username:req.body.username})
.exec()
.then(data=>{
if (data) {
if (data.password == md5(req.body.password)) {
res.json({
error:0,
data: ‘登录成功’,
token: jwt.sign({ _id: data._id }),// 生成token,并传入用户_id
});
}else{
res.json({error:1, data: ‘密码错误’});
}
} else {
res.json({error:1, data: ‘用户名不存在,请先注册’});
}
});
});
##### 在需要登录验证的方法中挂载验证函数
router.get(‘/user’, jwt.verify, (req, res)=>{
User.findOne({_id: req._id}).exec().then(data=>{
res.json({
error: 0,
data: JSON.parse(JSON.stringify(data))
});
});
});
#### 客户端
##### 登录成功保存token
在客户端接收到`token`后,一般情况会通过`localStorage`进行缓存
localStorage.setItem(‘token’, data.token);
##### 请求的过程中携带token
然后将`token`放到`HTTP`请求头`Authorization` 中,关于`Authorization` 的设置,前面要加上 [Bearer](https://bbs.csdn.net/topics/618636735) ,注意后面带有空格:
[JWT规范](https://bbs.csdn.net/topics/618636735)
axios({
method: ‘GET’,
url: ‘/user’,
headers: {
Authorization: localStorage.getItem(‘token’) ? ‘Bearer ’ + localStorage.getItem(‘token’) : ‘’,
}
}).then(res => {
$(’#login-box’).html(template(‘login-template’, res.data))
});
## session 和 jwt 对比
基于 session 都是需要服务端存储的,而 JWT 是不需要服务端来存储的,总结如下:
**1、基于 session 和 cookie 的认证和鉴权模式有什么好与不好的地方呢?**
缺点 :
1. 容易遇到跨域问题。不同域名下是无法通过 session 直接来做到认证和鉴权的。
2. 分布式部署的系统,需要使用共享 session 机制
3. 容易出现 [CSRF](https://bbs.csdn.net/topics/618636735) 问题。
优点:
1. 方便灵活,服务器端直接创建一个 sessionid,下发给客户端,客户端请求携带 sessionid 即可。
2. session 存储在服务端,更加安全。
3. 便于服务端清除 session,让用户重新授权一次。
**2、JWT 与 session 有什么区别呢?**
JWT 是基于客户端存储的一种认证方式,然而 session 是基于服务端存储的一种认证方式
优点:
* JWT具有通用性,所以可以跨域
* 组成简单,字节占用小,便于传输
* 服务端无需保存会话信息,很容易进行水平扩展
* 一处生成,多处使用,可以在分布式系统中,解决单点登录问题
* 可防护[CSRF](https://bbs.csdn.net/topics/618636735)攻击
缺点:
* 无法清除认证 token。由于 JWT 生成的 token 都是存储在客户端的,不能有服务端去主动清除,只有直到失效时间到了才能清除。除非服务端的逻辑做了改变。
* 存储在客户端,相对服务端,安全性更低一些。当 JWT 生成的 token 被破解,我们不便于清除该 token。
* payload部分仅仅是进行简单编码,所以只能用于存储逻辑必需的非敏感信息
* 需要保护好加密密钥,一旦泄露后果不堪设想
* 为避免token被劫持,最好使用https协议
**html用户界面代码:**