NestJS Passport认证 #
Passport简介 #
Passport是Node.js中最流行的认证中间件,支持多种认证策略(本地、OAuth、OpenID等)。NestJS提供了@nestjs/passport模块来集成Passport。
安装依赖 #
bash
npm install @nestjs/passport passport passport-local
npm install -D @types/passport-local
本地认证 #
配置Passport模块 #
typescript
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
@Module({
imports: [PassportModule],
providers: [AuthService, LocalStrategy],
})
export class AuthModule {}
认证服务 #
typescript
import { Injectable, UnauthorizedException } from '@nestjs/common';
@Injectable()
export class AuthService {
private users = [
{ id: 1, username: 'john', password: 'changeme' },
{ id: 2, username: 'maria', password: 'guess' },
];
async validateUser(username: string, password: string): Promise<any> {
const user = this.users.find(
u => u.username === username && u.password === password,
);
if (user) {
const { password, ...result } = user;
return result;
}
return null;
}
}
本地策略 #
typescript
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({ usernameField: 'email' });
}
async validate(username: string, password: string): Promise<any> {
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
return user;
}
}
认证控制器 #
typescript
import { Controller, Post, UseGuards, Request } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('auth')
export class AuthController {
@UseGuards(AuthGuard('local'))
@Post('login')
async login(@Request() req) {
return req.user;
}
}
JWT认证 #
安装依赖 #
bash
npm install @nestjs/jwt passport-jwt
npm install -D @types/passport-jwt
JWT配置 #
typescript
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.register({
secret: 'YOUR_SECRET_KEY',
signOptions: { expiresIn: '60s' },
}),
],
providers: [JwtStrategy],
exports: [JwtModule],
})
export class AuthModule {}
JWT策略 #
typescript
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'YOUR_SECRET_KEY',
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
登录返回Token #
typescript
import { Controller, Post, Body, UseGuards, Request } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@UseGuards(AuthGuard('local'))
@Post('login')
async login(@Request() req) {
return this.authService.login(req.user);
}
}
typescript
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) {}
async login(user: any) {
const payload = { username: user.username, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
}
保护路由 #
typescript
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('profile')
export class ProfileController {
@UseGuards(AuthGuard('jwt'))
@Get()
getProfile(@Request() req) {
return req.user;
}
}
自定义Guard #
typescript
import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from './public.decorator';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}
OAuth认证 #
Google OAuth #
安装依赖:
bash
npm install passport-google-oauth20
npm install -D @types/passport-google-oauth20
Google策略:
typescript
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor() {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/auth/google/callback',
scope: ['email', 'profile'],
});
}
async validate(
accessToken: string,
refreshToken: string,
profile: any,
done: VerifyCallback,
): Promise<any> {
const { name, emails, photos } = profile;
const user = {
email: emails[0].value,
firstName: name.givenName,
lastName: name.familyName,
picture: photos[0].value,
accessToken,
};
done(null, user);
}
}
控制器:
typescript
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('auth')
export class AuthController {
@Get('google')
@UseGuards(AuthGuard('google'))
async googleAuth() {}
@Get('google/callback')
@UseGuards(AuthGuard('google'))
googleAuthRedirect(@Request() req) {
return this.authService.googleLogin(req);
}
}
GitHub OAuth #
安装依赖:
bash
npm install passport-github2
npm install -D @types/passport-github2
GitHub策略:
typescript
import { Strategy } from 'passport-github2';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
@Injectable()
export class GitHubStrategy extends PassportStrategy(Strategy, 'github') {
constructor() {
super({
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/auth/github/callback',
scope: ['user:email'],
});
}
async validate(
accessToken: string,
refreshToken: string,
profile: any,
done: any,
): Promise<any> {
const user = {
id: profile.id,
username: profile.username,
email: profile.emails?.[0]?.value,
accessToken,
};
done(null, user);
}
}
刷新Token #
typescript
@Injectable()
export class AuthService {
constructor(
private jwtService: JwtService,
private usersService: UsersService,
) {}
async login(user: any) {
const payload = { username: user.username, sub: user.id };
return {
access_token: this.jwtService.sign(payload, { expiresIn: '15m' }),
refresh_token: this.jwtService.sign(payload, { expiresIn: '7d' }),
};
}
async refreshToken(refreshToken: string) {
try {
const payload = this.jwtService.verify(refreshToken);
const user = await this.usersService.findOne(payload.sub);
if (!user) {
throw new UnauthorizedException();
}
return {
access_token: this.jwtService.sign(
{ username: user.username, sub: user.id },
{ expiresIn: '15m' },
),
};
} catch {
throw new UnauthorizedException('Invalid refresh token');
}
}
}
密码加密 #
安装bcrypt:
bash
npm install bcrypt
npm install -D @types/bcrypt
使用:
typescript
import * as bcrypt from 'bcrypt';
@Injectable()
export class UsersService {
async create(createUserDto: CreateUserDto) {
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
return this.usersRepository.create({
...createUserDto,
password: hashedPassword,
});
}
async validatePassword(plainPassword: string, hashedPassword: string) {
return bcrypt.compare(plainPassword, hashedPassword);
}
}
总结 #
本章学习了NestJS与Passport的集成:
- 本地认证
- JWT认证
- OAuth认证(Google、GitHub)
- Token刷新
- 密码加密
接下来,让我们学习 JWT认证。
最后更新:2026-03-28