实现的场景:

登录接口,登录完成后服务器会返回加密的token,token存放在cookie中,由于有些设别无法使用cookie需要将token放在响应头的authorization中
添加学生接口:请求中必须携带cookie,服务端会解析cookie的token,由于设备中有差别,解析请求头的authorization中的token,都这两个地方都没有时,说明该用户没有权限访问该接口

实现所需要的技术点

url中的路径匹配:使用的插件path-to-regexp
token的加密与解密:使用node模块中的crypto模块
cookie的管理:使用的插件cookie-parser

代码

用户发送登录请求时,服务器生成token将其加密,存于cookie中

  1. const adminService = require('../../services/adminService');
  2. const express = require('express');
  3. // 路由
  4. const router = express.Router();
  5. //高阶函数 解决无法使用promise
  6. const { asyncHandler } = require('../getSendResult');
  7. // 加密与解密
  8. const cryptoToken = require('../../util/cryptoToken')
  9. router.post('/login', asyncHandler( async(req,res,next) => {
  10. const result = await adminService.login(req.body.loginId, req.body.loginPwd);
  11. if (result) {
  12. // 加密
  13. const id = cryptoToken.encrypt(result.id);
  14. res.cookie('token', id, {
  15. path: '/',
  16. domain: 'localhost',
  17. });
  18. // 当为手机端是无法使用cookie的
  19. // 需要在响应头的authorization存放token
  20. res.header('authorization', id);
  21. res.header('Access-Control-Allow-Origin', '*');
  22. }
  23. return result;
  24. }))
  25. module.exports = router;

路由匹配,那些路由需要有token才能访问

  1. /**
  2. * 那些接口需要有cookie才能访问
  3. */
  4. const needTokenApi = [
  5. { method: 'POST', path: '/api/student' },
  6. { method: 'PUT', path: '/api/student/:id' },
  7. { method: 'GET', path: '/api/student' },
  8. ];
  9. const { getErr } = require('./getSendResult');
  10. // 将路径转化为正则进行匹配
  11. const { pathToRegexp, match, parse, compile } = require('path-to-regexp');
  12. const cryptoToken = require('../util/cryptoToken');
  13. module.exports = (req, res, next) => {
  14. const apis = needTokenApi.filter((ele) => {
  15. const reg = pathToRegexp(ele.path);
  16. return ele.method == req.method && reg.test(req.path);
  17. });
  18. if (apis.length === 0) {
  19. next();
  20. return;
  21. }
  22. /**
  23. * 是否有token
  24. */
  25. let token = req.cookies.token;
  26. if (!token) {
  27. token = req.headers.authorization;
  28. }
  29. if (!token) {
  30. console.log('没有token');
  31. handleNonToken(req, res, next);
  32. return;
  33. }
  34. /**
  35. * token 解密
  36. */
  37. const userId = cryptoToken.decipheriv(token);
  38. req.userId = userId;
  39. next();
  40. };
  41. /**
  42. * 没有token
  43. * @param {*} req
  44. * @param {*} res
  45. * @param {*} next
  46. */
  47. function handleNonToken(req, res, next) {
  48. res
  49. .status(403)
  50. .send(getErr('you dont have any token to access the api', 403));
  51. }

token的加密与解密

  1. const crypto = require('crypto');
  2. // 原始密钥
  3. const key = Buffer.from('mm7h3ck87ugk9l4a');
  4. // 初始向量 初始向量必须是随机的
  5. const randomText = Math.random().toString(16).slice(-8) + Math.random().toString(16).slice(-8);
  6. // const randomText = '1bdf94c826a35d69';
  7. const iv = Buffer.from(randomText);
  8. /**
  9. * 加密
  10. * @param {*} str
  11. * @returns
  12. */
  13. exports.encrypt = function (str) {
  14. const cry = crypto.createCipheriv('aes-128-cbc', key, iv);
  15. let text = cry.update(str.toString(), 'utf-8', 'hex');7
  16. text += cry.final('hex');
  17. return text;
  18. };
  19. /**
  20. * 解密
  21. * @param {*} token
  22. */
  23. exports.decipheriv = function(str) {
  24. const dec = crypto.createDecipheriv('aes-128-cbc', key, iv);
  25. let text = dec.update(str.toString(), 'hex', 'utf-8');
  26. text += dec.final('utf-8');
  27. return text;
  28. };

完整代码存放与阿里云盘的语雀文件夹中