使用 Passport 和 Nest 框架已发送标头的错误

DWi*_*ick 5 typescript google-oauth passport.js nestjs

我有一个 NestJS 应用程序,其中包含一些身份验证逻辑。登录是使用 Passport 为 Google Oauth 实现的,似乎可以正常工作,但在服务器上我会出现此错误:

(node:19195) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Run Code Online (Sandbox Code Playgroud)

我谷歌了一下,似乎常见的原因之一可能是向客户端发送几次响应。话虽如此,我无法在我的代码中发现这一点,所以我怀疑这是否是原因。以下是相关的片段。

使用@nestjs/passport 对用户进行序列化和反序列化。

@Injectable()
export class GoogleSerializer extends PassportSerializer {
    constructor(private readonly usersService: UsersService) {
        super();
    }

    public serializeUser(user: User, done: Function) {
        console.log('serialize', user);
        if (user) {
            done(null, user.id);
        } else {
            done(null);
        }
    }

    public async deserializeUser(id: number, done: Function) {
        console.log('deserialize', id);
        this.usersService.findById(id).then(user => {
            done(null, user);
        }).catch(error => {
            done(error, null);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

这是 Passport JS 的 Google 策略:

@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly usersService: UsersService) {
    super({
      clientID:
        'client',
      clientSecret: 'I'm-secret',
      callbackURL: '/auth/google/callback',
      scope: ['email', 'profile'],
    });
  }

  async validate(accessToken, refreshToken, profile, done) {
    const user = <create the user object from google profile> 

    this.usersService.findOne(user.email).then(userEntity => {
      if (!userEntity) {
        this.usersService.createOne(user);
      }
    });
    return this.usersService
      .findOne(user.email)
      .then(user => {
        done(null, user);
        return user;
      })
      .catch(error => {
        done(error);
        return null;
      });
  }
}
Run Code Online (Sandbox Code Playgroud)

这是 AuthController 的路由:

@Controller('auth')
export class AuthController {
  @Get('/google/callback')
  async googleCallback(@Req() req, @Res() res, @Next() next) {
    passport.authenticate('google', {
      successReturnToOrRedirect: '/users/test',
      failureRedirect: '/auth/login'
    })(req, res, next);
  }

  @Get('/login')
  async login(@Req() req) {
    return '<a href="/auth/google/login">Login</a>';
  }

  @Get('/google/login')
  async googleLogin(@Req() req, @Res() res, @Next() next) {
    passport.authenticate('google', { scope: ['profile', 'email'] })(req, res, next);
  }
}
Run Code Online (Sandbox Code Playgroud)

我有一个用户需要登录的路径的中间件:

    @Injectable()
    export class LoginRequired implements NestMiddleware {
        resolve(...args: any[]): MiddlewareFunction {
            console.log('calling loginrequired');
            return (req, res, next) => {
                if (!req.isAuthenticated || !req.isAuthenticated()) {
                    if (req.session) {
                        req.session.returnTo = req.originalUrl || req.url;
                    }
                    return res.redirect('/auth/login');
                }
                next();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

这是设置阶段:

app.use(session({
secret: 'secret',
resave: false,
saveUninitialized: true,
cookie: { secure: false }}));
app.use(passport.initialize());
app.use(passport.session());
Run Code Online (Sandbox Code Playgroud)

例如,我尝试转到受此中间件保护的 /user 并且应该将用户发送到登录页面,该页面提示用户登录并将用户带到 /user 页面。一切正常,但我得到了

发送到客户端后无法设置标头

任何想法是如何发生的?

我使用 Pasport js 和 NestJs。