@Query(() => Product)
fetchProduct(
@Args('productId') productId: string, //
) {
return this.productService.findOne({ productId });
}
"productId"인수는 "사용자"가 GraphQL API에서 직접 입력하는 부분입니다
-----------------------------------------------------------------------------
const profile = {
name: "철수",
age: 10,
school: "다람쥐초등학교"
name: "영희"
}
객체에서 key가 중복되면 value가 덮어쓰기가 된다.
console.log(profile)
{ name:'영희', age: 10, school:'다람쥐초등학교' }
------------------------------------------------------------------------------
stateful:상태를 가지고 있다
stateles:상태를 가지고 있지 않다.
백엔드서버에 있는 세션정보를 DB로 옮긴다.
백엔드는 stateless가 된다.
DB에 과부하 →파티셔닝
DB파티셔닝
수직 파티셔닝, 수평 파티셔닝
세션 id == DB에 저장되는 id == 토큰 id
메모리를 활용한 데이터를 저장하는 방법:
메모리 - 컴퓨터 컸다가 키면, 데이터가 지워짐, cpu의 RAM접근속도가 빠르다. - ex)Redis, Memcache
참고 → 디스크 - 데이터가 영구적으로 저장됨, cpu의 디스크접근속도가 느리다.
-------------------------------------------------------------------------------------------------
JWT의 구조
Header(Http Header가 아님에 주의한다)
토큰에 대한 메타 데이터를 포함하고 있다.
(타입, 서명알고리즘, RSA...)
Payload
유저 정보, 만료기간, 주제 등
Verify Signature
JWT의 마지막 세그먼트는 토큰이 보낸 사람에 의해 서명되었는지, 다른 방식으로 변경되지 않았는지
확인하는데 사용되는 서명이다.
JWT 생성을 위한 모듈 4가지
@nestjs/jwt
-nestjs에서 jwt를 사용하기 위해 필요한 모듈
@nestjs/passport
-nestjs에서 passport를 사용하기 위해 필요한 모듈
passport
-passport 모듈
passport-jwt
-jwt 모듈
-------------------------------------------------------------------------------------------------------
JWT사용 흐름
유저로그인 → 벡엔드 서버의 로그인 API에서 토큰 생성(Payload + Secret Text) → 백엔드 서버에 토큰 보관
JWT(AccessToken)활용한 로그인 데이터 저장
→JWT토큰을 (로그인)접근용도로 사용한다면 JWT를 AccessToken이라고 부르는 것이며,
→DB에 저장되는 id도 (로그인)접근용도로 사용한다면 AccessToken이라고 부른다.
→따라서 어떠한 토큰이 접근용도로 사용될 때 해당 토큰을 AccessToken이라고 말한다.
{ email: aaa@naver.com
isLogin: true
}
------------------------------------------------------------------------------
1. AccessToken을 받아오는 과정-----→Authentication(인증)
로그인정보가 백엔드 서버에 들어옴 로그인 정보가 맞는지 DB에 가서 확인한 후 맞으면 →로그인API에서 Json객체 전체를 암호화 → 토큰생성: 특수한 문자열형태(AccessToken)
→ 벡엔드 서버에서 토큰(JWT)을 브라우저(사용자)에게 전달, 브라우저에 저장
→ 벡엔드 서버는 Secret Key(Text)를 보관하고 있다.
2. 토큰에 근거해서 해당사용자가 맞는지 판단하는 과정-----→Authorization
사용자가 로그인하는 경우 request객체의 Http Header에 AccessToken(특수한 문자열)을 넣어서 로그인API로 전달
→그러면 클라이언트에서 온 JWT의 header와 클라이언트에서 온 JWT의 payload를 가져온 다음
→서버에 보관되어 있는 secret Key로 Verify Signature를 만든 다음
→클라이언트에서 온 JWT의 Verify Signature와 서버가 방금 전에 만든 Verify Signature를 비교하여 같으면
→해당 사용자를 정당한 사용자로 판단하며
→정당한 사용자인 경우 DB에 가서 정상처리 후 브라우저(사용자)에게 response를 날림
→정당한 사용자가 아닌 경우 브라우저에게 "에러"를 던짐,
인가(Authorization)
토큰을 복호화해서 해당 사용자가 맞는지 검증하기 - ex)유저조회(fetchUser)는 로그인 사용자만 접근가능
프론트엔드에서 벡엔드로 데이터가 패킷형태로 전달됨 → 해커에 의해서 토큰이 탈취 될 가능성이 있다.
→따라서 http의 요청헤더(Request Headers)의 Authorization(키)에 "토큰(value)을 삽입"하여 벡엔드로 전달
→"Authorization" : "Bearer + 토큰"
→Bearer: 관례
벡엔드는 해당 토큰을 복호화해서 철수 인지 확인
AccessToken과 Bearer인증
인가(Authorization)의 흐름도
User가 로그인을 하면, 벡엔드 서버로 부터 토큰(JWT)을 받고,
다시 User가 Bearer방식으로 Http Header에 JWT을 넣어서 fetchUser API를 요청하면
@UserGuards에서 검문을 한다.★★
--------------------------------------------------------------------------------------------
@UseGuards(GqlAuthAccessGuard) //검문소
@Query(() => String)
fetchUser(
@CurrentUser() currentUser: any, //
) {
console.log('fetchUser 실행완료!!!');
console.log('유저정보는?', currentUser); //validate() 함수에서 리턴한 유저정보
return 'qqq';
}
}
---------------------------------------------------------------------------------------------
그러면 GqlAuthAccessGuard요청이 RestAPI요청으로 바뀌면서 myGuard를 찾고,
---------------------------------------------------------------------------------------------
//graphql API를 구현하는 경우 graphql API요청을 RestAPI 요청으로 바꾸기 위한 별도조치
import { ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';
export class GqlAuthAccessGuard extends AuthGuard('myGuard') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
} //오버라이딩
}
----------------------------------------------------------------------------------------------------------
//Request Header에 Bearer를 넣어주는 방식
//user.resolver.ts의 myGuard를 만들기 위한 전체로직
//jwt-access.strategy.ts
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, ExtractJwt } from 'passport-jwt';
export class JwtAccessStrategy extends PassportStrategy(Strategy, 'myGuard') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), //req.headers.Authorization...
secretOrKey: 'myAccessKey', //서명 //Docs에 나와있는 양식임. 외울필요X
});
}
//인가에 실패하는 경우 에러발생
//인가에 성공하는 경우 validate()실행(오버라이딩)
validate(payload) {
console.log(payload); // {email:c@c.com, sub: sdjhfsdkf-012093sd}
return {
email: payload.email,
id: payload.sub,
};
}
}
validate() 메서드에서 return 할 때 req객체에 user객체에 email, id를 넣어준다.★ →req.user
-------------------------------------------------------------------------------------------------------------------
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
//req.user에서 user정보를 받기 위한 나의 데코레이터 만들기 ---- @CurrentUser(일종의 함수)
//데코레이터==함수 만들기
export const CurrentUser = createParamDecorator(
(data, context: ExecutionContext) => {
const ctx = GqlExecutionContext.create(context); //graphqlAPI요청을 RestAPI요청으로 바꾸기 위한 작업
return ctx.getContext().req.user; //context에서 getContext를 한 다음, req정보에서 user정보만 뽑아와줘
},
);
----------------------------------------------------------------------------------------------------------------------
@UseGuards(GqlAuthAccessGuard) //검문소
@Query(() => String)
fetchUser(
@CurrentUser() currentUser: any, //
) {
console.log('fetchUser 실행완료!!!');
console.log('유저정보는?', currentUser); //validate() 함수에서 리턴한 유저정보
return 'qqq';
}
}
-----------------------------------------------------------------------------------------------------------------------
'고농축 백엔드' 카테고리의 다른 글
고농축 벡엔드 13 - Google 로그인 (0) | 2023.02.14 |
---|---|
고농축 벡엔드 12 - RefreshToken + Cookie (0) | 2023.02.13 |
고농축 백엔드 docker-bind★★ (0) | 2023.01.31 |
고농축 벡엔드 10 - TypeScript (0) | 2023.01.30 |
고농축 백엔드 9 - 객체, 약한결합, 강한결합, 의존성, 의존성주입 (0) | 2023.01.25 |