使用电子邮件进行身份验证时,NestJS 护照身份验证返回 401

Posted

技术标签:

【中文标题】使用电子邮件进行身份验证时,NestJS 护照身份验证返回 401【英文标题】:NestJS passport authentication returns 401 when using email for authentication 【发布时间】:2020-07-18 21:15:03 【问题描述】:

我有一个似乎并不罕见的问题,但我找到的解决方案在我的项目中不起作用。

我想要做的是使用本教程建议的简单身份验证:https://docs.nestjs.com/techniques/authentication

我一直遵循本教程,起初它很有效。后来我决定使用用户电子邮件和密码作为身份验证而不是用户名。因此,我将身份验证过程中的变量名和参数更改为电子邮件,这就是一切都崩溃的地方。我在这里遗漏了什么吗?

auth.module.ts

import Module from '@nestjs/common';
import UsersModule from "../users/users.module";
import AuthService from "./services/auth.service";
import PassportModule from "@nestjs/passport";
import LocalStrategy from "./strategies/local.strategy";
import AuthController from "./controllers/auth.controller";
import JwtModule from "@nestjs/jwt";
import jwtConstants from "./constants";
import JwtStrategy from "./strategies/jwt.strategy";
import EncryptionModule from "../encryption/encryption.module";

@Module(
    imports: [
        UsersModule,
        EncryptionModule,
        PassportModule.register(defaultStrategy: 'jwt'),
        JwtModule.register(
            secret: jwtConstants.secret,
            signOptions: 
                expiresIn: '30s'
            
        )
    ],
    providers: [
        AuthService,
        LocalStrategy,
        JwtStrategy
    ],
    controllers: [
        AuthController
    ]
)
export class AuthModule 

控制器/auth.controller.ts

import Controller, Get, Post, Request, UseGuards from '@nestjs/common';
import AuthService from "../services/auth.service";
import JwtAuthGuard from "../guards/jwt-auth.guard";
import LocalAuthGuard from "../guards/local-auth.guard";

@Controller('auth')
export class AuthController 
    constructor(private authService: AuthService) 
    

    @UseGuards(LocalAuthGuard)
    @Post('login')
    login(@Request() req) 
        return this.authService.login(req.user);
    

    @UseGuards(JwtAuthGuard)
    @Get('profile')
    getProfile(@Request() req) 
        return req.user;
    

服务/auth.service.ts

import Injectable from '@nestjs/common';
import UsersService from "../../users/services/users.service";
import User from "../../users/interfaces/user.interface";
import JwtService from "@nestjs/jwt";
import JwtPayloadDto from "../models/jwt-payload.dto";
import EncryptionService from "../../encryption/services/encryption.service";

@Injectable()
export class AuthService 
    constructor(private usersService: UsersService,
                private jwtService: JwtService,
                private encryptionService: EncryptionService) 
    

    async validateUser(email: string, pass: string): Promise<User | undefined> 
        /**
         * The findOne-method sends a database query
         * to my mongodb via mongoose.
         * I don't think it's necessary to post the UserService here, is it?
         */
        const user: User = await this.usersService.findOne(email);
        return this.encryptionService.compare(pass, user.password).then((result) => 
            if (result) 
                return user;
            
            return undefined;
        );
    

    async login(user: User) 
        const payload: JwtPayloadDto = 
            email: user.email,
            sub: user.id
        
        return 
            accessToken: this.jwtService.sign(payload)
        ;
    

策略/local.strategy.ts

import Injectable, UnauthorizedException from "@nestjs/common";
import PassportStrategy from "@nestjs/passport";
import Strategy from "passport-local";
import AuthService from "../services/auth.service";

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) 
    constructor(private authService: AuthService) 
        super();
    

    async validate(email: string, password: string): Promise<any> 
        const user = await this.authService.validateUser(email, password);
        if (!user) 
            throw new UnauthorizedException();
        
        return user;
    

守卫/local-auth.guard.ts

import Injectable from "@nestjs/common";
import AuthGuard from "@nestjs/passport";

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') 

根据this question,我发现 validate-methods 签名必须与请求有效负载密钥具有相同的参数名称。

出于调试目的,我在strategies/local.strategy.ts 的验证方法的第一行放置了一个console.log()-call,但它似乎根本没有被调用。

感谢您提前回答。 祝你好运!

【问题讨论】:

我需要更正最后一部分。我误读了。 validate-方法的签名必须是精确的用户名和密码。我将方法中的那部分改回原来的样子,但它仍然无法正常工作。 【参考方案1】:

对我来说,在创建 LocalStrategy 时,我将 usernameField: 'email' 传递给了 ParentClass。

如果您想使用“电子邮件”等自定义列检查用户身份验证,请尝试通过它。

我的 user.entity.ts:

@Entity()
export class User 
  @PrimaryGeneratedColumn()
  id: number;

  @Column( unique: true )
  email: string;

  @Column()
  name: string;

我的 local.strategy.ts:

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) 
  constructor(private authService: AuthService) 
    super( usernameField: 'email' );
  

  async validate(email: string, password: string): Promise<User> 
    console.log(email, password); // it works
  

【讨论】:

我在这个问题上花了 5 个小时!谢谢!【参考方案2】:

嗯,我自己解决了。 5个小时的调试浪费了! 原来,不知何故,我的邮递员没有随请求发送 Content-Type 标头。重新启动 Postman 即可解决。

【讨论】:

以上是关于使用电子邮件进行身份验证时,NestJS 护照身份验证返回 401的主要内容,如果未能解决你的问题,请参考以下文章

NestJS - 错误:未知的身份验证策略“本地”

Google oauth 不返回电子邮件护照身份验证

使用带护照本地的护照Facebook策略时无法将用户序列化到会话中

通过库共享时未注册 NestJS 自定义 PassportStrategy

在服务器端护照js本地用户身份验证后,如何在客户端使用cookie显示用户信息?

使用 JWT 进行护照身份验证:如何将护照的默认未经授权响应更改为我的自定义响应?