身份认证
更新: 1/16/2025 字数: 0 字 时长: 0 分钟
JWT
本项目模板仅内置了基于JWT的身份认证方式,如有其他需要,请自行添加其他认证方式,参考文档。
本项目模板的JWT认证模块位于src/common/modules/auth目录:
src/common/modules/auth
├── auth.module.ts // 在 root.module.ts 中导入了该模块
├── auth.service.ts
└── strategies
└── jwt.strategy.ts文件说明:
auth.module.ts模块导入导出auth.service.tsauth模块的对外提供方法strategies策略目录jwt.strategy.tsjwt策略逻辑
策略逻辑
基于上一节的业务端点中的示例,本项目模板初始内置了两个端点大分类,即admin和app,根据它们的用户来划分:
admin提供给admin后台管理的接口app提供给前台应用程序的接口
由此产生一个需求:admin分类下的接口身份验证与app分类下的接口身份验证不能使用同一个策略,否则会产生发放给app的jwt令牌可以用来访问admin的接口的问题。
因此,在jwt.strategy.ts中设置了两个jwt策略:
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
/**当前JWT策略列表 */
export const JwtStrategys={
app:{
name:'app-jwt',
secretOrKeyPath:'jwt.app.secret',//这里填写的是configService获取配置信息的key
expiresInPath:'jwt.app.expiresIn',//这里填写的是configService获取配置信息的key
},
admin:{
name:'admin-jwt',
secretOrKeyPath:'jwt.admin.secret',//这里填写的是configService获取配置信息的key
expiresInPath:'jwt.admin.expiresIn',//这里填写的是configService获取配置信息的key
}
}
@Injectable()
export class AppJwtStrategy extends PassportStrategy(Strategy, JwtStrategys.app.name) {
constructor(private configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: configService.get(JwtStrategys.app.secretOrKeyPath),
});
}
async validate(payload: any) {
if (!payload) {
throw new UnauthorizedException('Invalid token payload');
}
return payload;
}
}
@Injectable()
export class AdminJwtStrategy extends PassportStrategy(Strategy, JwtStrategys.admin.name) {
constructor(private configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: configService.get(JwtStrategys.admin.secretOrKeyPath),
});
}
async validate(payload: any) {
if (!payload) {
throw new UnauthorizedException('Invalid token payload');
}
return payload;
}
}本质上就是策略名和secretKey不一样而已。那么,如何使用上面已经配置好的jwt策略呢?
按照Nest官方文档,简单来用的话,只需要在控制器或方法上添加装饰器@UseGuards(AuthGuard('app-jwt')),示例:
@UseGuards(AuthGuard('app-jwt')) //app-jwt即刚才声明的策略名
@Controller()
export class AppController {
//@UseGuards(AuthGuard('app-jwt')) 或者在方法级别添加装饰器
hello(){}
}提示
在本项目模板中,不建议这么使用,因为本项目模板中封装了统一的@Auth和@NoAuth装饰器,它们同时具备了RBAC权限认证的功能(后面章节会有详细说明)。
源码:src/common/decorators/auth.decorator.ts
@Auth(strategyName:string,checkRoles:boolean=false)strategyNamesting策略名称,也就是对应刚才我们声明的app-jwt和admin-jwtcheckRolesboolean是否同时检查权限,默认false。
@NoAuth()用于排除某个接口不需要认证。
示例:
//使用JwtStrategys.admin.name避免魔术字符串,JwtStrategys变量来自jwt.strategy.ts
@Auth(JwtStrategys.admin.name, true)
@Controller()
export class AppController {
hello(){} //AppController的方法,都需认证
@NoAuth() //标记了@NoAuth,hello2无需认证
hello2(){}
}发放凭证
一般在登录接口登录通过后发放jwt凭证:
- 通过注入位于
src/common/modules/auth/auth.module.ts的AuthModule来获得AuthService所提供的generateToken方法,用于发放jwt凭证。
// 使用 AuthService 生成 token
const { access_token } = this.authService.generateToken({
userId: user._id.toString(),
roles: user.roles,
customData: {
username: user.username
}
}, 'admin');提示
generateToken(payload: JWTUser, strategyName: keyof typeof JwtStrategys)
payload自定义的jwt信息对象,可根据需求修改strategyName策略key,JwtStrategys对象中的一级key字符串
获取凭证信息
前端拿到access_token之后,在请求头中携带Authorization: Bearer <access_token>,后端在controller的接口方法中通过@Req()装饰器获取req上下文,在req.user对象中就是解析后的payload:
@Auth(JwtStrategys.app.name)
@Controller()
export class AppController {
@Get('hello')
hello(@Req() req){
console.log(req.user)
}
}