mirror of https://github.com/fantasticit/think.git
feat: use cookie to authentication
parent
f1cb1012f2
commit
49141c6566
|
@ -36,6 +36,7 @@
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.13.2",
|
"class-validator": "^0.13.2",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
|
"cookie-parser": "^1.4.6",
|
||||||
"date-fns": "^2.28.0",
|
"date-fns": "^2.28.0",
|
||||||
"express": "^4.17.2",
|
"express": "^4.17.2",
|
||||||
"express-rate-limit": "^6.2.0",
|
"express-rate-limit": "^6.2.0",
|
||||||
|
@ -64,6 +65,7 @@
|
||||||
"@nestjs/cli": "^8.0.0",
|
"@nestjs/cli": "^8.0.0",
|
||||||
"@nestjs/schematics": "^8.0.0",
|
"@nestjs/schematics": "^8.0.0",
|
||||||
"@nestjs/testing": "^8.0.0",
|
"@nestjs/testing": "^8.0.0",
|
||||||
|
"@types/cookie-parser": "^1.4.3",
|
||||||
"@types/cron": "^2.0.0",
|
"@types/cron": "^2.0.0",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/jest": "27.0.2",
|
"@types/jest": "27.0.2",
|
||||||
|
|
|
@ -12,10 +12,13 @@ import {
|
||||||
Patch,
|
Patch,
|
||||||
Post,
|
Post,
|
||||||
Request,
|
Request,
|
||||||
|
Res,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
|
import { wrapResponse } from '@transforms/http-response.transform';
|
||||||
|
import { Response as ExpressResponse } from 'express';
|
||||||
|
|
||||||
@Controller('user')
|
@Controller('user')
|
||||||
export class UserController {
|
export class UserController {
|
||||||
|
@ -31,9 +34,16 @@ export class UserController {
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@Post('login')
|
@Post('login')
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
async login(@Body() user: LoginUserDto) {
|
async login(@Body() user: LoginUserDto, @Res({ passthrough: true }) response: ExpressResponse) {
|
||||||
const res = await this.userService.login(user);
|
const { user: data, token } = await this.userService.login(user);
|
||||||
return res;
|
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)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
|
|
|
@ -253,6 +253,10 @@ export class WikiController {
|
||||||
@UseGuards(WikiUserRoleGuard)
|
@UseGuards(WikiUserRoleGuard)
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async getWikiTocs(@Request() req, @Param('id') wikiId) {
|
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);
|
return await this.wikiService.getWikiTocs(req.user, wikiId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,7 @@ export class DocumentAuthorityGuard implements CanActivate {
|
||||||
}
|
}
|
||||||
|
|
||||||
const request = context.switchToHttp().getRequest();
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const token = request?.cookies['token'];
|
||||||
let token = request.headers.authorization;
|
|
||||||
|
|
||||||
if (/Bearer/.test(token)) {
|
|
||||||
token = token.split(' ').pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = this.jwtService.decode(token) as IUser;
|
const user = this.jwtService.decode(token) as IUser;
|
||||||
const { params, query, body } = request;
|
const { params, query, body } = request;
|
||||||
const documentId = params?.id || params?.documentId || query?.id || query?.documentId || body?.documentId;
|
const documentId = params?.id || params?.documentId || query?.id || query?.documentId || body?.documentId;
|
||||||
|
|
|
@ -23,15 +23,8 @@ export class WikiUserRoleGuard implements CanActivate {
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
const targetUserRole = this.reflector.get<WikiUserRole | null>(KEY, context.getHandler());
|
const targetUserRole = this.reflector.get<WikiUserRole | null>(KEY, context.getHandler());
|
||||||
|
|
||||||
const request = context.switchToHttp().getRequest();
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const token = request?.cookies['token'];
|
||||||
let token = request.headers.authorization;
|
|
||||||
|
|
||||||
if (/Bearer/.test(token)) {
|
|
||||||
token = token.split(' ').pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = this.jwtService.decode(token) as IUser;
|
const user = this.jwtService.decode(token) as IUser;
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { NestFactory } from '@nestjs/core';
|
||||||
import { ValidationPipe } from '@pipes/validation.pipe';
|
import { ValidationPipe } from '@pipes/validation.pipe';
|
||||||
import { HttpResponseTransformInterceptor } from '@transforms/http-response.transform';
|
import { HttpResponseTransformInterceptor } from '@transforms/http-response.transform';
|
||||||
import * as compression from 'compression';
|
import * as compression from 'compression';
|
||||||
|
import * as cookieParser from 'cookie-parser';
|
||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import helmet from 'helmet';
|
import helmet from 'helmet';
|
||||||
|
|
||||||
|
@ -18,7 +19,13 @@ async function bootstrap() {
|
||||||
const config = app.get(ConfigService);
|
const config = app.get(ConfigService);
|
||||||
const port = config.get('server.port') || 5002;
|
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(compression());
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { PassportModule, PassportStrategy } from '@nestjs/passport';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { getConfig } from '@think/config';
|
import { getConfig } from '@think/config';
|
||||||
|
import { Request as RequestType } from 'express';
|
||||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||||
|
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
|
@ -25,8 +26,14 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||||
private readonly userService: UserService
|
private readonly userService: UserService
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
ignoreExpiration: false,
|
||||||
secretOrKey: jwtConfig.secretkey,
|
secretOrKey: jwtConfig.secretkey,
|
||||||
|
jwtFromRequest: ExtractJwt.fromExtractors([
|
||||||
|
(request: RequestType) => {
|
||||||
|
const token = request?.cookies['token'];
|
||||||
|
return token;
|
||||||
|
},
|
||||||
|
]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ export class UserService {
|
||||||
* @param user
|
* @param user
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async login(user: LoginUserDto): Promise<OutUser & { token: string }> {
|
async login(user: LoginUserDto): Promise<{ user: OutUser; token: string }> {
|
||||||
const { name, password } = user;
|
const { name, password } = user;
|
||||||
const existUser = await this.userRepo.findOne({ where: { name } });
|
const existUser = await this.userRepo.findOne({ where: { name } });
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ export class UserService {
|
||||||
|
|
||||||
const res = instanceToPlain(existUser) as OutUser;
|
const res = instanceToPlain(existUser) as OutUser;
|
||||||
const token = this.jwtService.sign(res);
|
const token = this.jwtService.sign(res);
|
||||||
return Object.assign(res, { token });
|
return { user: res, token };
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateUser(user: UserEntity) {
|
async validateUser(user: UserEntity) {
|
||||||
|
|
|
@ -6,6 +6,15 @@ interface Response<T> {
|
||||||
data: T;
|
data: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function wrapResponse({ statusCode, data }) {
|
||||||
|
return {
|
||||||
|
statusCode,
|
||||||
|
message: null,
|
||||||
|
success: true,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HttpResponseTransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
|
export class HttpResponseTransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
|
||||||
intercept(context: ExecutionContext, next: CallHandler<T>): Observable<Response<T>> {
|
intercept(context: ExecutionContext, next: CallHandler<T>): Observable<Response<T>> {
|
||||||
|
@ -13,17 +22,8 @@ export class HttpResponseTransformInterceptor<T> implements NestInterceptor<T, R
|
||||||
map((data) => {
|
map((data) => {
|
||||||
const ctx = context.switchToHttp();
|
const ctx = context.switchToHttp();
|
||||||
const response = ctx.getResponse();
|
const response = ctx.getResponse();
|
||||||
// const request = ctx.getRequest();
|
|
||||||
// const url = request.originalUrl;
|
|
||||||
const statusCode = response.statusCode;
|
const statusCode = response.statusCode;
|
||||||
const res = {
|
return wrapResponse({ data, statusCode });
|
||||||
statusCode,
|
|
||||||
message: null,
|
|
||||||
success: true,
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
// console.info(url, res);
|
|
||||||
return res;
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue