创建项目
1、在命令行输入nest new jwt-login,jwt-login是项目名。回车键继续。
2、选择包管理工具,这里我选择npm,回车键继续
此时就开始安装依赖包了,这里要等一等
进入项目就会看到这样的目录
3、控制台输入npm run start:dev启动项目, 出现这样的情况就是启动成功了。
端口号默认是3000,在浏览器输入http://localhost:3000, 出现hello word就是启动成功了
实现登录功能
创建auth模块
在浏览器输入nest g res auth --no-spec
默认选择rest api 一直回车,此时目录出现了auth文件夹
数据传输对象(dto)
在auth/dto/中将创建login-auth.dto.ts,并添加如下代码:
ts
export class LoginAuthDto {
username: string;
password: string;
}
显然所谓的数据传输对象就是前端传递过来的参数对象,这里前端需要传递username和password,那么为什么要创建这个数据传输对象呢,继续往下看。
安装依赖包:npm i --save class-validator class-transformer,接着将login-auth.dto.ts中的代码修改如下:
ts
import { IsNotEmpty, Length } from 'class-validator';
export class LoginAuthDto {
@IsNotEmpty({ message: 'Username is required' })
username: string;
@IsNotEmpty({ message: 'Password is required' })
@Length(6, 20, { message: 'Password must be between 6 and 20 characters' })
password: string;
}
以上代码中增加了两个装饰器,很显然一个是@IsNotEmpty的装饰器是校验该项不能为空的,@Leng是来限制参数的长度的。这个dto对象该如何用呢,后面继续会讲到。
在main.ts中新增如下代码:
ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
以上新增的代码意思是全局启用管道验证,具体功效后面会讲到。
创建登录接口
在auth/auth.service.ts中将原用自动生成的函数删掉,新增login函数:
ts
import { Body, Controller, Post } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LoginAuthDto } from './dto/login-auth.dto';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('login')
login(@Body() loginAuthDto: LoginAuthDto) {
return loginAuthDto;
}
}
注意login函数中的参数使用了之前创建的LoginAuthDto对象,在这里可以对前端传来的参数进行校验。
在apifox里面进行测试:
username设置为空的情况下请求会返回我们之前定义的报错信息,如果把username补上去就是这样的:
可见已经跑通了。这就是dto的魅力所在,可以很简单的实现参数的校验。
jwt
配置jwt
一般来说正常的登录逻辑是前端传递过来username和password后端生成token返回给前端,前端之后在请求头里面带上token。
安装依赖包:npm install --save @nestjs/jwt。在app.module.ts中引入配置jwt:
ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import { JwtModule } from '@nestjs/jwt';
@Module({
imports: [
AuthModule,
JwtModule.register({
global: true,
secret: 'xiaosong',
signOptions: { expiresIn: '7d' },
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
global: 将JwtModule设置成全局模块,secret:密钥,用于JWT令牌的签名和验证,signOptions: 设置token的过期时间为7天。
生成token
在auht/auth.service.ts中增加login函数,并且将自动生成的代码删除掉:
ts
import { HttpException, Injectable } from '@nestjs/common';
import { LoginAuthDto } from './dto/login-auth.dto';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private readonly jwtService: JwtService) {}
login(loginAuthDto: LoginAuthDto) {
const user = {
id: 1,
username: 'xiaosong',
password: '123123',
};
if (
loginAuthDto.username !== user.username ||
loginAuthDto.password !== user.password
) {
throw new HttpException('用户名或密码错误', 401);
}
return {
token: this.jwtService.sign({
id: user.id,
username: user.username,
}),
};
}
}
以上代码看似很多其实很简单,user对象模拟的数据库,存储了用户名、密码、id,当username和password有一处不对的时候抛出异常否则就使用sign生成token,并且token里面携带的是id和username。注意constructor构造函数中是依赖注入,将jwtService这个对象注入进来。
在auth.controller.ts中的login函数中将代码修改一下:
ts
import { Body, Controller, Post } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LoginAuthDto } from './dto/login-auth.dto';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('login')
login(@Body() loginAuthDto: LoginAuthDto) {
return this.authService.login(loginAuthDto);
}
}
在apifox里测试一下,先试一试密码不正确的情况: 看到返回了报错信息
再次试一下都正确的情况:
ok,可以看到返回token了
token校验
创建守卫
生成token了但是不进行校验就是虚空的token,这里要创建一个守卫进行拦截
执行指令:nest g gu login 创建一个守卫,在login/login.gurad.ts中增加如下代码
ts
import {
CanActivate,
ExecutionContext,
HttpException,
Injectable,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { Request } from 'express';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class LoginGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request: Request = context.switchToHttp().getRequest();
const token = request.headers.authorization;
try {
this.jwtService.verify(token, {
secret: 'xiaosong',
});
} catch (error) {
throw new HttpException('token失效', 401);
}
return true;
}
}
以上代码看似十分的复杂,其实核心代码如下:
context.switchToHttp().getRequest();
:获取request对象request.headers.authorization;
:获取tokenthis.jwtService.verify(token, {secret: 'xiaosong',});
:token校验
使用守卫
在auth.controller.ts中随便加一个接口
以上代码的 @UseGuards(LoginGuard)
是使用创建的login守卫
在apifox里面测试一下:
可见报错了,守卫生效了
先登录获取token,在请求头加上token再试一下:
ok,请求通过了,可见我们的守卫也是十分的好用的。至此jwt机制的登录已经完成了。