Express + PassportJS 无法读取 flash 消息

Posted

技术标签:

【中文标题】Express + PassportJS 无法读取 flash 消息【英文标题】:Express + PassportJS can't read flash messages 【发布时间】:2020-07-18 14:18:49 【问题描述】:

我有我的 Express + Passport + Firebase 项目,我在其中使用本地策略处理身份验证。由于我发现 Passport 会处理身份验证过程,所以我还发现它会接受 flash 消息作为 done() 函数的第三个参数(在策略中)。但我不确定如何阅读它们:

我想我设置和阅读 Flash 消息的流程是:

    使用 NPM 安装 connect-flash。

    导入后设置 Express 中间件:

import * as flash from 'connect-flash';
...
const app = express();
...
app.use(flash());
    在 Express 路由中根据 documentation 配置 Passport 身份验证:
// POST - /api/v1/admin/oauth/login
router.post(
    '/login',
    async (req: Request, res: Response) =>  /* middleware function to validate input */ ,
    passport.authenticate('local', 
        failureRedirect: '/api/v1/admin/oauth/login',
        failureFlash: true
    ),
    async (req: Request, res: Response) =>  /* function after success login */
);
    done()方法中包含flash消息,根据Passport configuration documentation:
import  Strategy as LocalStrategy  from 'passport-local';
import db from '../../config/database';
import * as bcrypt from 'bcryptjs';

export default new LocalStrategy( usernameField: 'email' , async (email, password, done) => 
    const ref = db.collection('users').doc(email);
    try 
        const doc = await ref.get();
        if (!doc.exists) 
            return done(null, false,  error: 'Wrong email' );
        

        const user = doc.data();

        const match: boolean = await bcrypt.compare(password, user.password);
        if (!match) 
            return done(null, false,  error: 'Wrong password' );
        

        user.id = doc.id;
        delete user.password;

        return done(null, user);

     catch(error) 
        return done(error);
    
);
    使用req.flash('error')阅读flash消息:
// GET - /api/v1/admin/oauth/login
router.get('/login', (req: any, res: Response) => 
    const result: IResult = 
        message: '',
        data: null,
        ok: false
    ;
    if (req.flash('error')) 
        resultado.message = req.flash('error');
        console.log(req.flash('error'));
    
    return res.status(400).json(result);
);

我认为它在我的脑海中在理论上起作用,直到第 5 步,req.flash('error') 中有一个空数组。我做错了什么?

【问题讨论】:

Flash 消息仅在请求的生命周期内可用。向HTTP POST 发出/login 请求会获得使用权并闪烁一条消息,该消息可以适当地呈现以响应HTTP POST 请求。 HTTP GET 请求是一个单独的请求,不会在另一个 HTTP POST 请求中检索闪现的消息。 @OluwafemiSule 这不正确。 Flash 消息存储在会话中,可用于后续请求。 这是闪存消息的记录行为。那么文档肯定是错误的。 @OluwafemiSule 来源?文档显示“使用 Flash 消息需要 req.flash() 函数”。当使用connect-flash 提供req.flash() 功能时,根据问题,闪存消息存储在会话中。 12 其实我错了。闪过的消息是 cookie 的,可在后续请求中使用,但只能读取一次。 【参考方案1】:

您传递的 flash 消息是错误的!

done() 的第三个参数应该是一个具有typemessage 字段的对象:

return done(null, false,  message: 'Wrong email' );

类型默认为error

此 API 似乎没有明确记录,但在 Passport.js 文档的 配置 章节中的 3rd example of the Verify Callback section 中显示。

我创建了a repo 并带有一个可重现的工作示例。

【讨论】:

我已经尝试过了,但没有用...因为说我尝试过的错误,您应该阅读Passport documentation,其中没有提及任何有关特定对象名称的内容。不过还是谢谢。 @Maramal 我已经更新了答案。我已经包含了一个最低限度的工作示例。如果该示例对您不起作用,则说明有问题。如果是这样,则问题出在代码的其他地方。 我可能不是最好的开发人员,但我理解这段代码。但是,既然我已经花掉了那个名声,我会把它给你,因为你创建了一个 repo 并努力向我解释,就像我是个白痴一样,我很感激...... 我不得不说我可能过于复杂了。我刚刚提到了req.flash('error')[0],效果很好。 @Maramal 谢谢,希望对你有所帮助【参考方案2】:

我一直在搜索,找到了一个解决方案,但它在第二次登录尝试时有效。

我修改后的问题步骤:

    使用 NPM 安装 connect-flash。

    导入后设置 Express 中间件:

import * as flash from 'connect-flash';
...
const app = express();
...
app.use(flash());
    在 Express 路由中根据 documentation 配置 Passport 身份验证:
// POST - /api/v1/admin/oauth/login
router.post(
    '/login',
    async (req: Request, res: Response) =>  /* middleware function to validate input */ ,
    passport.authenticate('local',  
        failureFlash: true,
        failureRedirect: '/api/v1/admin/oauth/login' 
    ),
    async (req: Request, res: Response) =>  /* function after success login */
);
    创建另一条路线,以便它可以显示 Flash 消息,感谢@Codebling:
// GET - /api/v1/admin/oauth/login
router.get('/login', (req: Request, res: Response) => 
    const result: IResult = 
        message: 'Auth ok',
        data: null,
        ok: true
    ;

    let status: number = 200;
    const flashMessage: any = req.flash('error');

    if (flashMessage.length) 
        resultado.message = flashMessage[0];
        resultado.ok = false;
        status = 400; 
    

    return res.status(status).json(result);
);
    done()方法中包含flash消息,根据Passport configuration documentation:
import  Strategy as LocalStrategy  from 'passport-local';
import db from '../../config/database';
import * as bcrypt from 'bcryptjs';

export default new LocalStrategy( usernameField: 'email' , async (email, password, done) => 
    const ref = db.collection('users').doc(email);
    try 
        const doc = await ref.get();
        if (!doc.exists) 
            return done(null, false,  message: 'Wrong email' );
        

        const user = doc.data();

        const match: boolean = await bcrypt.compare(password, user.password);
        if (!match) 
            return done(null, false,  message: 'Wrong password' );
        

        user.id = doc.id;
        delete user.password;

        return done(null, user);

     catch(error) 
        return done(error);
    
);

【讨论】:

为什么说“仅在第二次登录尝试时有效”?这就是闪存消息的工作方式。通常你会 failureRedirect 到显示 flash 消息的页面。 我明白了,我的错。不是在预身份验证过程中检查闪存消息的选项,因为它将仅第二次读取闪存消息。所以我必须始终使用failureRedirect。然后它第一次工作。我不应该再次感谢你......但谢谢你。

以上是关于Express + PassportJS 无法读取 flash 消息的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 PassportJS 读取未定义、NestJS 的属性“validateUser”

Express Passportjs在路由器回调中未进行身份验证

Express 会话与 PassportJS 会话

如何使用 Express & Passportjs 检查 MongoDB 文档中的特定字段

Express 会话和 PassportJS 之间的区别

在 React/Express 中使用 PassportJS 进行 Google OAuth 时出现 CORS 错误