本文通过使用LocalAuth进行身份验证,身份确认后,使用JwtAuth颁发身份令牌以及验证身份令牌的有效性。
LocalAuth
Passport提供了一种名为passport-local的策略,它实现了使用用户名/密码的身份验证机制。
首先安装需要的包:
npm install --save @nestjs/passport passport passport-local
npm install --save-dev @types/passport-local
在nest项目中创建AuthModule和AuthService
nest g module auth
nest g service auth
首先创建本地身份验证策略。在auth文件夹下创建strategies文件夹,然后创建local.strategy.ts文件。添加如下代码:
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) {
// 默认不传任何参数,用户名和密码的字段是username 和 password
super();
// 还可以定制我们自己的用户名和密码字段,如下:
// 如果还有其他需求,可查看Strategy类的定义和使用方法
super({
usernameField: 'account',
passwordField: 'pass',
})
}
// 要实现的验证函数,固定写法
async validate(username: string, password: string): Promise<any> {
// 这里就可以写我们的身份验证流程,检测用户名和密码
// 比如:
// if(username !== 'admin' || password !== 'admin'){
// throw new UnauthorizedException();
// }
// return {username, password};
// 为了保证我们的策略指责单一,推荐把具体的验证逻辑放到authService中,此处只调用
const user = await this.authService.validateUser(username, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
在authService.ts中写入验证身份逻辑,代码如下:
import { Injectable } from '@nestjs/common';
@Injectable()
export class AuthService {
constructor() { }
async validateUser(name: string, password: string): Promise<any> {
if (name !== 'admin') {
// UnauthorizedException异常会返回HTTP状态码是401的响应
// 可根据我们的需求定制返回信息
throw new UnauthorizedException({
code: 401,
message: '用户不存在'
});
} else if (password !== 'admin') {
throw new UnauthorizedException({
code: 401,
message: '密码错误'
});
}
return { name, password };
}
}
最后,还需要做的就是修改authModule.ts,应用我们的providers,也就是authService和LocalStrategy。如下:
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { PassportModule } from '@nestjs/passport';
import { LocalStrategy } from './strategies/local.strategy';
@Module({
imports: [PassportModule],
providers: [AuthService, LocalStrategy]
})
export class AuthModule { }
至此,我们的本地身份验证策略就创建好了。下一步就是在登陆接口上使用此策略。@nestjs/passport提供了一种内置的守卫 AuthGuard,通过在接口上应用此守卫,就可以实现本地验证策略流程。登陆接口示例如下:
import { Post, Controller, Request, UseGuards} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('user')
export class UserController {
constructor() { }
@UseGuards(AuthGuard('local'))
@Post('login')
login(@Request() req): Record<string, any> {
// req的user属性,是在passport-local身份验证流程中由Passport填充的
return { ...req.user }
}
}
请求登陆接口返回如下:
{ name: 'admin', password: 'admin' }
至此,我们完成了身份验证过程。但我们期望的返回值并不是一个用户对象,应该是一个加密后的身份令牌,后续使用这个令牌去请求其他功能,这样才能保证我们业务过程中的身份安全和数据安全。接下来就使用jwt来颁发令牌,继续向下:
JwtAuth
安装jwt相关的包:
npm install --save @nestjs/jwt passport-jwt
npm install --save-dev @types/passport-jwt
首先,创建生成jwt令牌的方法,在authService.ts中添加getToken方法:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
// ...
getToken(user) {
// 这里定义加密令牌的payload,可根据自己的需求定义
const payload = { ...user };
return this.jwtService.sign(payload);
}
}
为了使jwtService生效,需要更新AuthMoudle导入jwt相关依赖并配置jwtModule,如下:
首先创建auth/constants.ts文件,保存常量配置:
export const jwtConstants = {
secret: 'secretKey', // 加密密钥
expiresIn: '24h', // 24小时内过期
};
然后更新AuthMoudle:
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { PassportModule } from '@nestjs/passport';
import { LocalStrategy } from './strategies/local.strategy';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';
@Module({
imports: [
PassportModule,
JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: jwtConstants.expiresIn },
})
],
providers: [AuthService, LocalStrategy]
})
export class AuthModule { }
更新登陆接口,获取令牌并返回:
import { Post, Controller, Request, UseGuards} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from '../auth/auth.service';
@Controller('user')
export class UserController {
constructor(private authService: AuthService,) { }
@UseGuards(AuthGuard('local'))
@Post('login')
login(@Request() req): Record<string, any> {
// 生成令牌
let token = this.authService.getToken(req.user);
return { token };
}
}
到这里,就可以实现登陆接口成功后返回jwt令牌:
{ token: 'eyJhbGciOiJIUz1I1NiIbsInR5cjCI6IkpXVCJ9' }
当我们的接口携带jwt令牌访问,服务器该如何检测令牌的有效性呢?接下来,就需要定义jwt验证策略,用来检测我们的令牌。
在auth/strategies文件夹中创建jwt.strategy.ts,如下:
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { jwtConstants } from '../constants';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
// 如何提取令牌
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
// 是否忽略令牌过期
ignoreExpiration: false,
// 解析令牌的密钥
secretOrKey: jwtConstants.secret,
});
}
// 验证成功的毁掉函数
async validate(payload: any) {
return { ...payload };
}
}
在AuthModule中应用jwt策略:
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { PassportModule } from '@nestjs/passport';
import { LocalStrategy } from './strategies/local.strategy';
import { JwtStrategy } from './strategies/jwt.strategy';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';
@Module({
imports: [
PassportModule,
JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: jwtConstants.expiresIn },
})
],
providers: [AuthService, LocalStrategy, JwtStrategy]
})
export class AuthModule { }
到这里,jwt策略就配置好了。可以为需要验证令牌的接口配置守卫,应用jwt策略,举例如下:
import { Post, Get, Controller, Request, UseGuards} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from '../auth/auth.service';
@Controller('user')
export class UserController {
// ...
@UseGuards(AuthGuard('jwt'))
@Get('get')
getUser(@Request() req): Record<string, any> {
return req.user;
}
}
总结
以上内容官网均有涉及,浅浅记录,不值一提。如有错误,欢迎指正!!!