即使在经过身份验证的中间件之后,Express req.user 也是可选的

Posted

技术标签:

【中文标题】即使在经过身份验证的中间件之后,Express req.user 也是可选的【英文标题】:Express req.user is optional even behind an authenticated middleware 【发布时间】:2021-04-08 09:31:58 【问题描述】:

当使用 Passport 和 Express 时,Typescript 认为 req.user 在使用 auth 中间件时可能在路由内未定义。使用身份验证中间件时,我希望在所有情况下都定义 req.user,否则中间件将返回 401 未授权响应。

如果路由使用这样的授权中间件:

app.use( '/users', passport.authenticate('bearer',  session: false ), usersRouter ); 

/users 中的路由不应该有可能未定义的用户,因为它永远不会。

如何在不为每个路由声明要使用的自定义请求接口的情况下实现这一点?

【问题讨论】:

您能否提供minimal reproducible example - 您希望中间件在哪里更改处理程序中请求对象的类型? 如果路由使用这样的认证中间件:app.use('/users', passport.authenticate('bearer', session: false ), usersRouter); /users 中的路由不应该有可能未定义的用户,因为它永远不会。 那应该如何改变 userRouter 或请求的类型?路由器类型不是由它使用的地方定义的。 这就是我的问题,我不确定它是如何实现的,但我很好奇它是否可能。否则,我是否必须在每条路由的顶部执行 if (!req.user) return res.status(401).send() 之类的操作? 你可以像库一样扩展 Express 命名空间的 Request 接口,使用户成为非可选的,但它不能警告你 不是的路由i> 已通过身份验证。 【参考方案1】:

简而言之,您无法让 Typescript 了解您的中间件是否设置为 req.user,因为 TS 不“了解”中间件在 Express 中的工作方式。但是,您可以扩展请求接口并在您知道已设置值的情况下使用您的自定义实现(也就是在经过身份验证的路由中)。

定义自定义接口:

import  Request  from 'express';
import  YourUserDefinitionInterface  from './your-user-definition.interface.ts';

export interface AuthenticatedRequest extends Request 
  user: YourUserDefinitionInterface 

在路线中使用它:

// The below route is behind the auth middleware
app.get('/some-path', (req: AuthenticatedRequest) => 
  // Your handling logic here
);

【讨论】:

【参考方案2】:

简单地说javascript,它相当容易在例如req.user=name:"Jane",email:"janeDoe@gmail.com" 中动态设置属性。但是来到Typescript 并不是那么直接。我解决此问题的方法是在请求对象上设置 dynamic property 并使用自定义的 express.Request 类型来处理所有请求

import Request,Response,NextFunction from "express"

export type RequestType = 
    [prop: string]: any
 & Request

export const Authorize = async (
    req: RequestType,
    res: Response,
    next: NextFunction,
) => 
    try 
        const AuthHeaderToken = req.headers.authorization
        if (!AuthHeaderToken)
            throw new ErrorResponse('Auth header token not provided', 400)
        const token = AuthHeaderToken.split(' ')[1]
        return jwt.verify(token, SECRET_KEY!, (err, payload) => 
            if (err)
                throw new ErrorResponse('Invalid or expired auth token', 400)
            req.user = payload
            return next()
        )
     catch (err) 
        return next(err)
    

对于此实现,您可以即时为 Request 对象设置任何属性。希望对你有用

【讨论】:

以上是关于即使在经过身份验证的中间件之后,Express req.user 也是可选的的主要内容,如果未能解决你的问题,请参考以下文章

express jwt - 身份验证中间件问题

如何使用基于 JWT 令牌的 express 获取经过身份验证的用户信息

Node/Express 的 Passport 身份验证中间件 - 如何保护所有路由

哨兵:Express api基本身份验证中间件使哨兵无法工作

IIS Express - 临时模拟经过身份验证的用户

如何使 Express 路由默认需要身份验证?