You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS使用passport-local验证时合法用户仍返回401(Unauthorized)求助

NestJS passport-local 身份认证返回401未授权问题

我按照教程在NestJS中基于passport-local实现身份认证,但使用合法用户请求时始终返回401错误,找不到问题所在。

代码结构

  • 认证模块结构:Authentication Module
  • 用户模块结构:User Module

代码详情

认证模块

authentication.module.ts

import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { UserModule } from 'src/user/user.module';
import { AuthenticationController } from './controllers/authentication.controller';
import { AuthenticationService } from './services/authentication.service';
import { LocalStrategy } from './strategies/local.strategy';

@Module({
  imports:[UserModule, PassportModule],
  controllers: [AuthenticationController],
  providers: [AuthenticationService, LocalStrategy]
})
export class AuthenticationModule {}

authentication.controller.ts

import { Controller, Post, Request, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { UserService } from 'src/user/services/user.service';

@Controller('authentication')
export class AuthenticationController {

    constructor(private userService: UserService){}

    @UseGuards(AuthGuard('local'))
    @Post('signin')
    async signin(@Request() req){
        return req.user;
    }
}

authentication.service.ts

import { Injectable } from '@nestjs/common';
import { UserService } from 'src/user/services/user.service';

@Injectable()
export class AuthenticationService {

    constructor(private userService: UserService) {}
    
    async validateUser(email: string, password: string): Promise<any> {
        const user = await this.userService.readUserByEmail(email);

        if (user && user.password === password) {
            const { password, ...result } = user;
            return result;
        }
        return null;
    }
}

local.strategy.ts

import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-local";
import { AuthenticationService } from "../services/authentication.service";

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy){

    constructor(private authenticationService: AuthenticationService){
        super();
    }
    
    async validate(username: string, password: string): Promise<any> {
        const user = await this.authenticationService.validateUser(username, password);

        if (!user) {
            throw new UnauthorizedException();
        }

        return user;
    }
}

用户模块

user.module.ts

import { Module } from '@nestjs/common';
import { UserController } from './controllers/user.controller';
import { UserService } from './services/user.service';

@Module({
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService]
})
export class UserModule {}

user.service.ts

import { Injectable } from '@nestjs/common';
import { prisma } from 'src/main';
import { CreateUserDTO } from '../dto/create-user.dto';
import { UpdateUserDTO } from '../dto/update-user.dto';

@Injectable()
export class UserService {

    private readonly users = [
        {
            id: "1",
            name: "Ajitesh",
            email: "ajitesh@example.com",
            password: "secret"
        }
    ]

    //....其他方法

    async readUserByEmail(email: string){
        return this.users.find(user => user.email === email);
    }
}

请求示例

{
 "email": "ajitesh@example.com",
 "password": "secret"
}

问题原因及解决方法

passport-local 默认会从请求体中读取username和password字段,但你的请求传的是email和password,导致策略无法正确获取邮箱参数,验证失败返回401。

解决步骤:

  1. 在LocalStrategy的构造函数中,通过super()配置自定义字段名,将用户名字段指定为email:
constructor(private authenticationService: AuthenticationService){
    super({ usernameField: 'email' });
}
  1. 可选:将validate方法的第一个参数名改为email,语义更清晰:
async validate(email: string, password: string): Promise<any> {
    const user = await this.authenticationService.validateUser(email, password);

    if (!user) {
        throw new UnauthorizedException();
    }

    return user;
}

修改后,passport-local就能正确读取请求体中的email字段完成用户验证。

内容的提问来源于stack exchange,提问作者Ajitesh Sivakumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:55:32