Nest.js:做个图书借阅系统(4) jwt验证

320 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情 

本系列文章将涉及到的技术有Nest.js、Typescript、Vite、Vue、Antdv、MySql。本人也是个菜鸡前端,初涉全栈,第一次写文,希望各位大大多多包涵,有什么建议可以提出来互相交流。


-【Passport】

passport.js是Nodejs中的一个做登录验证的中间件,可以与Express等Web框架无缝集成。

策略是passport中最重要的概念。passport模块本身不能做认证,所有的认证方法都以策略模式封装为插件,需要某种认证时安装对应策略即可。

策略模式是一种设计模式,它将算法和对象分离开来,通过加载不同的算法来实现不同的行为,适用于相关类的成员相同但行为不同的场景,比如在passport中,认证所需的字段都是用户名、邮箱、密码等,但认证方法是不同的。关于策略模式,本文不详细展开,想了解的推荐阅读Javascript中的策略模式,或者更广泛意义上的策略模式

我们这次就使用passport中的jwt策略来对接口进行验证。


-【Nest守卫】

守卫顾名思义,就像你进入办公大楼的时候,那个让你出示核酸码的保安一样,他必须确保你的核酸正常,你才能通行。 要了解Nest的守卫,你就得了解整个请求的处理过程。

从客户端发送一个请求,到服务器接收请求内容,触发绑定的函数并且执行相关逻辑完毕,然后返回内容给客户端的整个过程大体上要经过如下几个步骤:

发送请求->中间件->守卫->拦截器->管道->方法逻辑->拦截器->返回

中间件就像是一个洋葱一样不断的包裹再包裹,一层层调用,我们都知道在Express中,其实守卫,拦截器,管道都称作中间件,而Nest只不过把这些步骤给拆分出来细化了。我们的验证逻辑最适合就是放在守卫里去实现。


-【Jwt策略具体实现】

首先我们安装相关的包

$ yarn add --save @nestjs/passport passport 
$ yarn add @nestjs/jwt passport-jwt 
$ yarn add @types/passport-jwt --save-dev

在根目录添加guardsstrategies目录和对应的文件

guards/jwt-auth.guard.ts

// guards/jwt-auth.guard.ts

import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

strategies/jwt.strategy.ts

// strategies/jwt.strategy.ts  jwt策略配置

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { jwtConstants } from './constants';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: jwtConstants.secret,
    });
  }
  
  // 该方法参数表示通过守卫后解析token得到的内容,返回值将传入控制器方法参数
  async validate(payload: any) {
    return {
      id: payload.id,
      username: payload.username,
      name: payload.name,
      companyId: payload.companyId,
    };
  }
}

strategies/constants.ts

export const jwtConstants = {
  secret: 'booklend123', // jwt生成的密钥可以随便定
};

然后我们修改对应的逻辑,这一步就是在登录成功后生成token

// 只显示修改部分

...
constructor(
    private readonly authService: AuthService,
    private readonly jwtService: JwtService, 
  ) {} 
// 要在这里注入对象,就必须在Auth模块中注册

...
@Get('checkUser')
  async checkUser(@Request() req: any) {
    console.log(req.query);
    const { username, password } = req.query;
    const result: any = await this.authService.checkUser({
      username,
    });
    const isOk = bcryptjs.compareSync(password, result.password);
    if (!isOk) {
      throw new HttpException('用户验证失败', HttpStatus.UNAUTHORIZED);
    }

    return {
      // 表示最终需要把哪些字段值存入token
      access_token: this.jwtService.sign({
        id: result.id,
        username: result.username,
        name: result.name,
        companyId: result.companyId,
      }),
    };
  }

最后一定不能忘记注册到模块中去,因为我整个项目都会使用,所以我直接注册到根模块中,

// 只显示更改部分

import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { jwtConstants } from './strategies/constants';
import { JwtStrategy } from './strategies/jwt.strategy';

@Module({
  imports: [
    ...
    PassportModule,
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '60s' },
    }),
    ...
  ],
  controllers: [AppController],
  providers: [AppService, JwtStrategy],
})
export class AppModule {}

接下来修改一下前端的代码,登录后保存token,再加上一个测试的接口,

message.success('登录成功')
// 登录后存储access_token
localStorage.setItem('access_token', r.data.access_token)  

请求的时候headers请务必带上Authorization: Bearer token

let token = localStorage.getItem('access_token')  
  axios.get('http://localhost:3000/lend/test',{
  headers: {
	Authorization: `Bearer ${token}`
  }
})

这样就完成了jwt认证的功能,后面只需要在适当的地方开启守卫就好了,可以直接加在控制器上,或者全局,根据需求来吧。

@UseGuards(AuthGuard('jwt'))

到这里可以通过守卫验证了,但是我突然发现要修改守卫返回的提示却无从下手,有知道的小伙伴可以分享一下经验吗?

下一篇我们去实现一些业务代码。

这部分完整代码可以看我的github地址:点这里,点这里,点这里