token和axios
token
token是什么
由于服务器集群和反向代理的原理,session因为cookie的原因所以有跨域的问题,因此我们选择Json web token(JWT),JWT是基于token的鉴权机制,类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息,为应用的扩展提供了便利。JWT具备以下几个优点:
-
因json的通用性,所以JWT是可以进行跨语言
-
JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息
-
便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的
-
它不需要在服务端保存会话信息,所以它非常适合应用在前后端分离的项目上
为什么需要 token
=> http 无状态
=> session 无法跨服务器
=> cors 跨域以后 cookie 无法使用
使用JWT进行鉴权的工作流程如下(重点):
- 用户使用用户名密码来请求服务器 -
服务器进行验证用户的信息(查数据库)
-
服务器通过验证发送给用户一个token(令牌)
-
客户端存储token(Vuex+localStorage),并在每次请求时附送上这个token值
-
服务端验证token值,并返回数据
token 验证的过程
- => token 验证登录
-> 三段式加密字符串
-> xxxxxxxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyy.zzzzzzzzzzzzz
-> 第一段: 头, 签证: 安全信息验证, 你的口令, 进行不可逆加密
-> 第二段: 你要保存的信息: basa64 加密后截取内容
-> 第三段: 额外信息: 不可逆加密
-> 这一段字符串由后端发给前端
-> 再登陆过以后, 生成一个 token 给到前端 - 前端保存这个 token
- 如果前端需要登陆后查看的页面, 或者登陆后发送的请求
- 只要你把 token 带回来, 我解密一下试试
- session的缺点
=> session 得持久化存储
-> 一人一半密码
-> 一半再服务器
-> 一半再 cookie
=> 如果想长久保存登录状态- 服务器不能变
- 可以设置 cookie
session 的缺点
1. 需要保证是同一台服务器, 不能更换服务器
2. 保证 cookie 可以设置, 跨域以后 cookie 不能设置
3. 容易被伪造
token登录 购物车逻辑
-
前端
=> 发送请求 -
后端
=> 接收请求
=> 数据库比对
=> { userid: _id } 生成一个 token
=> 返回信息的时候, 连带 token 一起返回给前端(响应体) -
前端
=> 接收信息以后
=> 把 token 存储在 localStorage 里面购物车页面
-
前端发送请求请求用户的购物车数据
=> 要求登陆后查看, 把 token 放在请求头里面带过去 -
后端
=> 接收请求
=> 查看请求头里面有没有 token, 没有, 直接打回去
=> 如果由 token , 解析一下试试, 如果过期, 打回去, 如果无效, 打回去
=> 解密 token 没有问题, 那么继续向后, 进入路由
=> 准备购物车数据, 给你返回 -
前端
=> 根据响应绝对是否跳转回登录页
token的使用
测试使用 jwt
- jsonwebtoken
- 专门生成 token 和解析 token 的一个第三方
使用
- 下载: npm i jsonweb token
- 导入:
- 使用
=> 生成: jwt.sign(你要保存的信息, 口令, 参数)
-> 保存的信息
-> 口令: 加密口令, 加密的时候混入信息使用, 解密的时候还要这个口令
-> 参数: 是一个对象, {}
expiresIn: 过期时间,单位为秒 (‘1d’)
=> 解码: jwt.verify(你要解析的 token, 口令, 回调函数)
-> token: 必须是一个指定的 token
-> 口令: 必须是加密时候的口令
-> 回调函数: 接收结果
案例
express-jwt的使用
express-jwt
- 是一个 express 框架和 jwt 结合的第三方中间件
- 作用: 验证 token
使用
- 下载: npm i express-jwt
- 导入
- 注册为中间件
=> 语法: app.use( expressJWT({ 配置 }).unless({ 配置 }) )
注意: 如果你要使用 express-jwt
=> 你必须有一个全局错误中间件
- 正常的 token 返回给前端
- 需要写成 Bearer token
代码
const jwt = require(‘jsonwebtoken’)
const expressJWT = require(‘express-jwt’)
app.use(expressJWT({
// 解析口令, 需要和加密的时候一致
secret: ‘guoxiang’,
// 加密方式: SHA256 加密方式在 express-jwt 里面叫做 HS256
algorithms: [‘HS256’]
}).unless({
// 不需要验证 token 的路径标识符
path: [’/login’, ‘/banner’]
}))
- 需要写成 Bearer token
大部分企业会在提供的接口jwt所返回的token格式可能会在原有token之前拼接一个持有者(空格)
的信息,例如用户zhangsan
获取到的token:
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJ1c2VySWQiOjMxMTY3NTA5LCJtb2JpbGUiOiIxODUxMjM0NTY3OCIsImlhdCI6MTYwMjY0OTg2NX0
.tVByVZYu4s5dgzLZwR00HHW7QZ0gkYpVXaVNhCdawbU
案例样式
//第一个参数为需要携带的信息,第二个参数为密钥,第三个参数设置过期时间
const res = jwt.sign(userInfo, 'zzzzzzzzzzz', { expiresIn: 20 })
//解析token 第一个为前端传递过来的token 第二个参数为密钥
jwt.verify(res, 'zzzzzzzzzzz', (err, data) => {
if (err) return console.log(err)
console.log(data)
}
//添加持有者信息返回给前端
const token = 'Bearer ' + jwt.sign(userInfo, 'zzzzzzzzzzz', { expiresIn: 60 })
//关于使用express-jsonwebtoken 的方法 提前挂载在app上 需要使用时就在使用的地方获取解析比对
app.use(expressJWT({
// 解析口令, 需要和加密的时候一致
secret: 'zzzzzzzzzzz',
// 加密方式: SHA256 加密方式在 express-jwt 里面叫做 HS256
algorithms: ['HS256']
}).unless({
// 不需要验证 token 的路径标识符
path: ['/login', '/banner']
}))
// 因为token会携带在请求头中的authorization中 解构赋值获取token
const { url, headers: { authorization: token } } = req
//再使用jwt.verify来进行token验证
jwt.verify(token, 'zzzzzzzzzzz', (err,data) => {
if (err && err.message === 'invalid token') return res.send({ message: '无效 token', code: 0 })
if (err && err.message === 'jwt expired') return res.send({ message: 'token 失效', code: 0 })
next();
})
axios
- 前端专门发送 ajax 请求的插件
为什么使用axios
从需求看问题
1. 我要发送很多的 ajax
=> 原生麻烦, 写的代码多
=> jQuery, 库太大, 一堆 DOM 操作没有意义
2. 请求地址
=> 因为跨域请求, 每次都需要写完整地址
=> 不想每次都写前面那一段一样的
3. 请求头
=> 除了登录的请求, 其他的都需要携带 token
=> 我不想每次都设置一遍请求头
解决问题
axios
1. 下载: npm i axios
2. 引入文件
3. 基础使用
=> 当你引入 axios.js 或者 axios.min.js 的时候
=> 回向全局暴露一个 axios 变量
基本使用
1. 发送 get 请求
=> 语法: axios.get(地址, { params: { get 请求的参数 } }).then()
2. 发送 post 请求
=> 语法: axios.post(地址, { 参数 }).then()
3. 发送 delete 请求
=> 语法: axios.delete(地址, { 参数 }).then()
axios 进阶使用
+ 语法: axios.create(配置)
+ 返回值: 一个 axios 的实例
=> 你使用实例依旧可以发送 get post delete … 请求
=> 使用实例发送的每一个请求都遵循 create 的时候的配置
// 1. 生成实例
// const instance = axios.create({
// // 基准地址配置
// baseURL: 'http://localhost:8080/'
// })
// // 从此以后再次发送请求的时候
// // 只要你使用这个实例
// // 你的地址位置只需要写基准地址以后的就可以了
// instance.get('/login').then(res => console.log(res))
// instance.get('/banner').then(res => console.log(res))
// instance.get('/cartInfo').then(res => console.log(res))
axios 的拦截器
const instance = axios.create({
baseURL: 'http://localhost:8080/'
})
// 请求拦截器, 每一个使用 instance 发送的请求都会经历我
// 然后我同意了你才能发送出去
设置一个请求拦截器
// 请求拦截器, 每一个使用 instance 发送的请求都会经历我
// 然后我同意了你才能发送出去
instance.interceptors.request.use(function (config) {
// console.log('我被触发了')
// console.log(config)
// 如果是 /login 和 /banner 直接放过
// ·
if (config.url === '/login' || config.url === '/banner') return config
// 剩下的就要像请求头里面添加了信息在放过去
config.headers.authorization = window.localStorage.getItem('token')
return config
})
设置一个响应拦截器
每一个响应回到浏览器的时候都要先经历我
// 我允许了才会返回给 实例的 then
instance.interceptors.response.use(function (response) {
console.log(response)
if (response.status === 404) return widnow.location.href = '404.html'
return response.data
})
// 使用 instance 发送了一个请求
instance.get('/login').then(res => console.log(res))
// instance.get('/banner')
// instance.get('/cartInfo')
// instance.get('/pay')