Wecode 부트캠프 -Foundation 2/인증, 인가- 개념

Foundation 2- [jwt, token] 토큰 개념, 토큰 코드 만들기, 토큰 발행 / 프론트와 연결하는 법 (엔드포인트, api)

JBS 12 2023. 9. 9. 15:59

간단한 전반적 흐름

 

로그인 + token 발행 [개념]

이후 공부한 자세한 내용 [jwt, token] 토큰 개념, 토큰 코드 만들기, 토큰 발행 / 프론트와 연결하는 법 (엔드포인트, api) 토큰? 백엔드가 매번 프론트에게 사용자의 로그인 기록을 주는데, 비밀번호

pm-developer-justdoit.tistory.com

token 에 담긴 id가 

user table의 pk인 userId


토큰?

 백엔드가 매번 프론트에게 사용자의 로그인 기록을 주는데, 비밀번호를 암호화해서 줌 --> 로그인 기록 유지 하는 기능 (다른 거 하는 동안) 로그인 성공한 시점에 토큰을 발급.

 

그 토큰의 이름이 jsonwebtoken해서 jwt 

 

jwt

토큰을 암호화 

 

 

'JWT 토큰' 구성요소
- Header에는 암호화 알고리즘 'alg' 정보와 해당 토큰의 타입 'type'이 들어있다.
- PayLoad에는 토큰을 생성할 당시에 집어넣은 문자열 or 객체 or Buffer 정보가 들어있다. (id, name, email, password) 
Signature에는 개인 키로 서명한 전자서명

header에 알고리즘과 토큰 type: jwt

토큰을 주고 나면 프론트가 또 언제 쓰느냐? 

 

토큰은 '내가 로그인 했었어' 알려주는거, 언제?

로그인에 대한 증거니까, 로그인할때가 아니라,

--> 다른 api에서 사용할 때. 결제/게시물 작성 등ㅎ  (로그인이 필요한 시점, 누군지 알아야 하는 시점 = 거의 모든 apI 호출할 때) 

 


 

토큰은 

- 내가 로그인 했어

- 내가 누구야 

 

이  2개를 동시에 알려주는 것. 

 

토큰 안에 사용자에 대한 정보가 들어가 있어야 함 .ex 이름,아이디

 

 

백엔드가 토큰을 프론트 에게 알려주고,

사용자 정보를 집어 넣어서 

 

 

 

토큰 안에 사용자 정보를 넣는 사람은?

백엔드 

 

언제?

로그인을 성공할 때, 토큰 만들 때 누가 로그인했는지를 넣어준다. 

 

 

로그인 한다는 것은,

1. 비밀번호 / 아이디 있다는 거 확인하고,

2. 사용자의  DB의 이메일, 아이디을 토큰으로 만들어서

알 수 없게 암호화로 감싸서 프론트에게 주는 것

 

(프론트는 이게 누구꺼인지 알 수 없음. 감싼 상태로 받아서, 로그인했구나만 아는거, 누가 결제했어 도 알 수 없음, 

백엔드는 토큰을 깔 때에만 안다)

 

 

 

토큰을 발행해야 하는 코드, 토큰을 만들어 낼 위치 

일단 패스워드 통과하고 나서 

로그인 후에 토큰을 발행하니 1-4번은 아니고

5,6번 중에인데,

return이 되고 나고는, 뒤에 코드는 실행이 되지 않기에, 

return 뒤에 있는 6번이 아니라, 

5번에 넣어야함. 

 

 

return은 함수의 끝! 

 

5번에서 토큰을 만들고,

6번에서 프론트에게 토큰을 주는, 토큰을 발행! 


순서 2. 토큰 발행 , 프론트에게 토큰 준다 

이제 return에 메세지를 보내면서,

"accessToken" : token 

response body에 담아서, response보낼 때 토큰을 담아서 준다

 


순서1. 토큰 만들기 

1. 토큰 만들어주는 library 사용 --> json web token 만들어주는 library가 또 있으니까

토큰 안에 데이터베이스 id 만 담는다. 

토큰 안에 패스워드는 담지 않음. 텍스트가 암호를 빡세게 하지 않아서, 해커가 string 갖고 가서 까면 볼 수 있음. 

나중에 누군지 알려고 내가 누구야 하고 토큰을 까보는 사람이 누구? 백엔드. 우리니까. 

우리만 알면 되기 때문에 , 우리가 user를 식별 할 수 있으면 됨. --> 가장 확실한게 데이터베이스 id 이기에 데이터베이스 id를 넣는다. 

다른 개발자가 아닌, 이 데이터베이스 쓰는 우리 서버 개발자가 볼 거니까. 

 

2. 그래서 토큰 안에 데이터베이스 id 넣어줄 것 (형식  {"id":10}  --> 만약 데이터베이스 10번 유저다 하면) 

+ 옵션으로 1시간짜리 이다

 

3. signature 

우리 회사에서 발급한 것이라는 시그니처의 

우리 회사꺼만 우리가 열어볼 수

다른 회사에서 우리꺼 까보면 안 되니. 

 


jwt.io

왼쪽이 토큰

 

토큰을 까보면, = incoding했던 것을 decoding

오른쪽에 decoded에 

 

payload에 

객체로 

"이름이 John Doe,

iat는 유효한 시간 (millisecond로 되어있을 것)  

 

 

만약 여기에서 

오른쪽에서 name을 바꾸면

왼쪽에서 토큰도 같이 바뀜

 

 

이제 만들어보자, jsonwebtoken으로! 

npm에 jsonwebtoken이라는 설치하는 라이브러리가 있음 (express같은 framwork가 아닌 라이브러리)

 

jsonwebtoken 이 뭐냐면, 함수 라이브러리를 여러 개 만들어서 우리에게 줌

 

 

json webtoken 함수를 보면 ,

 

1) jwt.sign()

토큰을 만들어주는 함수

npmjson.com/package/jsonwebtoken

첫번째 인자로 데이터 뭐 넣을 건지 

 

첫번째 인자: 뭘 암호화할지 

- exp: 만료 되는 시간 (지금 시간을 초로 나눠서 _ 밀리 세컨드 단위라 초로 환산) 

- data: 데이터베이스의 id

 

두번째 인자: signature 

- 우리 회사만의 고유한 문자열들을 넣어서, 

나중에 토큰 열 때에도 이 문자열 넣어야 동작  

 

Express 안에서 JWT 사용

1. npm install jsonwebtoken --save 설치

 

2. const jwt = require('jsonwebtoken'); 

jsonwebtoken을 jwt라는 변수에 담는다

 

- jwt 안에 보면 6개의 함수가 한번에 묶여있음.

- jwt 라이브러리 만든 개발자들이, 함수 6개를 미리 만들어놓고, 개발자분들 쓰세요 하고 npm에 업로드 해놓은 거.

그거를 우린 npm install로 다운 받아서 쓰는 거고. 

- npm install로 설치할 수 있는 건 개발자들이 미리 만들어서 올린 거. (일반 개발자들도 만들어서 업로드 가능) 

 

[jwt 안에 들어있는 6개의 함수]

  • decode: 토큰을 푸는, 디코딩하는 함수
  • verify: 나중에 결제할 때 등, 프론트가 토큰 주면 우리꺼 맞는 지 확인하는 
  • sign: jwt 만드는 함수 
  • JsonWebTokenError: 에러 - 토큰 자체가 이상할 때, 토큰이 안 맞아 , 이거 우리가 만든 거 아닌거 같은데 
  • NotBeforeError: 에러 - 공식문서 참고
  • TokenExpiredError: 에러 - 토큰 시간 만료 될 때 (1시간짜리 토큰인데 2시간에도 쓰려할떄)

 

 

토큰을 만들자 = sign 함수 이용

npmjs.com/package/jsonwebtokem

jwt.sign 함수에 

- 첫번째 인자에, 어떤 걸 넣고 싶은지 쓰고 (객체)

- 두번째 인자에, secret key (문자열) 넣어줌 'shhhh' 

 

 

const에 token 변수 만들고,  const token

jwt 객체 안에 있는 sign함수 쓴다  jwt.sign( ) 

  • 첫번째 인자에는 객체로 데이터베이스 아이디 써주고, 
  • 두번째 인자에는 랜덤한 문자열로 써주고. 

 

 

데이터베이스의 id를 넣어줘보자

 

만약, database id가 10인 사람이라면 

{id:10} 

 

console로 한번 찍어보면, (우리가 보기 위해서)

토큰이 나온다. 


잠깐, 볼 수 있는 Decoding 복호화 과정 

 

이 토큰을 아까 그 페이지로 가서 

왼쪽에 있는 토큰에 

 

우리가 방금 출력한 토큰을 붙여넣기 하면, 

 

우리가 넣은 토큰에 해당되는 사용자의 id:10 가 나온다.  ==> decoding 하는 과정 (토큰을 까서, 누구꺼인지 db id 나온다)

이렇게 토큰 넣으면 id 다 나오기에, Password를 토큰에 넣으면 안 된다는 것.

토큰은 로그인 시점에 나오고, 

이 토큰을 프론트엔드에게 준다. 

 

이제 vscode에 넣어보자

1. require을 해줘야 함. const jwt라는 이름으로, jsonwebtoken을 받아오겠다 -- >app.js에서 쓰겠다. 

const jwt =require('jsonwebtoken')

2. 로그인 함수로 가서, (token은 로그인 시 발급 되니까) 

token을 만들 자리 (패스워드 일치 확인 후/ return 전) 에

const token 은 jwt 가 만든다 (jwt.sign) 

id 넣고, signature 넣고 

 

 

 

이 id는

0. request에서 받아서 가져오는 거 (근데 request에서는 email, password만 주고, database id를 주진 않음)

프론트 입장에서 post, 백엔드는 받아오지// postman의 request의 body에서 한다 = req.body // req.body 객체 형태의 email을 본다 = req.body.email

1. 이 이메일로 database에 있는지 확인 후

2. 데이터베이스에 있다 -> 비밀번호도 확인 후 있는지

3. 그 후에, 그 사람의 데이터베이스 id를 넣어줘야 함. --> user.id (user테이블의 id) 

4. id 자리에  변수로 들어감

    ex. userInfo.id 

id를 그대로 넣으면 안 됨. 10으로 하드코딩하면 안되고 변수로 해야 함. 

로그인할 때마다, 들어갈 사람의 id 가 다를텐데

--> U가 들어오면 U의 id ,

P가 들어오면 P의 db 숫자일텐데 하드코딩을 해놓으면 안된다. 그 사람의 고유한 id가 들어가야 한다.

 

 

https://www.digitalocean.com/community/tutorials/nodejs-jwt-expressjs

여기서도, {username: req.body.username} 은 

req에서 body에서 username을 가져온다 라는 변수


백엔드가 프론트에게 토큰을 보낼 때에는 response body에 보내고,

프론트가 토큰을 들고 다니다가, 결제 요청 보내고 할 때는 백엔드에게 요청 보낼 때 어떻게 보내지? req로 보내지 

그러니 req 보낼 때 header에 몰래 숨겨서 토큰을 보냄

 


복호화 (양방향 암호화)

decode가 아니라, verify로 해야 함.

결제할 때는 decode를 해줘야 함.

 

signature가 틀리면, 에러 (JsonWebTokenError) invalid signature (signature 잘못 들어갔다) 

 

단방향 암호화 vs 양방향 암호화 알아야 (면접에서 많이 ★물어봄) 

 

로그인 과정에서 토큰 여는 과정은 안 들어감. 토큰화 하는 것만. 

토큰 여는 과정은, 다음 api (결제 api)에서 필요한 것


백엔드가 결제하는 api 엔드포인트를 만들 때

1. 우리 db에 있는지 (없으면 로그인 안 한 거) 

2. 있으면 우리 signature 넣어서, 우리가 발급한 토큰으로 요청하는게 맞는지 (우리 서버에서 발급한 토큰인지) -> 없으면 error 띄우고

(프론트가 자기들끼리 만든 토큰으로 시도할 수도 있으니)


프론트엔드가 우리가 만든 회원가입, 로그인 함수에 들어오려면, 

프론트에게 백엔드가 알려줘야 할 것은, 

http:// ________:8000/users

1. 우리 서버에 접근하도록, 엔드포인트 알려줘야 함 /users /login

2. 이 서버에 들어올 수 있도록 ip 주소 알려줘야 함

같은 와이파이 쓴다면, 특정 컴퓨터 안에 있는 함수를 호출해야 함, (함수 호출 =우리집에 찾아와서 찾아가)

그 컴퓨터가 누군지 알려줘야 함. 

컴퓨터한테 고유하게 갖고 있는게 뭘까? ip 주소 

 

네트워크 상에서 특정 서버 (특정 컴퓨터)를 . 

주소 알려줘야 

 

3. 특정 서버에 들어오는 문이 여려 개 -> port 번호 알려줘야 들어올 수 

 

 

프론트는 저 주소 하나로 들어오는 거. 주소만 알면 됨. (회원가입인지, 로그인인지 알 거 없고) 

 

 

맥북에서 내 ip 주소 아는 법: ipconfig getifaddr en0