如何正确使用ExpressJS中的中间件来保护一些使用Token Auth的路径?

时间:2017-07-14 06:10:31

标签: node.js express authentication

我目前开始使用ExpressJS,并遇到了实现令牌身份验证的问题。首先,这里是代码:

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const jsonwebtoken = require('jsonwebtoken');

const config = require('./config');
const User = require('./models/user');

// Connect to mongoose
mongoose.connect(config.db, { useMongoClient: true });
const db = mongoose.connection;

app.use(bodyParser.json());

app.get('/', function(req, res) {
  res.send('Please use /api/ with an existing endpoint.');
});

const router = express.Router();

router.post('/auth', function(req, res) {
  User.findOne({
    name: req.body.name
  }, function(err, user) {
    if (err)
      throw err;

    if (!user) {
      res.json({
        success: false,
        message: 'Authentication failed. User not found.'
      });
    } else {
      if (user.password != req.body.password) {
        res.json({
          success: false,
          message: 'Authentication failed. Wrong password.'
        });
      } else {
        const token = jsonwebtoken.sign(user, config.secret, { expiresIn: "20 seconds" });

        res.json({
          success: true,
          message: 'Authentication succeeded. Enjoy your token.',
          token: token
        });
      }
    }
  });
});

router.use(function(req, res, next) {
  const token = req.body.token || req.query.token || req.headers['x-access-token'];

  if (token) {
    jsonwebtoken.verify(token, config.secret, function(err, decoded) {
      if (err) {
        res.status(403).json({
          success: false,
          message: 'Failed to authenticate token.'
        });
      } else {
        req.decoded = decoded;
        next();
      }
    });
  } else {
    res.status(403).json({
      success: false,
      message: 'No token provided.'
    });
  }
});

router.get('/', function(req, res) {
  res.send('Please use /api/ with an existing endpoint.');
});

router.get('/users', function(req, res) {
  User.getUsers(function(err, users) {
    if (err)
      throw err;

    res.json(users);
  });
});

router.get('/users/:_id', function(req, res) {
  User.getUserById(req.params._id, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

router.post('/users', function(req, res) {
  var user = req.body;

  User.addUser(user, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

router.delete('/users/:_id', function(req, res) {
  var id = req.params._id;

  User.removeUser(id, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

app.use('/api', router);
app.listen(3000);
console.log('Listening at 3000');

所以我尝试在使用任何类型的/ api / users / path之前需要一个令牌。我还有一个路径/ auth /,您可以通过身份验证获取令牌。但是当使用该路径(/ api / auth /)时,我也得到了#34;没有提供令牌"。当然我想在那里获得那个标记。当然我没有提供令牌,我还没有:)

我做错了什么?这是中间件的错误使用吗?还是其他什么?

第二个问题是我是否真的需要使用快速路由器。我使用它是因为我遵循了这个指南:https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens

3 个答案:

答案 0 :(得分:1)

问题是您没有定位所选路线。它基本上适用于所有人。没关系,我会重构你的代码并告诉你在ExpressJS中使用中间件的方式。 请看下面的参考,以了解我如何将authMiddleware与特定路线相关联。

构建您的应用应该是这样的。

app.js
app
    controller
        AppController.js
    middleware
        AuthMiddleware.js
routes
    index.js
    routes.js //configure router here.
views
public
.
.
.

我认为app.js是您的主要文件。

app.js
-----
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const config = require('./config');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
const app = express();

//connect to mongodb
var connect = function(){
    var options = {
        server: {
            socketOptions:{
                keepAlive : 1
            }
        }
    };
    console.log('info', 'connected to mongodb with config url : '+config.db);
    mongoose.connect(config.db,options);
};
connect();
mongoose.connection.on('error',console.log);
mongoose.connection.on('disconnected',connect);

require('./routes/routes')(app);
...
// your custom codes like locale, loggers etc. can go here.
...


routes/routes.js
---------
var index = require('./index');
var customRoute = require('./customRoute');// if you want to further refactor and move your /customRoute routes to a dedicated file.

module.exports = function (app){
    app.use('/',  index);
    //app.use('/customRoute', customRoute);
}

routes/index.js
----------------
var express = require('express');
var router = express.Router();
var ctrl = require('../app/controller/AppController');

var authCheck = require('../app/middlewares/AuthMiddleware');

router.get('/signup',ctrl.signup); //unprotected route
router.get('/login', ctrl.login);  //unprotected route
router.get('/users',authCheck, ctrl.deals); //protected route

app/middleware/AuthMiddleware.js
--------------------------------
module.exports = function(req, res, next){
    //write your logic here to check for token.
    if(token is present ){
        next();
    }else{
        // write logic to redirect to some view or return unauthorized response.
    }
};

因此,您可以看到您的应用程序现在更好,更容易管理。 如果成功登录,则可以在Browser cookie中存储令牌。 在AuthMiddleware中写入auth检查逻辑,读取cookie值。如果cookie数据有效,则允许用户继续请求或者返回未经授权的响应。

如果您想开始使用,请使用express generator。它将为您提供一个基本的应用程序,以便开始实验并继续学习。

答案 1 :(得分:0)

问题是,当您use路由器中的中间件时,它将用于包含路由器的所有路由中。在路由器中,router.use取决于优先级而不是您的情况。有关详细说明,请阅读this。您可以简单地在必须使用中间件的其他路由中更改中间件的使用。请参考以下代码。



const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const jsonwebtoken = require('jsonwebtoken');

const config = require('./config');
const User = require('./models/user');

// Connect to mongoose
mongoose.connect(config.db, { useMongoClient: true });
const db = mongoose.connection;

app.use(bodyParser.json());

app.get('/', function(req, res) {
  res.send('Please use /api/ with an existing endpoint.');
});

const router = express.Router();
const userRouter = express.Router();

router.post('/auth', function(req, res) {
  User.findOne({
    name: req.body.name
  }, function(err, user) {
    if (err)
      throw err;

    if (!user) {
      res.json({
        success: false,
        message: 'Authentication failed. User not found.'
      });
    } else {
      if (user.password != req.body.password) {
        res.json({
          success: false,
          message: 'Authentication failed. Wrong password.'
        });
      } else {
        const token = jsonwebtoken.sign(user, config.secret, { expiresIn: "20 seconds" });

        res.json({
          success: true,
          message: 'Authentication succeeded. Enjoy your token.',
          token: token
        });
      }
    }
  });
});

userRouter.use(function(req, res, next) {
  const token = req.body.token || req.query.token || req.headers['x-access-token'];

  if (token) {
    jsonwebtoken.verify(token, config.secret, function(err, decoded) {
      if (err) {
        res.status(403).json({
          success: false,
          message: 'Failed to authenticate token.'
        });
      } else {
        req.decoded = decoded;
        next();
      }
    });
  } else {
    res.status(403).json({
      success: false,
      message: 'No token provided.'
    });
  }
});

userRouter.get('/', function(req, res) {
  User.getUsers(function(err, users) {
    if (err)
      throw err;

    res.json(users);
  });
});

userRouter.get('/:_id', function(req, res) {
  User.getUserById(req.params._id, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

useRouter.post('/', function(req, res) {
  var user = req.body;

  User.addUser(user, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

userRouter.delete('/:_id', function(req, res) {
  var id = req.params._id;

  User.removeUser(id, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

router.use('/users', userRouter);
app.use('/api', router);
app.listen(3000);
console.log('Listening at 3000');




答案 2 :(得分:0)

我很抱歉,但我的代码按预期工作。我刚试错了。我测试了get请求,但需要用post测试。根据我读到的指南,声明的顺序很重要。这就是为什么它的工作原理。