From 49141c6566f3afd6141e1d556eece1058ce949f0 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Sun, 22 May 2022 23:40:22 +0800 Subject: [PATCH] feat: use cookie to authentication --- packages/server/package.json | 2 ++ .../server/src/controllers/user.controller.ts | 16 ++++++++++++--- .../server/src/controllers/wiki.controller.ts | 4 ++++ .../server/src/guard/document-auth.guard.ts | 8 +------- packages/server/src/guard/wiki-user.guard.ts | 9 +-------- packages/server/src/main.ts | 9 ++++++++- packages/server/src/modules/user.module.ts | 9 ++++++++- packages/server/src/services/user.service.ts | 4 ++-- .../src/transforms/http-response.transform.ts | 20 +++++++++---------- 9 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index 35ca533..04def60 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -36,6 +36,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.13.2", "compression": "^1.7.4", + "cookie-parser": "^1.4.6", "date-fns": "^2.28.0", "express": "^4.17.2", "express-rate-limit": "^6.2.0", @@ -64,6 +65,7 @@ "@nestjs/cli": "^8.0.0", "@nestjs/schematics": "^8.0.0", "@nestjs/testing": "^8.0.0", + "@types/cookie-parser": "^1.4.3", "@types/cron": "^2.0.0", "@types/express": "^4.17.13", "@types/jest": "27.0.2", diff --git a/packages/server/src/controllers/user.controller.ts b/packages/server/src/controllers/user.controller.ts index c39b1f5..9b7cdf3 100644 --- a/packages/server/src/controllers/user.controller.ts +++ b/packages/server/src/controllers/user.controller.ts @@ -12,10 +12,13 @@ import { Patch, Post, Request, + Res, UseGuards, UseInterceptors, } from '@nestjs/common'; import { UserService } from '@services/user.service'; +import { wrapResponse } from '@transforms/http-response.transform'; +import { Response as ExpressResponse } from 'express'; @Controller('user') export class UserController { @@ -31,9 +34,16 @@ export class UserController { @UseInterceptors(ClassSerializerInterceptor) @Post('login') @HttpCode(HttpStatus.OK) - async login(@Body() user: LoginUserDto) { - const res = await this.userService.login(user); - return res; + async login(@Body() user: LoginUserDto, @Res({ passthrough: true }) response: ExpressResponse) { + const { user: data, token } = await this.userService.login(user); + response.cookie('token', token, { httpOnly: true, sameSite: 'none', secure: true }); + return response.send(wrapResponse({ data: { ...data, token }, statusCode: HttpStatus.OK })); + } + + @Get('logout') + async logout(@Res({ passthrough: true }) response: ExpressResponse) { + response.cookie('token', '', { expires: new Date() }); + return; } @UseInterceptors(ClassSerializerInterceptor) diff --git a/packages/server/src/controllers/wiki.controller.ts b/packages/server/src/controllers/wiki.controller.ts index 8955fa9..feee4c0 100644 --- a/packages/server/src/controllers/wiki.controller.ts +++ b/packages/server/src/controllers/wiki.controller.ts @@ -253,6 +253,10 @@ export class WikiController { @UseGuards(WikiUserRoleGuard) @UseGuards(JwtGuard) async getWikiTocs(@Request() req, @Param('id') wikiId) { + const sleep = (v) => { + return new Promise((r) => setTimeout(r, v * 1000)); + }; + await sleep(4); return await this.wikiService.getWikiTocs(req.user, wikiId); } diff --git a/packages/server/src/guard/document-auth.guard.ts b/packages/server/src/guard/document-auth.guard.ts index 399eff4..c927883 100644 --- a/packages/server/src/guard/document-auth.guard.ts +++ b/packages/server/src/guard/document-auth.guard.ts @@ -23,13 +23,7 @@ export class DocumentAuthorityGuard implements CanActivate { } const request = context.switchToHttp().getRequest(); - - let token = request.headers.authorization; - - if (/Bearer/.test(token)) { - token = token.split(' ').pop(); - } - + const token = request?.cookies['token']; const user = this.jwtService.decode(token) as IUser; const { params, query, body } = request; const documentId = params?.id || params?.documentId || query?.id || query?.documentId || body?.documentId; diff --git a/packages/server/src/guard/wiki-user.guard.ts b/packages/server/src/guard/wiki-user.guard.ts index 1412425..83f7ef6 100644 --- a/packages/server/src/guard/wiki-user.guard.ts +++ b/packages/server/src/guard/wiki-user.guard.ts @@ -23,15 +23,8 @@ export class WikiUserRoleGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const targetUserRole = this.reflector.get(KEY, context.getHandler()); - const request = context.switchToHttp().getRequest(); - - let token = request.headers.authorization; - - if (/Bearer/.test(token)) { - token = token.split(' ').pop(); - } - + const token = request?.cookies['token']; const user = this.jwtService.decode(token) as IUser; if (!user) { diff --git a/packages/server/src/main.ts b/packages/server/src/main.ts index ea84695..c058476 100644 --- a/packages/server/src/main.ts +++ b/packages/server/src/main.ts @@ -5,6 +5,7 @@ import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@pipes/validation.pipe'; import { HttpResponseTransformInterceptor } from '@transforms/http-response.transform'; import * as compression from 'compression'; +import * as cookieParser from 'cookie-parser'; import * as express from 'express'; import helmet from 'helmet'; @@ -18,7 +19,13 @@ async function bootstrap() { const config = app.get(ConfigService); const port = config.get('server.port') || 5002; - app.enableCors(); + app.enableCors({ + // TODO: fixme + origin: 'http://localhost:5001', + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', + credentials: true, + }); + app.use(cookieParser()); app.use(compression()); app.use(helmet()); app.use(express.json()); diff --git a/packages/server/src/modules/user.module.ts b/packages/server/src/modules/user.module.ts index 9765e76..3ac5e6b 100644 --- a/packages/server/src/modules/user.module.ts +++ b/packages/server/src/modules/user.module.ts @@ -10,6 +10,7 @@ import { PassportModule, PassportStrategy } from '@nestjs/passport'; import { TypeOrmModule } from '@nestjs/typeorm'; import { UserService } from '@services/user.service'; import { getConfig } from '@think/config'; +import { Request as RequestType } from 'express'; import { ExtractJwt, Strategy } from 'passport-jwt'; const config = getConfig(); @@ -25,8 +26,14 @@ export class JwtStrategy extends PassportStrategy(Strategy) { private readonly userService: UserService ) { super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, secretOrKey: jwtConfig.secretkey, + jwtFromRequest: ExtractJwt.fromExtractors([ + (request: RequestType) => { + const token = request?.cookies['token']; + return token; + }, + ]), }); } diff --git a/packages/server/src/services/user.service.ts b/packages/server/src/services/user.service.ts index 6d70baf..809b041 100644 --- a/packages/server/src/services/user.service.ts +++ b/packages/server/src/services/user.service.ts @@ -111,7 +111,7 @@ export class UserService { * @param user * @returns */ - async login(user: LoginUserDto): Promise { + async login(user: LoginUserDto): Promise<{ user: OutUser; token: string }> { const { name, password } = user; const existUser = await this.userRepo.findOne({ where: { name } }); @@ -125,7 +125,7 @@ export class UserService { const res = instanceToPlain(existUser) as OutUser; const token = this.jwtService.sign(res); - return Object.assign(res, { token }); + return { user: res, token }; } async validateUser(user: UserEntity) { diff --git a/packages/server/src/transforms/http-response.transform.ts b/packages/server/src/transforms/http-response.transform.ts index 9042398..bb55292 100644 --- a/packages/server/src/transforms/http-response.transform.ts +++ b/packages/server/src/transforms/http-response.transform.ts @@ -6,6 +6,15 @@ interface Response { data: T; } +export function wrapResponse({ statusCode, data }) { + return { + statusCode, + message: null, + success: true, + data, + }; +} + @Injectable() export class HttpResponseTransformInterceptor implements NestInterceptor> { intercept(context: ExecutionContext, next: CallHandler): Observable> { @@ -13,17 +22,8 @@ export class HttpResponseTransformInterceptor implements NestInterceptor { const ctx = context.switchToHttp(); const response = ctx.getResponse(); - // const request = ctx.getRequest(); - // const url = request.originalUrl; const statusCode = response.statusCode; - const res = { - statusCode, - message: null, - success: true, - data, - }; - // console.info(url, res); - return res; + return wrapResponse({ data, statusCode }); }) ); }