http 无状态
session 无法跨服务器
cors 跨域以后 cookie 无法使用
在前后分离的项目中,每次请求session都会变化,前端调用后端api接口,因此使用cors = require('cors')
来解决了跨域问题,而跨域对于cookie来说,就是两个不同的网站,因此session会不停的变。
在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般我们所说的的token大多是指用于身份验证的token
我们需要每次都知道当前请求的人是谁,但是又不想每次都让他提交用户名和密码,这时就需要有一个等同于用户名密码也能够标识用户身份的东西,即—token
token是由三段式加密字符串组成以.
分开例如xxxxxxxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyy.zzzzzzzzzzzzz
第一段: 头, 签证: 安全信息验证, 你的口令, 进行不可逆加密*
第二段: 你要保存的信息: basa64 加密后截取内容,
第三段: 额外信息: 不可逆加密
第一段和第三段不可逆加密 哈希散列,第二段 base64 可逆加密
这一段字符串由后端发给前端,再登陆过以后, 生成一个 token 给到前端
npm i jsonwebtoken
||
yarn add jsonwebtoken
在nodejs中直接导入
// 0. 导入
const jwt = require('jsonwebtoken')
jwt.sign(你要保存的信息, 口令, 参数)
保存的信息
口令: 加密口令, 加密的时候混入信息使用, 解密的时候还要这个口令
参数: 是一个对象, {}
expiresIn: 过期时间,单位为秒 (‘1d’)*
jwt.verify(你要解析的 token, 口令, 回调函数)
token: 必须是一个指定的 token
口令: 必须是加密时候的口令
回调函数: 接收结果
const express = require('express')
const jwt = require('jsonwebtoken')
const cors = require('cors')
const router = express.Router()
const app = express()
app.use(cors())
// 准备一个 token 验证的中间件
app.use(function (req, res, next) {
// req.url 表示当前地址
const {
url, headers: {
authorization: token } } = req
// const url = req.url
// const { authorization: token } = req.headers
// const token = req.headers.authorization
// 不需要验证的 请求地址
if (url === '/login' || url === '/banner') return next()
// 来到这里表示需要 token 验证
if (!token) return res.send({
message: '请携带 token 请求', code: 0 })
jwt.verify(token, 'Josiah', (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()
})
})
router.get('/login', (req, res) => {
// 默认登录成功
const userInfo = {
username: 'XXX',
age: 18
}
// 生成一个 token 返回给前端
const token = jwt.sign(userInfo, 'Josiah, {
expiresIn: 60 })
res.send({
message: '登录成功', code: 1, token })
})
// 第二次测试
router.get('/cartInfo', (req, res) => {
res.send({
message: '获取购物车数据成功', code: 1 })
})
router.get('/pay', (req, res) => {
res.send({
message: '支付成功', code: 1 })
})
// 第一次测试
// router.get('/cartInfo', (req, res) => {
// 你想要购物车数据
// 那么你必须登录以后
// 验证 token 是不是正确
// req, 是本次请求的信息
// req.headers 就是本次的请求头
// const token = req.headers.authorization
// // 1. 有没有
// if (!token) return res.send({ message: '该请求需要 token', code: 0 })
// // 2. 对不对
// jwt.verify(token, 'guoxiang', (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 })
// res.send({ message: '获取购物车数据成功', code: 1 })
// })
// })
router.get('/banner', (req, res) => {
// 你需要轮播图信息
// 不需要登录验证
res.send({
message: '轮播图数据获取成功', code: 1, list: [ {
}, {
} ] })
})
app.use(router)
app.listen(8080, () => console.log('running at 8080'))
token的生成和解码
// 0. 导入
const jwt = require('jsonwebtoken')
// 准备一个信息
const userInfo = {
_id: '2131245655',
username: '小星星',
age: 18,
gender: '男'
}
// 1. 生成一个 token
const res = jwt.sign(userInfo, 'key', {
expiresIn: 20 })
console.log(res)
//res会生成一下内容
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
// eyJfaWQiOiIyMTMxMjQ1NjU1IiwidXNlcm5hbWUiOiLpg63nv5QiLCJhZ2UiOjE4LCJnZW5kZXIiOiLnlLciLCJpYXQiOjE1OTkxOTk2MDYsImV4cCI6MTU5OTE5OTYxNn0.
// Cg-PxkAzTnCUF9xja1O9gcOmRzaRsw4TlFM2nCZenAw
// 2. 解码
jwt.verify(res, 'key', (err, data) => {
console.log('第一次 1.5s')
if (err) return console.log(err)
console.log(data)
})
注意:
以下案例用到的插件:
express、jsonwebtoken、express-jwt、cors
app.js
const express = require('express')
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
const cors = require('cors')
const router = expryicisess.Router()
const app = express()
app.use(cors());//解决跨域问题
// app.use() 里面放的 expressJWT().unless()
// 注册 token 验证中间件
app.use(expressJWT({
// 解析口令, 需要和加密的时候一致
secret: 'Josiah',
// 加密方式: SHA256 加密方式在 express-jwt 里面叫做 HS256
algorithms: ['HS256']
}).unless({
// 不需要验证 token 的路径标识符
path: ['/login', '/banner']
}))
router.get('/login', (req, res) => {
// 默认登录成功
const userInfo = {
username: '郭翔',
age: 18
}
// 生成一个 token 返回给前端
const token = 'Bearer ' + jwt.sign(userInfo, 'Josiah', {
expiresIn: 60 })
res.send({
message: '登录成功', code: 1, token })
})
app.use(router)
// 错误处理中间件
app.use((err, req, res, next) => {
res.send({
err })
})
app.listen(8080, () => console.log('running at 8080'))
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button>登录</button>
<script>
// 如果你是做后台管理系统, 一般存储在 sessionStorage
// window.sessionStorage.setItem('abc', '100')
// 前端携带 token 我们存储在 请求头 里面携带
// authorization: token
const xhr = new XMLHttpRequest()
const token = window.localStorage.getItem('token')
xhr.open('GET', 'http://localhost:8080/cartInfo')
xhr.onload = function () {
const res = JSON.parse(xhr.responseText)
console.log(res)
}
xhr.setRequestHeader('authorization', token)
xhr.send()
</script>
</body>
</html>
需要在生成之前添加 Bearer
后面有个空格
const token = 'Bearer ' + jwt.sign(userInfo, 'guoxiang', { expiresIn: 60 })
// app.use() 里面放的 expressJWT().unless()
// 注册 token 验证中间件
app.use(expressJWT({
// 解析口令, 需要和加密的时候一致
secret: 'guoxiang',
// 加密方式: SHA256 加密方式在 express-jwt 里面叫做 HS256
algorithms: ['HS256']
}).unless({
// 不需要验证 token 的路径标识符
path: ['/login', '/banner']
}))
xhr.setRequestHeader('authorization', token)