nodejs express-jwt使用

作用是什么

express-jwt是nodejs的一个中间件,他来验证指定http请求的JsonWebTokens的有效性,如果有效就将JsonWebTokens的值设置到req.user里面,然后路由到相应的router。 此模块允许您使用Node.js应用程序中的JWT令牌来验证HTTP请求。 JWT通常用于保护API端点。

express-jwt和jsonwebtoken是什么关系


express-jwt内部引用了jsonwebtoken,对其封装使用。 在实际的项目中这两个都需要引用,他们两个的定位不一样。jsonwebtoken是用来生成token给客户端的,express-jwt是用来验证token的。

如何使用


安装

npm install express-jwt
const expressJwt = require('express-jwt');
const app=require('./common');


let jwt = () => {
    const cert = app.fs.readFileSync(app.path.join(__dirname, '../config/jwt', 'ec_public_key.pem'));// 公钥
    return expressJwt({
        secret: cert,
        getToken: function fromHeaderOrQuerystring (req) {
            if (req.headers.authorization) {
                return req.headers.authorization;
            } else if (req.query && req.query.token) {
                return req.query.token;
            }

            return null;
        },
        requestProperty: 'auth',
    }).unless({
        path: [
            { url: '/user/login', methods: ['POST'] },
        ]
    })

};

module.exports = jwt;

校验token失败时的处理

let errorHandler = (err, req, res, next) => {
    console.log(err);
    let error = {
        code: "",
        message: ""
    };

    if (typeof (err) === 'string') {
        error.code = "10009";
        error.message = err;
        return res.status(400).json(error);
    }

    if (err.name === 'UnauthorizedError') {
        error.code = "401";
        error.message = "Invalid Token";
        return res.status(401).json(error);
    }

    // default to 500 server error
    error.code = "500";
    error.message = "Internal Server Error";
    return res.status(500).json(error);
};

module.exports=errorHandler;

token过期时的err值:

{
    "name": "UnauthorizedError",
    "message": "jwt expired",
    "code": "invalid_token",
    "status": 401,
    "inner": {
        "name": "TokenExpiredError",
        "message": "jwt expired",
        "expiredAt": "2017-08-03T10:08:44.000Z"
    }
}

token无效时的err值:

{
    "name": "UnauthorizedError",
    "message": "invalid signature",
    "code": "invalid_token",
    "status": 401,
    "inner": {
        "name": "JsonWebTokenError",
        "message": "invalid signature"
    }
}

后续

客户端携带token正确的姿势

放到 authorization 这个header里, 对应的值以Bearer开头然后空一格

authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiQmluTWFpbmciLCJkYXRhIjoiPT09PT09PT09PT09PSIsImlhdCI6MTUwMTgxNDE4OCwiZXhwIjoxNTAxODE0MjQ4fQ.GoxGlc6E02W5VvqDNawaOrj3MPO-4UYeFdngKR4bVTE

为什么会这样携带token,请看express-jwt源码里是如何获取token的:

//1 从options中获取token  这个忽略  因为 在设置 需要保护的API 时 并没有传递 getToken 这个方法
 if (options.getToken && typeof options.getToken === 'function') {
      try {
        token = options.getToken(req);
      } catch (e) {
        return next(e);
      }
      //2 从authorization中获取token
    } else if (req.headers && req.headers.authorization) {
      // --这是关键代码-----开始切割--------->
      var parts = req.headers.authorization.split(' '); 
      if (parts.length == 2) {
        var scheme = parts[0];
        var credentials = parts[1];

        if (/^Bearer$/i.test(scheme)) {
          token = credentials; // <-------最终获取到token---------          
        } else {
          if (credentialsRequired) {
            return next(new UnauthorizedError('credentials_bad_scheme', { message: 'Format is Authorization: Bearer [token]' }));
          } else {
            return next();
          }
        }
        //3 以上两个途径都没有token时 就报错呗
      } else {
        return next(new UnauthorizedError('credentials_bad_format', { message: 'Format is Authorization: Bearer [token]' }));
      }
    }

express-jwt 作为中间件都做了什么

总体流程:

源码赏析():

var middleware = function(req, res, next) {
    var token;
    // 1 请求method为 options时的处理
    if (req.method === 'OPTIONS' && req.headers.hasOwnProperty('access-control-request-headers')) {
      var hasAuthInAccessControl = !!~req.headers['access-control-request-headers']
                                    .split(',').map(function (header) {
                                      return header.trim();
                                    }).indexOf('authorization');

      if (hasAuthInAccessControl) {
        return next();
      }
    }
  //2  获取token(上面已经解释过了)
    if (options.getToken && typeof options.getToken === 'function') {
      try {
        token = options.getToken(req);
      } catch (e) {
        return next(e);
      }
    } else if (req.headers && req.headers.authorization) {
      var parts = req.headers.authorization.split(' ');
      if (parts.length == 2) {
        var scheme = parts[0];
        var credentials = parts[1];

        if (/^Bearer$/i.test(scheme)) {
          token = credentials;
        } else {
          if (credentialsRequired) {
            return next(new UnauthorizedError('credentials_bad_scheme', { message: 'Format is Authorization: Bearer [token]' }));
          } else {
            return next();
          }
        }
      } else {
        return next(new UnauthorizedError('credentials_bad_format', { message: 'Format is Authorization: Bearer [token]' }));
      }
    }

    if (!token) {
      if (credentialsRequired) {
        return next(new UnauthorizedError('credentials_required', { message: 'No authorization token was found' }));
      } else {
        return next();
      }
    }

 // 3 解码 token
    var dtoken;
    try {
      dtoken = jwt.decode(token, { complete: true }) || {};
    } catch (err) {
      return next(new UnauthorizedError('invalid_token', err));
    }

// 4 依次执行下面操作
    async.waterfall([
     // 获取我们传递 secret
      function getSecret(callback){
        var arity = secretCallback.length;
        if (arity == 4) {
          secretCallback(req, dtoken.header, dtoken.payload, callback);
        } else { // arity == 3
          secretCallback(req, dtoken.payload, callback);
        }
      },
      // 用secret验证token  这段代码是不是很熟悉  没错 就是调用了jsonwebtoken验证token的方法 
      function verifyToken(secret, callback) {
        jwt.verify(token, secret, options, function(err, decoded) {
          if (err) {
            callback(new UnauthorizedError('invalid_token', err));
          } else {
            callback(null, decoded);
          }
        });
      },
      // 检查token 是否被注销了  这个是express-jwt的功能
      function checkRevoked(decoded, callback) {
        isRevokedCallback(req, dtoken.payload, function (err, revoked) {
          if (err) {
            callback(err);
          }
          else if (revoked) {
            callback(new UnauthorizedError('revoked_token', {message: 'The token has been revoked.'}));
          } else {
            callback(null, decoded);
          }
        });
      }
// 最终的结果汇集到这里了 
    ], function (err, result){
      if (err) { return next(err); }  // 有错误 直接就返回了
      if (_resultProperty) {
        set(res, _resultProperty, result);
      } else {
        set(req, _requestProperty, result);
      }
      next();  // 没问题  就流转到对应的url 逻辑操作里面
    });
  };

express-jwt 与jsonwebtoken

express-jwt是对jsonwebtoken进行了封装,在验证策略方面做了很多扩展,具体扩展请看这里。如果你的验证策略很简单,使用jsonwebtoken就足够了,如下面事例:

// 自定义 验证策略 中间件
app.use(function(req, res, next) {
// 定义 不用token 的api
    if (req.originalUrl.indexOf('/getToken') >= 0) {
        return next();
    }
//定义 用token的api  对其验证
     var token = rq.body.token || rq.query.token || rq.headers["x-access-token"];
    jwt.verify(token, "secretOrPrivateKey", function(err, decoded) {
        if (err) {
            // 返回错误信息
            res.send({
                success: false,
                message: 'Failed to authenticate token. Make sure to include the ' +
                    'token returned from /users call in the authorization header ' +
                    ' as a Bearer token'
            });
            return;
        } else {
            // 解析必要的数据(相应字段为定义token时的字段)
            req.username = decoded.username;
            req.orgname = decoded.orgName;
            logger.debug(util.format('Decoded from JWT token: username - %s, orgname - %s', decoded.username, decoded.orgName));
            // 跳出中间件
            return next();
        }
    });
});

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值