前言:
 为什么会用到Token,因为cookie和session一些自身的缺点,限制了一些功能的实现,比如:
 cookie:优点是节省服务器空间,缺点不安全。不要保存敏感信息。
 session:优点是安全,缺点需要服务器空间(服务器重启,则数据丢失), 是一种最常见的解决方案。而这个时候,我们用token就能更好
   简介:
 token的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。
 JWT(json web token) 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。
{
"姓名": "张三",
"角色": "管理员",
"到期时间": "2022年7月11日0点0分"
}
以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。
 当然,为了防止用户篡改数据,服务器在生成这个对象的时候,会给他加密一下,就是我们看到的一个长长的字符串
 如图所示:

使用token:
使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的
1.前端使用用户名跟密码请求首次登录
2.后服务端收到请求,去验证用户名与密码是否正确
3.验证成功后,服务端会根据用户id、用户名、定义好的秘钥、过期时间生成一个 Token,再把这个 Token 发送给前端
4.前端收到 返回的Token ,把它存储起来,比如放在 Cookie 里或者 Local Storage 里
5.前端每次路由跳转,判断 localStroage 有无 token ,没有则跳转到登录页。有则请求获取用户信息,改变登录状态;
6.前端每次向服务端请求资源的时候需要在请求头里携带服务端签发的Token
7.服务端收到请求,然后去验证前端请求里面带着的 Token。没有或者 token 过期,返回401。如果验证成功,就向前端返回请求的数据。
8.前端得到 401 状态码,重定向到登录页面。
使用第三方模块 jsonwebtoken 创建token字符串
基本步骤:
- 
在项目中下载安装
npm i jsonwebtoken - 
加载模块 const jwt = require(‘jsonwebtoken’);
 - 
在用户登陆成功之后,调用 jwt.sign() 方法创建token, 它有如下4个参数: 参数1:必填,对象形式;希望在token中保存的数据 参数2:必填,字符串形式;加密的钥匙;后续验证token的时候,还需要使用 参数3:可选,对象形式;配置项,比如可以配置token的有效期
 
参数4:可选,函数形式;生成token之后的回调
生成的token前面,必须拼接 Bearer 这个字符串
参考代码
const express = require('express')
const cors = require('cors')
const app = express();
const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');
app.use(cors({ origin: true, credentials: true }))
app.use(express.static('public'))
app.use(express.urlencoded())
// app.use(jwt().unless());
// jwt() 用于解析token,并将 token 中保存的数据 赋值给 req.user
// unless() 约定某个接口不需要身份认证
app.use(expressJwt({
  secret: '这个名字需要一致', // 生成token时的 钥匙,必须统一
  algorithms: ['HS256'] // 必填,加密算法,无需了解
}).unless({
  path: ['/login'] // 除了数组中的接口之外,其他都需要认证有token才能访问
}));
app.post('/login', (req, res) => {
  console.log('接收到的数据是', req.body)
  const { name, password } = req.body
  if (password === '123456') {
    // 创建token
    // expiresIn:过期时间,单位是秒
    const tokenStr = jwt.sign({ name: name }, '这个名字需要一致', { expiresIn: 10 });
    res.json({ msg: '登录成功', token: 'Bearer ' + tokenStr })
  } else {
    res.json({ msg: '登录失败' })
  }
})
app.get('/test', (req, res) => {
  res.json({ msg: '测试tokenOk' })
})
app.use((err, req, res, next) => {
  if (err.name === 'UnauthorizedError') {
    // res.status(401).send('invalid token...');
    res.status(401).send({ status: 1, message: '身份认证失败!' });
  }
})
app.listen(3000, () => {
  console.log(3000);
})
浏览器端:保存后端回传的token
保存在localStorage
$('#btn_login').click(function(){
  $.ajax({
    type:'post',
    url:'http://localhost:3000/login',
    data:{name:$('#username').val(),password:$('#password').val()},
    success(res){
      console.log(res);
+      localStorage.setItem('token', res.token)
    }
  })
})
浏览器端:发请求时手动携带token
必须放置在Authorization中
$('#btn_testToken').click(function(){
  $.ajax({
    type:'get',
    url:'http://localhost:3000/test',
    headers: {
+      Authorization: localStorage.getItem('token'),
    },
    success(res){
      console.log(res);
    }
  })
})
服务器端:实现token认证
选择使用 express-jwt 第三方模块进行身份认证。从模块名可以看出,该模块是专门配合express使用的。
下载安装
npm i express-jwt 
中间件技术-验证token
const expressJwt = require('express-jwt');
// app.use(jwt().unless());
// jwt() 用于解析token,并将 token 中保存的数据 赋值给 req.user
// unless() 约定某个接口不需要身份认证
app.use(expressJwt({
  secret: '创建钥匙的名字一致', // 生成token时的 钥匙,必须统一
  algorithms: ['HS256'] // 必填,加密算法,无需了解
}).unless({
  path: ['/login'] // 除了个接口,其他都需要认证
}));
上述代码完成后,当一个接口请求到了服务器后,它会自动验证请求头中的 Authorization 字段了,并且会自动完成:
如果没有问题:
1、将token中保存的数据赋值给req.user
2、next()
如果有问题,则抛出错误next(错误信息)
补充中间件技术-统一处理错误
在所有的路由最后,加入错误处理中间件,来提示token方面的错误。
app.use((err, req, res, next) => {
  if (err.name === 'UnauthorizedError') {
    // res.status(401).send('invalid token...');
    res.status(401).send({ status: 1, message: '身份认证失败!' });
  }
});
		
菜鸟笔记