Express.js中间件针对在其上方定义的路由执行

时间:2018-06-19 22:58:51

标签: node.js express jwt express-jwt

从我读过的herehere来看,您放置中间件功能的顺序很重要,因为如果某些路由放置在中间件功能之前,您可能会使其无法通过中间件功能,然后放置的路由将通过此中间件功能。

我看到的结果参差不齐,因为我的开发环境不尊重这一点,而我的产品环境则尊重。代码完全相同。

我要做的是让登录路由不受令牌检查器中间件功能的保护,而其余路由受令牌保护。

这是我的代码:

routes.get('/login', function(req, res) {
    // login user, get token
});

routes.use(function(req, res, next) {
    // check header or url parameters or post parameters for token
    var token = req.headers['access-token'];
    // decode token
    if (token) {
        // validate token
    }
    else if (req.method === 'OPTIONS') {
        next();
    }
    else {
        // if there is no token
        // return an error
        return res.status(403).send({
            success: false,
            message: 'No token provided.'
        });
    }
});

routes.get('/query/:keywords', function(req, res) {
    console.log(req.params.keywords);
    // execute query
});

app.use('/', routes);

/query路由是唯一必须正确通过令牌中间件功能的路由吗?现在,我也通过令牌中间件功能获得了/login路由,这没有意义,因为我不需要登录就可以拥有令牌。

更好的是,如果有一种方法可以定位我想要保护的路由以及不想要保护的路由,那似乎比依赖于放置中间件功能的“顺序”更好。

3 个答案:

答案 0 :(得分:2)

首先,遵循ExpressJS中的用法:

  

一个以上的回调函数可以处理一条路由(确保指定了下一个对象)。例如:

app.get('/example/b', function (req, res, next) {
  console.log('the response will be sent by the next function ...')
  next()
}, function (req, res) {
  res.send('Hello from B!')
})

您会注意到它的定义与您在routes.use(yourFunction(...))上声明的内容很接近。但是,除了遵循您在文档中看到的示例外,没有其他理由这样做,尽管这是一个很好的起点。

但是,这是一个脆弱的实现,express将允许其.get() .post()方法中的层次结构,这是正确的,但这是特定于用例的,而不是您要查找的内容。

您需要使用双重回调配置来实现自定义身份验证过程。做到这一点:

// You can save this function in a separate file and import it with require() if you want

const tokenCheck = function(req, res, next) {
    // check header or url parameters or post parameters for token
    var token = req.headers['access-token'];
    // decode token
    if (token) {
        // validate token
    }
    else if (req.method === 'OPTIONS') {
        next();
    }
    else {
        // if there is no token
        // return an error
        return res.status(403).send({
            success: false,
            message: 'No token provided.'
        });
    }
});


routes.get('/login', function(req, res) {
    // login user, get token [Unprotected]
});

routes.get('/query/:keywords', tokenCheck, function(req, res) {
    console.log(req.params.keywords);
    // execute query [Protected with tokenCheck]
});

app.use('/', routes);

您可能需要使用上面的代码,但是它将指导您正确的方向,通过这种方式,您可以指定特定的路由来根据需要执行tokenCheck(req, res, next)函数。

答案 1 :(得分:0)

由于路由顺序以及登录路由从不调用下一个对象这一事实,令牌中间件的应用不应在登录路由上发生。没有更多的信息,我们真的无法排除发生的一切,但是您可以try inspecting it in your dev environment with a debugger中断并查看影响该中间件的需求。

但是,我们可以为您提供一些有关如何尝试隔离您的.use中间件以及如何应用中间件顺序的信息,以便您可以像在问题的底部完全将其与登录路径分开。


当仅将中间件应用于特定路由时,应注意order和.use用于应在响应请求的中间件,然后告诉express继续在路由器中寻找它们之后的其他中间件,它们也将处理该请求。如果只想在一些路由上使用,则可以像这样明确地将其添加到一些路由上:

router.get('/route', [ middleware1, middleware2, ..., middlewareX])

router.get('/route', middleware1, middleware2, ..., middlewareX)
两种模式都可以使用。但是,我发现数组模式更可口,因为我可以定义很多我想应用的中间件,然后将新的中间件连接为特定的逻辑,并且只需要在声明该连接的地方进行修改即可添加更多功能。但是,很少需要那么多中间件,并且您应该能够使用其中任何一个。

您还可以通过使用路由器将该中间件划分为路由的子集,并将其作为第一个中间件应用于路由器之前的路由链。

app.use('/user', authentication, userRouter)

或者您可以使用.use将其作为第一个中间件放入路由器内部,以便处理所有请求。

因此请记住有关中间件使用的一般提示:

您可以在expressjs documentation for middleware

中找到有关此信息的更多信息

答案 2 :(得分:0)

最简单的方法是使用路由器中间件来确定需要身份验证的路由和不需要身份验证的路由的范围。由于所有路由器都是中间件,因此我们可以像其他任何中间件一样实现它们。确保按希望评估路由的顺序放置路由器和路由。

在下面的示例中,Express服务器具有2个路由器,分别是LoginRouter和ApiRouter。

  • LoginRouter-收到对POST /login的请求时生成令牌,并将令牌返回给请求者,以供以后在/api路由中使用。
  • ApiRouter-包装所有其他路由器,集中需要在/ api下全局应用于所有路由的中间件。仅可用于身份验证的请求。

仅当标头中包含令牌并且该令牌是从LoginRouter获得的时,才可以访问API路由器。 LoginRouter不需要身份验证。

使用此设置,您将继续通过ApiRouter上的.use()将授权中间件之后的路由器添加到API路由器。

以下由其他路由器组成路由器的模式非常强大,可扩展且易于维护。

server.js

const express = require('express')
const bodyParser = require('bodyParser')
const ApiRouter = require('./routes/api')
const LoginRouter = require('./routes/login')
const port = process.env.PORT || 1337

const server = express()

server.use(bodyParser.json())

server.use('/login', LoginRouter)
server.use('/api', ApiRouter)

server.listen(port, () => console.log(`Listening on ${port}`))

LoginRouter-/routes/login.js

const router = require('express').Router()

router.post('/', (req, res) => {    
    // Validate Credentials
    // some validation code...

    // Then create the token for use later in our API
    let token = '...'

    // Response 200 OK with the token in the message body   
    return res.status(200).send({token})
})

module.exports = router

ApiRouter-/routes/api/index.js

const router = require('express').Router()    
const UsersRouter = require('./routes/api/users')

router.use((req, res, next) => {
    let authorizationHeader = req.headers['authorization'] || req.headers['Authorization'] // handle lowercase
    let [, token] = authorizationHeader.split(' ')
    if (!token) {
        return res.sendStatus(403) // Forbidden, you're not logged in
    } else {
        // validate the token    
        if (!tokenIsValid) {
            return res.sendStatus(403) // Forbidden, invalid token
        } 

        // Everything is good, continue to the next middleware
        return next()
    }
})

router.use('/users', UsersRouter)

module.exports = router

UsersRouter-/ routes / api / users

const router = require('express').Router()

router.get('/', (req, res) => {
    // We only get here if the user is logged in     
    return res.status(200).json({users: []})
})

module.exports = router