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

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

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

  1. 使用 NPM 安装 connect-flash

  2. 导入后设置 Express 中间件:

import * as flash from 'connect-flash';
...
const app = express();
...
app.use(flash());
  1. 在 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 */
);
  1. 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);
    }
});
  1. 使用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 的,可在后续请求中使用,但只能读取一次。

标签: node.js typescript express flash passport.js


【解决方案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】:

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

我修改后的问题步骤:

  1. 使用 NPM 安装 connect-flash

  2. 导入后设置 Express 中间件:

import * as flash from 'connect-flash';
...
const app = express();
...
app.use(flash());
  1. 在 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 */
);
  1. 创建另一条路线,以便它可以显示 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);
});
  1. 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。然后它第一次工作。我不应该再次感谢你......但谢谢你。
猜你喜欢
  • 1970-01-01
  • 2011-08-18
  • 2011-01-12
  • 1970-01-01
  • 1970-01-01
  • 2022-11-17
  • 1970-01-01
  • 2016-02-13
  • 2016-12-15
相关资源
最近更新 更多