安全性是任何应用程序的关键组成部分。NestJS 提供了多种安全机制,包括身份验证、授权、数据加密、防护措施等,帮助开发者构建安全可靠的应用程序。
身份验证(Authentication)
JWT 认证
安装依赖
bashnpm install @nestjs/jwt @nestjs/passport passport passport-jwt npm install -D @types/passport-jwt
创建 JWT 策略
typescriptimport { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; import { ConfigService } from '@nestjs/config'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(private configService: ConfigService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: configService.get('JWT_SECRET'), }); } async validate(payload: any) { return { userId: payload.sub, username: payload.username }; } }
创建认证服务
typescriptimport { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { UsersService } from '../users/users.service'; import * as bcrypt from 'bcrypt'; @Injectable() export class AuthService { constructor( private usersService: UsersService, private jwtService: JwtService, ) {} async validateUser(username: string, password: string): Promise<any> { const user = await this.usersService.findByUsername(username); if (user && await bcrypt.compare(password, user.password)) { const { password, ...result } = user; return result; } return null; } async login(user: any) { const payload = { username: user.username, sub: user.userId }; return { access_token: this.jwtService.sign(payload), }; } async register(username: string, password: string) { const hashedPassword = await bcrypt.hash(password, 10); return this.usersService.create({ username, password: hashedPassword }); } }
创建认证控制器
typescriptimport { Controller, Post, Body, UseGuards, Request } from '@nestjs/common'; import { AuthService } from './auth.service'; import { LocalAuthGuard } from './local-auth.guard'; import { JwtAuthGuard } from './jwt-auth.guard'; @Controller('auth') export class AuthController { constructor(private authService: AuthService) {} @UseGuards(LocalAuthGuard) @Post('login') async login(@Request() req) { return this.authService.login(req.user); } @Post('register') async register(@Body() registerDto: { username: string; password: string }) { return this.authService.register(registerDto.username, registerDto.password); } @UseGuards(JwtAuthGuard) @Post('profile') getProfile(@Request() req) { return req.user; } }
本地认证策略
typescriptimport { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-local'; import { AuthService } from './auth.service'; @Injectable() export class LocalStrategy extends PassportStrategy(Strategy) { constructor(private authService: AuthService) { super({ usernameField: 'username', passwordField: 'password', }); } async validate(username: string, password: string): Promise<any> { const user = await this.authService.validateUser(username, password); if (!user) { throw new UnauthorizedException(); } return user; } }
OAuth2 认证
安装依赖
bashnpm install @nestjs/passport passport passport-google-oauth20 npm install -D @types/passport-google-oauth20
创建 Google OAuth 策略
typescriptimport { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy, VerifyCallback } from 'passport-google-oauth20'; import { ConfigService } from '@nestjs/config'; @Injectable() export class GoogleStrategy extends PassportStrategy(Strategy, 'google') { constructor(private configService: ConfigService) { super({ clientID: configService.get('GOOGLE_CLIENT_ID'), clientSecret: configService.get('GOOGLE_CLIENT_SECRET'), callbackURL: configService.get('GOOGLE_CALLBACK_URL'), 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); } }
授权(Authorization)
基于角色的访问控制(RBAC)
typescriptimport { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [ context.getHandler(), context.getClass(), ]); if (!requiredRoles) { return true; } const { user } = context.switchToHttp().getRequest(); return requiredRoles.some((role) => user?.roles?.includes(role)); } }
角色装饰器
typescriptimport { SetMetadata } from '@nestjs/common'; export const ROLES_KEY = 'roles'; export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
使用角色守卫
typescript@Controller('users') export class UsersController { @Get('admin') @UseGuards(JwtAuthGuard, RolesGuard) @Roles('admin') getAdminData() { return 'Admin data'; } }
密码加密
使用 bcrypt
typescriptimport * as bcrypt from 'bcrypt'; async hashPassword(password: string): Promise<string> { const salt = await bcrypt.genSalt(10); return bcrypt.hash(password, salt); } async comparePassword(password: string, hash: string): Promise<boolean> { return bcrypt.compare(password, hash); }
安全中间件
Helmet
typescriptimport helmet from 'helmet'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(helmet()); await app.listen(3000); }
CORS 配置
typescriptasync function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors({ origin: 'https://example.com', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', credentials: true, }); await app.listen(3000); }
速率限制
typescriptimport rateLimit from 'express-rate-limit'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use( rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs }), ); await app.listen(3000); }
数据验证
使用 class-validator
typescriptimport { IsEmail, IsString, MinLength } from 'class-validator'; export class CreateUserDto { @IsEmail() email: string; @IsString() @MinLength(6) password: string; }
全局验证管道
typescriptimport { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes( new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, }), ); await app.listen(3000); }
SQL 注入防护
使用参数化查询
typescript// 不安全 const query = `SELECT * FROM users WHERE name = '${name}'`; // 安全 const query = `SELECT * FROM users WHERE name = ?`; const result = await this.userRepository.query(query, [name]);
使用 ORM
typescript// TypeORM 自动防止 SQL 注入 const user = await this.userRepository.findOne({ where: { name: username }, });
XSS 防护
输入清理
typescriptimport * as xss from 'xss'; function sanitizeInput(input: string): string { return xss(input); }
内容安全策略(CSP)
typescriptimport helmet from 'helmet'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use( helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"], }, }), ); await app.listen(3000); }
CSRF 防护
使用 csurf
typescriptimport * as csurf from 'csurf'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(csurf({ cookie: true })); await app.listen(3000); }
安全最佳实践
- 使用 HTTPS:始终在生产环境中使用 HTTPS
- 环境变量:敏感信息存储在环境变量中
- 密码加密:使用 bcrypt 等安全算法加密密码
- 输入验证:验证所有用户输入
- 最小权限原则:用户只拥有必要的权限
- 定期更新依赖:保持依赖项更新以修复安全漏洞
- 日志记录:记录安全相关事件
- 错误处理:不向客户端暴露敏感错误信息
- 会话管理:使用安全的会话管理
- 安全头:设置适当的安全头
总结
NestJS 安全和认证提供了:
- 多种身份验证策略
- 灵活的授权机制
- 强大的数据保护
- 全面的安全防护
- 易于集成的安全工具
掌握安全性和认证是构建安全、可靠的 NestJS 应用程序的关键。通过合理使用认证策略、授权机制和安全最佳实践,可以保护应用程序免受各种安全威胁,确保用户数据和系统的安全。