Express - API接口2

给自己的API上加入 Token 验证

1. token

Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位

2. 安装

  • jsonwebtoken

    用于生成 token , 解析 token

    npm install jsonwebtoken
    
  • express-jwt

    用于验证 token 是否过期或失效

    npm install express-jwt
    

3. 使用

  • utils/token_vertify.js 文件

    用于封装 token 生成和解析函数

    // 引入 jwt
    const jwt = require('jsonwebtoken');
    // 解析 token 用的密钥
    const TOKEN_SECRET = 'mes_qdhd_mobile_xhykjyxgs';
    
    const setToken = (username, userid) => {
      return new Promise((resolve, reject) => {
        const token = jwt.sign(
          {
            name: username,
            _id: userid,
          },
          TOKEN_SECRET,
          {
            expiresIn: 60 * 10,  //10分钟
          }
        );
        resolve(token);
      });
    };
    
    const verToken = (token) => {
      return new Promise((resolve, reject) => {
        let info = jwt.verify(token, TOKEN_SECRET);
        resolve(info);
      });
    };
    
    module.exports = {
      TOKEN_SECRET,
      setToken,
      verToken,
    };
    
  • app.js

    // 引入express模块
    const express = require('express');
    const bodyParser = require('body-parser');
    const tokenVertify = require('./utils/token_vertify.js');
    const expressJwt = require('express-jwt');
    const port = 3000;
    // 创建app对象,通过语法expres, 底层原理http模块的createServer
    const app = express();
    
    // 导入body-parser中间件,用于post方法
    app.use(bodyParser.json()); // 添加json解析
    app.use(bodyParser.urlencoded({ extended: false }));
    
    //设置跨域访问
    app.all('*', (req, res, next) => {
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Headers', 'X-Requested-With');
      res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
      res.header('X-Powered-By', ' 3.2.1');
      next();
    });
    
    // 解析token获取用户信息
    app.use((req, res, next) => {
      const token = req.headers['authorization'];
      if (token == undefined) {
        return next();
      } else {
        // console.log(token);
        tokenVertify
          .verToken(token.split(' ')[1])
          .then((info) => {
            // console.log('解析 success: ', info);
            req.info = info;
            return next();
          })
          .catch((error) => {
            console.log('解析 fail: ', error);
            return next();
          });
      }
    });
    
    // 验证token是否过期并规定哪些路由不用验证;
    app.use(
      expressJwt({
        secret: tokenVertify.TOKEN_SECRET,
        algorithms: ['HS256'],
      }).unless({
        path: [ '/api/user/login', '/api/user/register'],  //可以写正则表达式
        // 除了这个地址,其他的URL都需要验证
      })
    );
    
    //处理静态目录,访问静态文件
    app.use('/static', express.static('public'));
    // 用户
    app.use('/api/user', require('./routes/user.js'));
    // 资源不存在(写在所有路由的最后)
    app.get('*', (req, res, next) => {
      next({
        meta: {
          status: 404,
          msg: '所请求的资源不存在或不可用',
        },
        data: null,
      });
    });
    
    // 当token失效返回提示信息
    app.use((err, req, res, next) => {
      let status = err.status || err.meta.status;
      if (status == 401) {
        return res.status(status).send({
          meta: {
            status: 401,
            msg: err.inner.message,
          },
          data: null,
        });
      }
      return res.status(status).send(err);
    });
    
    // 启动服务监听
    app.listen(port, () => {
      console.log('http://localhost:3000');
    });
    
  • routes/user.js

  • controller/user.js

    // 导入数据库模型
    const userMoudles = require(process.cwd() + '/src/moudles/user.js');
    var tokenVertify = require(process.cwd() + '/src/utils/token_vertify.js');
    
    /**
     * 用户注册
     */
    const userRegister = async (req, res, next) => {
      // 1. 拿到请求过来的数据
      let user = req.body;
      // 2. 验证数据是否正确
      if (Object.keys(user).length == 0) {
        return next({
          meta: {
            status: 400,
            msg: '参数为空',
          },
          data: null,
        });
      }
      if (!(user.userName && user.passWord && user.cellularPhone)) {
        return next({
          meta: {
            status: 400,
            msg: '参数错误',
          },
          data: null,
        });
      }
      // 3. 调用操作数据库的方法
      let b1 = await isUserNameUnique(user.userName);
      if (!b1) {
        return next({
          meta: {
            status: 400,
            msg: 'userName重复',
          },
          data: null,
        });
      }
      let b2 = await isCellularPhoneUnique(user.cellularPhone);
      if (!b2) {
        return next({
          meta: {
            status: 400,
            msg: 'cellularPhone重复',
          },
          data: null,
        });
      }
      let rs = await userMoudles.userRegister({
        userBaseInfo: user,
        userLikeList: {
          goods: [],
          shop: [],
        },
        userOrderForm: [],
        userShoppingCart: [],
      });
      // 4. 返回信息
      if (rs) {
        return res.json({
          meta: {
            status: 200,
            msg: '注册成功',
          },
          data: null,
        });
      } else {
        return next({
          meta: {
            status: 500,
            msg: '服务器错误,注册失败',
          },
          data: null,
        });
      }
    };
    
    /**
     * 用户登录
     */
    const userLogin = async (req, res, next) => {
      // 1. 拿到请求过来的数据
      let user = req.body;
      console.log(user);
      // 2. 验证数据是否正确
      if (Object.keys(user).length == 0) {
        return next({
          meta: {
            status: 400,
            msg: '参数为空',
          },
          data: null,
        });
      }
      if (!(user.userName && user.passWord)) {
        return next({
          meta: {
            status: 400,
            msg: '参数错误',
          },
          data: null,
        });
      }
      // 3. 调用操作数据库的方法
      let rs = await userMoudles.userLogin(user.userName, user.passWord);
      // 4. 返回信息
      if (rs) {
        rs = rs[0];
        if (rs) {
          let _id = rs._id;
          // 通过_id,用户名生成token,一定不要把密码放入token  
          tokenVertify.setToken(user.userName, _id).then((token) => {
            console.log('生成的token:\n', token);
            return res.json({
              meta: {
                status: 200,
                msg: '登录成功',
              },
              data: {
                token,
              },
            });
          });
        } else {
          return next({
            meta: {
              status: 400,
              msg: '用户名或密码错误',
            },
            data: null,
          });
        }
      } else {
        return next({
          meta: {
            status: 500,
            msg: '系统错误,登录失败',
          },
          data: null,
        });
      }
    };
    
    const isUserNameUnique = async (userName) => {
      let num = await userMoudles.countUserName(userName);
      return num == 0;
    };
    const isCellularPhoneUnique = async (cellularPhone) => {
      let num = await userMoudles.countCellularPhone(cellularPhone);
      return num == 0;
    };
    
    module.exports = {
      userRegister,
      userLogin,
    };
    
  • moudels/user.js

  • utils/db.js

4. 注意

  • 前端头部传递时,在 token 前一定要加上"Bearer ",否则会一直报错
  • 在 app.js 已经通过中间件把 token 解析的数据放入了req.info,所以需要时直接调用req.info即可
  • 不要把密码放入 token

5. 参考文章

  1. https://blog.csdn.net/qq_39905409/article/details/87905335
  2. https://www.cnblogs.com/blog-zy/p/11840964.html
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王文聪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值