- 使用 nest g res auth去生成restful风格的auth模块,下面是具体操作
nest g res auth
安装基础依赖
{
"name": "auth",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.1.0",
"@nestjs/mapped-types": "*",
"@nestjs/passport": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/typeorm": "^10.0.0",
"bcryptjs": "^2.4.3",
"mysql2": "^3.6.0",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"typeorm": "^0.3.17"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/bcryptjs": "^2.4.2",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/passport-jwt": "^3.0.9",
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.59.11",
"@typescript-eslint/parser": "^5.59.11",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^29.5.0",
"prettier": "^2.8.8",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
- 执行命令 yarn install 安装 依赖
配置 基础信息
在app.modules 中去配置数据库连接信息
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { APP_GUARD } from '@nestjs/core';
import { JwtAuthGuard } from './auth/jwt-auth.grard';
@Module({
imports: [AuthModule, TypeOrmModule.forRoot({
type: 'mysql', // 数据库类型
host: 'localhost', // 主机名
port: 3306, // 端口
username: 'root', // 用户名
password: 'root', // 密码
database: 'auth', // 数据库名称
synchronize: true,
retryDelay: 500, //重试连接数据库间隔
retryAttempts: 10,//重试连接数据库的次数
autoLoadEntities: true, //如果为true,将自动加载实体 forFeature()方法注册的每个实体都将自动添加到配置对象的实体数组中
})],
controllers: [AppController],
// 注册为全局守卫
providers: [AppService, {
provide: APP_GUARD,
useClass: JwtAuthGuard
}],
})
export class AppModule { }
接着在 auth.entity.ts文件中去写关于用户表的配置,我这里就做三个字段id,username,password
- 定义密钥有效时间
export const jwtConstants = {
secret: "leeKey", // 密钥
expiresIn: "60s" // token有效时间
}
- 配置AUTH 的 策略
import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { jwtConstants } from "./constants";
interface JwtPayload {
username: string
}
@Injectable()
// 验证请求头中的token
export class JwtAuthStrategy extends PassportStrategy(Strategy, "jwt") {
constructor() {
super(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: jwtConstants.secret
}
)
}
async validate(payload: JwtPayload) {
console.log(payload.username);
const { username } = payload
return {
username
}
}
}
- 在auth.modules 完成jwt 配置 引入密钥配置
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { NV_Users } from './entities/auth.entity';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from "./constants"
import { JwtAuthStrategy } from "./jwt-auth.strategy"
@Module({
imports: [TypeOrmModule.forFeature([NV_Users]), JwtModule.register({
secret: jwtConstants.secret,
signOptions: { expiresIn: jwtConstants.expiresIn }
})],
controllers: [AuthController],
providers: [AuthService, JwtAuthStrategy]
})
export class AuthModule { }
- 创建表
CREATE TABLE NV_Users (
id int NOT NULL AUTO_INCREMENT,
username varchar(255) NOT NULL,
password varchar(255) NOT NULL,
PRIMARY KEY (id)
);
- 配置实体类
import { Column, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class NV_Users {
@PrimaryGeneratedColumn()
id:number
@Column()
username:string
@Column()
password:string
}
- 业务层完成基础的注册逻辑
import { BadRequestException, Injectable } from '@nestjs/common';
import { CreateAuthDto } from './dto/create-auth.dto';
import { NV_Users } from './entities/auth.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import * as bcryptjs from "bcryptjs"
import { JwtService } from "@nestjs/jwt"
@Injectable()
export class AuthService {
constructor(
@InjectRepository(NV_Users) private readonly user: Repository<NV_Users>,
private readonly JwtService: JwtService
) { }
// 注册
async signup(signupData: CreateAuthDto) {
const findUser = await this.user.findOne({
where: { username: signupData.username }
})
if (findUser && findUser.username === signupData.username) return "用户已存在"
// 对密码进行加密处理
signupData.password = bcryptjs.hashSync(signupData.password, 10)
await this.user.save(signupData)
return "注册成功"
}
// 登录
async login(loginData: CreateAuthDto) {
const findUser = await this.user.findOne({
where: { username: loginData.username }
})
// 没有找到
if (!findUser) return new BadRequestException("用户不存在")
// 找到了对比密码
const compareRes: boolean = bcryptjs.compareSync(loginData.password, findUser.password)
// 密码不正确
if (!compareRes) return new BadRequestException("密码不正确")
const payload = { username: findUser.username }
return {
access_token: this.JwtService.sign(payload),
msg: "登录成功"
}
}
}
- 定义请求的dto
export class CreateAuthDto {
username: string
password: string
}
- 通过注解进行控制 是否需要授权
import { SetMetadata } from "@nestjs/common";
export const IS_PUBLIC_KEY = 'isPublic'
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
- 请求层
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
import { CreateAuthDto } from './dto/create-auth.dto';
import { Public } from 'src/common/public.decorator';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) { }
// 注册
// @Public()
@Post("/signup")
signup(@Body() signupData: CreateAuthDto) {
return this.authService.signup(signupData)
}
// 登录
@Public()
@Post("/login")
login(@Body() loginData: CreateAuthDto) {
return this.authService.login(loginData)
}
}
- 配置拦截守卫
import { ExecutionContext, Injectable } from "@nestjs/common";
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { Observable } from "rxjs";
import { IS_PUBLIC_KEY } from "src/common/public.decorator";
@Injectable()
export class JwtAuthGuard extends AuthGuard("jwt") {
constructor(private reflector: Reflector) {
super()
}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass()
])
console.log(isPublic, "isPublic");
if (isPublic) return true
return super.canActivate(context)
}
}
测试
- 获取token