NestJS JWT MIDDLEWARE
MIDDLEWARE
まず、jwtフォルダにはjwtが含まれます.middleware.tsファイルを作成します.
ミドルウェアをクラスとして定義したり、関数として定義したりできます.
nestjsのミドルウェアはexpressと同じなのでnext()が必要です.
import { NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
//implements는 해당 클래스가 interface로 행동하도록 한다.
// export class JwtMiddleware implements NestMiddleware {
// use(req: Request, res: Response, next: NextFunction) {
// console.log(req.headers);
// next();
// }
// }
export function jwtMiddleware(req: Request, res: Response, next: NextFunction) {
console.log(req.headers);
next();
}
使用
Applying middleware
@Module()
Decoratorにはミドルウェアの位置がありません.モジュールクラスの configure()
メソッドを使用して設定します.ミドルウェアを含むモジュールは、次のとおりです. NestModule
インタフェースを実装する必要があります. AppModule
レベル#レベル# 設定jwtMiddleware
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { jwtMiddleware } from './jwt/jwt.middleware';
@Module({
...
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(jwtMiddleware)
.forRoutes({ path: '/graphql', method: RequestMethod.ALL });
}
}
ユーザがpathとして指定されたurlにリクエストを送信すると、ミドルウェアは正常に動作します.Global middleware
すべての登録済みパスにミドルウェアを一度にバインドするには
INestApplication
インスタンスが提供する use()
メソッドを使用できます.app.useは、パラメータとしての値がfunctionの場合にのみ使用できます.
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { jwtMiddleware } from './jwt/jwt.middleware';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
app.use(jwtMiddleware);
await app.listen(3000);
}
bootstrap();
jwtミドルウェアの作成
users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateAccountInput } from './dtos/create-account.dto';
import { LoginInput } from './dtos/login.dto';
import { User } from './entities/user.entity';
import { ConfigService } from '@nestjs/config';
import { JwtService } from 'src/jwt/jwt.service';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly users: Repository<User>,
private readonly config: ConfigService,
private readonly jwtService: JwtService,
) {}
//create User
...
//Login User
...
async findById(id: number): Promise<User> {
return this.users.findOne({ id });
}
}
まず,idでユーザを検索する方法を作成する.users.module.ts
ユーザー・サービスをエクスポートして、他の場所から依存性を注入します.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { UsesrResolver } from './users.resolver';
import { UsersService } from './users.service';
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UsesrResolver, UsersService],
exports: [UsersService],
})
export class UsersModule {}
jwt/jwt.service.tsユーザがサーバからトークンを受信と、 サーバにリクエストを送信する場合、request.タグを含む要求をHeaderに送信します.
その後、サーバは受信したトークンが有効かどうかを確認します.
jwt.verify()関数を使用してトークンの有効性をチェックできます.
jwt.verify()関数には、次のパラメータが含まれます.
最初のtoken:clientから受信したtoken
2番目のsecretkey:tokenの作成時に使用するsecretkey
import { Inject, Injectable } from '@nestjs/common';
import * as jwt from 'jsonwebtoken';
import { JwtModuleOptions } from './jwt.interfaces';
import { CONFIG_OPTIONS } from './jwt.constants';
@Injectable()
export class JwtService {
constructor(
@Inject(CONFIG_OPTIONS)
private readonly options: JwtModuleOptions,
) {
console.log(this.options);
}
sign(userId: number): string {
return jwt.sign({ id: userId }, this.options.privateKey);
}
verify(token: string) {
return jwt.verify(token, this.options.privateKey);
}
}
jwt/jwt.middleware.tsif(「x-jwt」in req.headers)でヘッダにクライアントから与えられたx-jwtがあるかどうかを確認すると、
jwtServiceのverifyメソッドにより、jwt.signに渡されるtoken値を取得できます.
コンソールに復号を書き込もうとすると、{id:3、iat:16362850}が得られます.
decodeにidという構成がある場合は、userServiceのfindByIdメソッドでユーザーを検索できます.
import { Injectable, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
import { UsersService } from 'src/users/users.service';
import { JwtService } from './jwt.service';
// implements는 해당 클래스가 interface로 행동하도록 한다.
@Injectable()
export class JwtMiddleware implements NestMiddleware {
constructor(
private readonly jwtService: JwtService,
private readonly userService: UsersService,
) {}
async use(req: Request, res: Response, next: NextFunction) {
if ('x-jwt' in req.headers) {
const token = req.headers['x-jwt'];
try {
const decoded = this.jwtService.verify(token.toString());
if (typeof decoded === 'object' && decoded.hasOwnProperty('id')) {
const user = await this.userService.findById(decoded['id']);
// 이 request는 HTTP request같은건데 이걸 graphql resolver에 전달해 줘야한다.
req['user'] = user;
}
} catch (e) {}
}
next();
}
}
req['user']=userこのrequestはHTTP requestと類似しており、graphsql解析器に渡す必要があります.req.user=userはreq[「user」]=userではなく、objキーを動的に使用する場合にobj[key]として使用されるためです.また、reqには元のユーザーというプロファイルがないため、タイプスクリプトにエラーが表示されます.
GraphQLModule context
GraphQLModule.forRootの場合はcontextを使用できます
contextがフォースとして定義されると、各リクエストが呼び出されます.
contextはexpressからreq属性を含むオブジェクトを受信する.
すなわち,ミドルウェアに渡されるreq['user']=userの値はcontextである.userに渡される値.
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { jwtMiddleware } from './jwt/jwt.middleware';
@Module({
...
GraphQLModule.forRoot({
autoSchemaFile: true, //메모리에 저장
//request context는 각 request에서 사용이 가능하다
//context기 힘수로 정의되면 매 request마다 호출된다.
//이것은 req property를 포함한 object를 express로부터 받는다.
context: ({ req }) => ({ user: req['user'] }),
}),
...
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(jwtMiddleware)
.forRoutes({ path: '/graphql', method: RequestMethod.ALL });
}
}
Guard
nestg mo authでauthモジュールを作成します.
auth/auth.guard.tsファイルの作成
canActivateは関数で、trueを返すと要求が続行され、falseの場合は要求が停止します.
要求されたコンテキストには、canActivate(コンテキスト:ExecutionContext)のコンテキストでアクセスできます.
しかし問題はcontextがhttpであることです.だからこれをGraphqlに変えます
GqlExecutionContext.create(context).graphqlコンテキスト値はgetContext()で取得できます.
user gisではreturn trueに設定してrequestを行い、userが存在しない場合は戻り値をfalseに設定してrequestを停止します.
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
// guard는 함수인데 request를 다음 단계로 진행할지 말지 결정한다.
// CanActivate는 함수인데 true를 리턴하면 request를 진핼시키고 false면 request를 멈추게한다
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context).getContext();
const user = gqlContext['user'];
if (!user) {
return false;
}
return true;
}
}
Guardの使用@UseGuardsのパラメータを使用すると、ミドルウェアによって作成されたAuthGuardを使用できます.
これで、Queryにmeを要求すると、トークン値がある場合は要求を続行し、ない場合は要求を停止します.
import { UseGuards } from '@nestjs/common';
import { Resolver, Query, Mutation, Args, Context } from '@nestjs/graphql';
import { AuthGuard } from 'src/auth/auth.guard';
import {
CreateAccountInput,
CreateAccountOutput,
} from './dtos/create-account.dto';
import { LoginInput, LoginOutput } from './dtos/login.dto';
import { User } from './entities/user.entity';
import { UsersService } from './users.service';
@Resolver((of) => User)
export class UsesrResolver {
constructor(private readonly usesrService: UsersService) {}
...
@Query((returns) => User)
@UseGuards(AuthGuard)
me() {}
}
AuthUser Decoratorの作成auth/auth-user.decorator.tsファイルを作成します.
createParamDecoratorには工場機能が必要です.
factory関数には、常に未知の値のデータとコンテキストが含まれます.
以前にGuardを作成したときに使用したコンテキストを使用してuserをロードして返します.
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
export const AuthUser = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const gqlContext = GqlExecutionContext.create(context).getContext();
const user = gqlContext['user'];
return user;
},
);
AuthUser Decoratorの使用私たちが作成したAuthUserは、関数のパラメータに入れて使用できます.
authUserが返す値はauthUserに入ります.
import { UseGuards } from '@nestjs/common';
import { Resolver, Query, Mutation, Args, Context } from '@nestjs/graphql';
import { AuthGuard } from 'src/auth/auth.guard';
import {
CreateAccountInput,
CreateAccountOutput,
} from './dtos/create-account.dto';
import { LoginInput, LoginOutput } from './dtos/login.dto';
import { User } from './entities/user.entity';
import { UsersService } from './users.service';
@Resolver((of) => User)
export class UsesrResolver {
constructor(private readonly usesrService: UsersService) {}
...
@Query((returns) => User)
@UseGuards(AuthGuard)
me(@AuthUser() authUser: User) {
//AuthUser에서 return한 값이 authUser에 들어간다.
console.log(authUser);
return authUser;
}
}
Reference
この問題について(NestJS JWT MIDDLEWARE), 我々は、より多くの情報をここで見つけました https://velog.io/@abc5259/NestJS-JWT-MIDDLEWAREテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol