NestJS 实现 JWT
前言
在使用NestJS的过程中,用到了JWT做登录鉴权,在这里记录一下这部分内容。
首先附上官网介绍:https://docs.nestjs.com/security/authentication
具体实现
先安装相关依赖
npm install --save @nestjs/passport passport passport-local passport-jwt
npm install --save-dev @types/passport-local
添加 auth 相关文件,这里面处理 token 的颁发和解析
nest g module auth
nest g service auth
创建 auth/jwt.strategy.ts
文件,这是 jwt 的策略文件。
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: process.env.SECRET,
});
}
async validate(payload: any) {
return payload
}
}
编辑 auth/auth.module.ts
文件
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt/dist';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
JwtModule.register({
secret: process.env.SECRET, // 这里是jwt的密钥
signOptions: { expiresIn: '60s' }
})
],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule { }
对应一些常量,我存储在了根目录下的 .env
文件中,推荐包括数据库在内等关键信息都存储在这里面。
使用 process 对象需要在 app.module.ts
中添加以下配置
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot(),
...
],
controllers: [AppController],
providers: [AppService],
})
编辑 auth/auth.service.ts
文件,这里我们只需要声明一个获取 token 的函数。
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) { }
public async getToken(payload) {
return this.jwtService.sign(payload, { secret: process.env.SECRET })
}
}
编辑user/user.module.ts
文件,引入 AuthModule
模块
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { AuthModule } from 'src/auth/auth.module';
@Module({
imports: [TypeOrmModule.forFeature([User]), AuthModule],
controllers: [UserController],
providers: [UserService],
exports: [UserService]
})
export class UserModule { }
编辑 user/user.service.ts
文件,当登录校验完成后,获取token返回给前端。
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { LoginUserDto } from './dto/login-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';
import { Result, stateCode } from '../config/ResultType';
import { comparePassword } from 'src/utils/bcrypt';
import { AuthService } from 'src/auth/auth.service';
@Injectable()
export class UserService {
constructor(@InjectRepository(User) private userRepository: Repository<User>, private authService: AuthService) { }
// 用户登录
async login(loginUserDto: LoginUserDto) {
const { user_account } = loginUserDto
const user = await this.userRepository.findOne({
select: ['user_password'],
where: {
user_account
}
})
if (user && comparePassword(loginUserDto.user_password, user.user_password)) {
// 重新获取用户信息
const res = await this.userRepository.findOne({ where: { user_account } })
const token = await this.authService.getToken({ ...res })
return Result.success({ ...res, token });
} else {
return Result.fail(stateCode.UNEXPECT, '用户名或密码错误')
}
}
// ...
async findOneByAccount(user_account: LoginUserDto['user_account']) {
return await this.userRepository.findOne({
select: ['user_id', 'user_password'],
where: { user_account }
});
}
async findOne(id: number) {
return await this.userRepository.findOne({ where: { user_id: id } });
}
}
编辑user/user.controller.ts
文件,进行 jwt 的校验
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Req } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { LoginUserDto } from './dto/login-user.dto';
import { AuthGuard } from '@nestjs/passport';
import { Result } from 'src/config/ResultType';
@Controller({ path: 'user', version: '1' })
export class UserController {
constructor(private readonly userService: UserService) { }
@Post('login')
login(@Body() loginUserDto: LoginUserDto) {
return this.userService.login(loginUserDto)
}
@UseGuards(AuthGuard('jwt'))
@Get('whoami')
async whoami(@Req() req) {
const res = await this.userService.findOne(+req.user.user_id) // 通过jwt中的user_id,获取用户信息
return Result.success(res)
}
}
这样,通过访问 http://localhost:3000/v1/user/login
,就可以获取到 token 了。
通过访问 http://localhost:3000/v1/user/whoami
,在 header 里面携带 Authorization:Bearer token
,就可以获取到登录用户的信息。
注意,这里的 Bearer 是必须要携带的,不然token会校验失败。
这里的 @UseGuards(AuthGuard('jwt'))
是对单个接口进行校验,如果整个模块都需要 jwt 校验的话,可以将这行代码放至@Controller()
上方,对整个模块的接口都进行校验。
总结
以上就是我对 NestJS 中使用 JWT 的一些个人总结。
下一篇:TypeORM 简单入门