From c08129624200370656674efc6884aec5cf15a750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E7=A7=8B=E6=97=AD?= Date: Thu, 16 Feb 2023 12:09:48 +0800 Subject: [PATCH] JwtAuthGuard --- package.json | 4 +++ pnpm-lock.yaml | 65 +++++++++++++++++++++++++++++++++- src/auth/auth.controller.ts | 10 +++++- src/auth/auth.module.ts | 3 +- src/auth/jwt-auth.guard.ts | 5 +++ src/auth/jwt.strategy.ts | 30 ++++++++++++++++ src/auth/models/token.model.ts | 8 +++++ 7 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 src/auth/jwt-auth.guard.ts create mode 100644 src/auth/jwt.strategy.ts diff --git a/package.json b/package.json index 46732eb..2c56314 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@nestjs/core": "^9.0.0", "@nestjs/jwt": "^10.0.2", "@nestjs/mapped-types": "^1.2.2", + "@nestjs/passport": "^9.0.3", "@nestjs/platform-express": "^9.0.0", "@nestjs/swagger": "^6.2.1", "@prisma/client": "^4.10.1", @@ -37,6 +38,8 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "nestjs-prisma": "^0.20.0", + "passport": "^0.6.0", + "passport-jwt": "^4.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0" }, @@ -46,6 +49,7 @@ "@types/bcrypt": "^5.0.0", "@types/express": "^4.17.13", "@types/node": "18.11.18", + "@types/passport-jwt": "^3.0.8", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab1c0e3..fce5fab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,7 @@ specifiers: '@nestjs/core': ^9.0.0 '@nestjs/jwt': ^10.0.2 '@nestjs/mapped-types': ^1.2.2 + '@nestjs/passport': ^9.0.3 '@nestjs/platform-express': ^9.0.0 '@nestjs/schematics': ^9.0.0 '@nestjs/swagger': ^6.2.1 @@ -14,6 +15,7 @@ specifiers: '@types/bcrypt': ^5.0.0 '@types/express': ^4.17.13 '@types/node': 18.11.18 + '@types/passport-jwt': ^3.0.8 '@typescript-eslint/eslint-plugin': ^5.0.0 '@typescript-eslint/parser': ^5.0.0 bcrypt: ^5.1.0 @@ -25,6 +27,8 @@ specifiers: husky: ^8.0.0 lint-staged: ^13.1.2 nestjs-prisma: ^0.20.0 + passport: ^0.6.0 + passport-jwt: ^4.0.1 prettier: ^2.3.2 prisma: ^4.10.1 reflect-metadata: ^0.1.13 @@ -42,6 +46,7 @@ dependencies: '@nestjs/core': 9.3.9_q6agyr4hwia55oswpsa7zjxcpm '@nestjs/jwt': 10.0.2_@nestjs+common@9.3.9 '@nestjs/mapped-types': 1.2.2_sm5si6oczf3vcbfl6qa276t67m + '@nestjs/passport': 9.0.3_v3ijvyhfbmibt6kvwmic262qla '@nestjs/platform-express': 9.3.9_77foi4w27ghy47yutmnzv7krjy '@nestjs/swagger': 6.2.1_ldzmua2hsw2tga2e42i6lmmdty '@prisma/client': 4.10.1_prisma@4.10.1 @@ -49,6 +54,8 @@ dependencies: class-transformer: 0.5.1 class-validator: 0.14.0 nestjs-prisma: 0.20.0_uhhmeuf5jto6tk72f36tv2cdfe + passport: 0.6.0 + passport-jwt: 4.0.1 reflect-metadata: 0.1.13 rxjs: 7.8.0 @@ -58,6 +65,7 @@ devDependencies: '@types/bcrypt': 5.0.0 '@types/express': 4.17.17 '@types/node': 18.11.18 + '@types/passport-jwt': 3.0.8 '@typescript-eslint/eslint-plugin': 5.52.0_6cfvjsbua5ptj65675bqcn6oza '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm eslint: 8.34.0 @@ -486,6 +494,16 @@ packages: reflect-metadata: 0.1.13 dev: false + /@nestjs/passport/9.0.3_v3ijvyhfbmibt6kvwmic262qla: + resolution: {integrity: sha512-HplSJaimEAz1IOZEu+pdJHHJhQyBOPAYWXYHfAPQvRqWtw4FJF1VXl1Qtk9dcXQX1eKytDtH+qBzNQc19GWNEg==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 + passport: ^0.4.0 || ^0.5.0 || ^0.6.0 + dependencies: + '@nestjs/common': 9.3.9_welcnyot5bzd5wa2aovbkxpi4i + passport: 0.6.0 + dev: false + /@nestjs/platform-express/9.3.9_77foi4w27ghy47yutmnzv7krjy: resolution: {integrity: sha512-f8ja2sYuDGj2QSMmjg05n3WF19wJG5yTiYxRi64nsu5GKL0qLM1LzxNemehkni/knExlvF2bDpbKKpna9nC1JA==} peerDependencies: @@ -706,7 +724,6 @@ packages: resolution: {integrity: sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==} dependencies: '@types/node': 18.11.18 - dev: false /@types/mime/3.0.1: resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} @@ -719,6 +736,27 @@ packages: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} dev: true + /@types/passport-jwt/3.0.8: + resolution: {integrity: sha512-VKJZDJUAHFhPHHYvxdqFcc5vlDht8Q2pL1/ePvKAgqRThDaCc84lSYOTQmnx3+JIkDlN+2KfhFhXIzlcVT+Pcw==} + dependencies: + '@types/express': 4.17.17 + '@types/jsonwebtoken': 9.0.1 + '@types/passport-strategy': 0.2.35 + dev: true + + /@types/passport-strategy/0.2.35: + resolution: {integrity: sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==} + dependencies: + '@types/express': 4.17.17 + '@types/passport': 1.0.12 + dev: true + + /@types/passport/1.0.12: + resolution: {integrity: sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==} + dependencies: + '@types/express': 4.17.17 + dev: true + /@types/qs/6.9.7: resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} dev: true @@ -2984,6 +3022,27 @@ packages: engines: {node: '>= 0.8'} dev: false + /passport-jwt/4.0.1: + resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==} + dependencies: + jsonwebtoken: 9.0.0 + passport-strategy: 1.0.0 + dev: false + + /passport-strategy/1.0.0: + resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==} + engines: {node: '>= 0.4.0'} + dev: false + + /passport/0.6.0: + resolution: {integrity: sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==} + engines: {node: '>= 0.4.0'} + dependencies: + passport-strategy: 1.0.0 + pause: 0.0.1 + utils-merge: 1.0.1 + dev: false + /path-exists/4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3020,6 +3079,10 @@ packages: engines: {node: '>=8'} dev: true + /pause/0.0.1: + resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} + dev: false + /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 7edd377..34ae9de 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -1,8 +1,9 @@ -import { Body, Controller, Post } from '@nestjs/common' +import { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common' import { AuthService } from './auth.service' import { CreateUserDto } from 'src/users/dto/create-user.dto' import { ApiTags } from '@nestjs/swagger' import { LoginInputDto } from './dto/login-input.dto' +import { JwtAuthGuard } from './jwt-auth.guard' @ApiTags('auth') @Controller() @@ -18,4 +19,11 @@ export class AuthController { async login(@Body() user: LoginInputDto) { return this.authService.login(user.email, user.password) } + + @UseGuards(JwtAuthGuard) + @Get('api/profile') + async getUserInfo(@Req() req: any) { + console.log(req) + return req.user + } } diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index ea8f787..2a81ada 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -3,10 +3,11 @@ import { AuthService } from './auth.service' import { AuthController } from './auth.controller' import { UsersModule } from 'src/users/users.module' import { JwtService } from '@nestjs/jwt' +import { JwtStrategy } from './jwt.strategy' @Module({ controllers: [AuthController], - providers: [AuthService, JwtService], + providers: [AuthService, JwtService, JwtStrategy], imports: [UsersModule], }) export class AuthModule {} diff --git a/src/auth/jwt-auth.guard.ts b/src/auth/jwt-auth.guard.ts new file mode 100644 index 0000000..aa859f7 --- /dev/null +++ b/src/auth/jwt-auth.guard.ts @@ -0,0 +1,5 @@ +import { Injectable } from '@nestjs/common' +import { AuthGuard } from '@nestjs/passport' + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt') {} diff --git a/src/auth/jwt.strategy.ts b/src/auth/jwt.strategy.ts new file mode 100644 index 0000000..25668c0 --- /dev/null +++ b/src/auth/jwt.strategy.ts @@ -0,0 +1,30 @@ +import { Strategy, ExtractJwt } from 'passport-jwt' +import { PassportStrategy } from '@nestjs/passport' +import { Injectable, UnauthorizedException } from '@nestjs/common' +import { ConfigService } from '@nestjs/config' +import { User } from '@prisma/client' +import { TokenPayload } from './models/token.model' +import { PrismaService } from 'nestjs-prisma' + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor( + private prismaService: PrismaService, + readonly configService: ConfigService, + ) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + secretOrKey: configService.get('JWT_ACCESS_SECRET', 'JWT_ACCESS_SECRET'), + }) + } + + async validate(payload: TokenPayload): Promise { + const user = await this.prismaService.user.findUnique({ + where: { id: payload.userId }, + }) + if (!user) { + throw new UnauthorizedException() + } + return user + } +} diff --git a/src/auth/models/token.model.ts b/src/auth/models/token.model.ts index 5f3fd81..22db965 100644 --- a/src/auth/models/token.model.ts +++ b/src/auth/models/token.model.ts @@ -7,3 +7,11 @@ export class Token { @ApiProperty() refreshToken: string } + +export class TokenPayload { + userId: string + /** Issued at */ + iat: number + /** Expiration time */ + exp: number +}