http是无状态协议(不知道上一次是否登录过了),所以要返回一个登录凭证(如cookie+session、token)。这里我们使用token实现身份认证。
JWT生成token由三部分组成:
- header
- payload
- signature
在项目中通过一个库完成:jsonwebtoken
并且一般都是使用非对称加密:
- 私钥:用于发布令牌
- 公钥:用于验证令牌
这里使用git bash生成;
git bash 中输入openssl:
// 通过dotenv统一管理环境标量
const dotenv = require('dotenv').config();
const fs = require('fs')
const path = require('path')
// 这里读取到的文件时buffer
const PRIVATE_KEY = fs.readFileSync(path.resolve(__dirname, './keys/private.key'))
const PUBLIC_KEY = fs.readFileSync(path.resolve(__dirname, './keys/public.key'))
const {
APP_PORT,
.....
} = process.env;
module.exports = {
APP_PORT,
......
}
// 注意这里的导出需要写到module.exports={}的后面
module.exports.PRIVATE_KEY = PRIVATE_KEY;
module.exports.PUBLIC_KEY = PUBLIC_KEY;
颁发令牌之前要先通过账号的验证(如账号秘密)中间件,然后再在另一个中间件颁发令牌。
const express = require('express')
const LoginRouter = express.Router()
const {
login
} = require('../controller/login.controller')
const {
verifyLogin,
} = require('../middleware/login.middleware')
// verifyLogin 为颁发令牌前的信息验证。login中间件中颁发令牌
LoginRouter.post('/', verifyLogin, login)
module.exports = LoginRouter;
login中间件:
调用jwt的sign方法生成token,sign()的参数:
- 第一个参数为:payload中的可携带的数据
- 第二个参数为:私钥
- 第三个参数为:sign()的其他设置,expiresIn为token生效时间,algorithm为加密方式,这里需指明为SH256(非对称加密)
const { getPassword } = require('../service/login.service')
const jwt = require('jsonwebtoken')
const { PRIVATE_KEY } = require('../app/config')
class LoginController {
// 登录
async login(req, res, next) {
const { name, password } = req.body
if (password === await getPassword(name)) {
// 生成token
const token = jwt.sign({ name, password }, PRIVATE_KEY, {
expiresIn: 60 * 60 * 24, // 一天
algorithm: "RS256" // 这里必须写RS256(非对象加密),因为默认为HS256(对称加密)
})
return res.json({
data: { code: 0, token: token },
meta: { message: "登录成功!", status: 200 }
})
}
}
}
module.exports = new LoginController();
通过公钥验证token。
// 验证授权 (验证token)
const verifyAuth = async (req, res, next) => {
// 1.获取token (这里假设前端通过data携带token)
const token = req.headers.token
// 2.验证token 返回的结果为token携带的数据,如payload中携带的name,passqord,以及过期时间等
try {
const result = jwt.verify(token, PUBLIC_KEY, {
algorithms: ["RS256"]
});
next();
} catch {
res.json({
meta: {
message: "无效的token!",
status: 401 // 未授权
}
})
}
}
注:在需要验证token的地方直接插入这个中间件即可。如:
studyRouter.get('/getsortlist', verifyAuth, getSortList)
localStorage.setItem('adminToken', data.token)
这里展示请求拦截器中携带token:
// 二次封装axios
import axios from 'axios'
const requests = axios.create({
baseURL: '/admin',
timeout: 5000
})
// 请求拦截器
requests.interceptors.request.use(config => {
if (localStorage.getItem('adminToken')) {
config.headers.token = localStorage.getItem("adminToken")
}
// config: 配置对象,里面有一个熟悉很重要,headers请求头
return config
})
// 响应拦截器
requests.interceptors.response.use(res => {
return res.data
}, error => {
return Promise.reject(error)
})
export default requests;