2023.05.12 session jwt access token 발급

이무헌·2023년 7월 21일
0

node.JS

목록 보기
4/10
post-thumbnail

1.JWT 토큰

💡 JWT 란? ⇒Json Web Token의 줄임말이다. JWT는 웹 토큰의 한 종류로서 클라이언트와 서버 간의 인증 정보를 안전하게 전달하기 위해 사용됩니다. JWT는 서명된 토큰으로, 정보를 JSON 객체로 표현하고 필요한 경우 디지털 서명을 통해 검증됩니다. 일반적으로 사용자 인증을 처리하고 API 요청에 대한 인가를 구현하는 데 사용됩니다.

jwt에는 총 3가지의 구성요소가 있다.

  • Header: 토큰의 유형 및 사용하는 해싱 알고리즘에 대한 정보를 포함합니다.
  • Payload: 클레임(claim)이라고도 불리는 토큰에 담길 정보를 포함합니다. 클레임은 이름-값 쌍으로 구성되며, 등록된 클레임, 공개 클레임, 비공개 클레임으로 구분됩니다.
  • Signature: 서버에서 생성한 비밀키를 사용하여 토큰의 무결성을 보장하기 위해 생성됩니다. 서버는 이 서명을 사용하여 토큰을 검증합니다.

인코딩 하기위한 secret KEY가 필요하므로 환경변수 설정을 잘 해주어야 한다.

const e = require("express");
const jwt = require("jsonwebtoken");
const path = require("path");
// dotenv 모듈 가져오기 config메서드 바로 실행
const dot = require("dotenv").config();
// process.env 객체에 우리가 .env파일에 설정한 이름으로 키값이 들어있다.
const KEY = process.env.KEY;

// JWT토큰을 만들건데
// 비밀키를 가지고 토큰을 만들어서 암호화를 시킬 예정
// 이 비밀키를 탈취 안되게 env에 보관
const app = e();

app.set("views", path.join(__dirname, "page"));
app.set("view engine", "ejs");
// body 사용하기 위해서 extended 설정은 깊은 객체 사용할지 안할지
app.use(e.urlencoded({ extended: false }));

app.listen(8080, () => {
  console.log("성공했구나 이녀석....");
});

app.get("/", (req, res) => {
  res.render("main");
});

app.post("/login", (req, res) => {
  // 로그인을 정상적으로 했다 가정하고
  // 토큰을 발급
  // 유저 정보는 변수로 만들어 주자.
  const name = "user1";
  const KEY = process.env.KEY;
  // sign 메서드 JWT 토큰을 생성
  // 첫번째 매개변수 헤더객체
  // 두번째 매개변수 비밀키,
  // 세번째 매개변수 payload
  let token = jwt.sign(
    {
      // 타입은 JWT
      type: "JWT",
      // 유저 이름
      name: name,
    },
    KEY,
    {
      // 토큰을 유지 시킬 유효시간 만료시간
      // 5분 유지 시킬 토큰
      expiresIn: "5m",
      // 토큰 발급한 사람
      issuer: "user1",
    }
  );
  res.send(JSON.stringify(token));
  //   점으로 구분해서 첫번째가 header,두번째가 payload,세번째가 signature
  // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiSldUIiwibmFtZSI6InVzZXIxIiwiaWF0IjoxNjgzODU4NTU4LCJleHAiOjE2ODM4NTg4NTgsImlzcyI6InVzZXIxIn0.UkVJVrx0JPSA8WOBm1JpSQrmasn4mFTfoZZMzSecBcw
});

// 로그인 할 때 검증
// JWT 토큰을 사용합니다~

// Json Web Token
// 웹표준으로 두 객체의 JSON객체를 사용해서 정보를 안정성 있게 전달 해준다.

// JWT은 사용할 정보를 자체적으로 가지고 있다.(우리가 필요한 것들 유저 정보같은)
// JWT로 발급한 토큰은 기본정보(유저의 정보 프로필)
// 그리고 토큰이 정상인지 검증(서명을 포함 하고 있다. signature)

// 주로 로그인이 정상적인지 회원 인증 권한에서 사용한다.

// JWT은 유저가 로그인을 요청하면 서버에서 유저의 정보를 가지고
// 정상적인 루트로 로그인을 요청한 유저면 토큰을 발급해서 전달해준다.
// 유저가 서버에 요청을 할 때 JWT 토큰을 포함해서 요청을 하면 서버가
// 요청을 받고 토큰이 썩은건지 착한건지 검사해서 유저가 요청한 작업을 처리해주고 응답해준다.

// JWT를 쓰는 이유는 안정성 있게 정보를 전달해서 요청을 할 수 있다.

// JWT를 생성하면 사용할 모듈이 인코딩과 해싱작업을 해준다.
// HMAC:해싱 기법을 적용해서 메시지의 위변조를 방지하는 기법
// SHA256:임의의 길이 메시지를 256bit의 축약된 메시지로 만들어내는 해시 알고리즘.

// JWT의 구조

// ------------------------------------
let header = {
  // 사용하는 해싱 알고리즘
  alg: "SHA256",
  //   토큰의 타입
  type: "JWT",
};

let payload = {
  // 토큰의 이름 제목
  sub: "546534",
  //   유저의 이름(유저 프로필)
  name: "namae",
  //   토큰 발급된 시간, 발급된지 얼마나 지났는지
  lat: "123123123",
};
// ------------------------------------

// 비밀 키 생성

// let signature = HMACSHA256(BASE64URL(header)) + BASE64URL(payload);

// header:타입과 알고리즘의 정보를 가지고 있고.
// payload:유저의 정보와 만료기간이 포함된 객체를 가지고 있다.
// signature:header,payload을 인코딩하고 합쳐서 해싱해 비밀키로 만듦.

// 사용할 모듀 express,jsonwebtoken,dotenv
// dotenv 어플리케이션을 만들때 설정값을 이 곳에 작성
// 보안=>민감한 정보를 .env 파일에 설정값들을 작성 해둔다.
// 비밀 키, 암호, API 토큰 등등을 저장해놓는다.

// 페이지 부터 만들자.
  • 환경변수
    // dotenv 모듈 가져오기 config메서드 바로 실행
    const dot = require("dotenv").config();
    // process.env 객체에 우리가 .env파일에 설정한 이름으로 키값이 들어있다.
    const KEY = process.env.KEY;
    • 환경변수 파일(.env)을 루트 경로에 만들고 process를 이용해 가져온다.
  • 토큰 요청을 받는 로직
    app.post("/login", (req, res) => {
      // 로그인을 정상적으로 했다 가정하고
      // 토큰을 발급
      // 유저 정보는 변수로 만들어 주자.
      const name = "user1";
      const KEY = process.env.KEY;
      // sign 메서드 JWT 토큰을 생성
      // 첫번째 매개변수 payload
      // 두번째 매개변수 비밀키,
      // 세번째 매개변수 헤더
      let token = jwt.sign(
        {
          // 타입은 JWT
          type: "JWT",
          // 유저 이름
          name: name,
        },
        KEY,
        {
          // 토큰을 유지 시킬 유효시간 만료시간
          // 5분 유지 시킬 토큰
          expiresIn: "5m",
          // 토큰 발급한 사람
          issuer: "user1",
        }
      );
      res.send(JSON.stringify(token));
      //   점으로 구분해서 첫번째가 header,두번째가 payload,세번째가 signature
      // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoiSldUIiwibmFtZSI6InVzZXIxIiwiaWF0IjoxNjgzODU4NTU4LCJleHAiOjE2ODM4NTg4NTgsImlzcyI6InVzZXIxIn0.UkVJVrx0JPSA8WOBm1JpSQrmasn4mFTfoZZMzSecBcw
    });
    • 첫번째 매개변수는 payload 객체를 넣는다 타입과 토큰을 받는 유저의 이름을 지정할 수 있다.
    • 두번째 매개변수는 비밀키가 들어간다. 토큰은 이 비밀키를 기준으로 복호화를 하며 decode도 진행한다. 즉, 보안은 필수적이다.
    • 세번째 매개변수는 헤더가 들어간다. 만료날짜와 발급한 사람의 이름이 들어간다.

2.JWT 토큰 쿠키에 넣기

  • app.js⇒ 토큰에 필요한 미들웨어 설정
    const e = require("express");
    const path = require("path");
    const session = require("express-session");
    const pageRouter = require("./routers/page");
    const tokenRouter = require("./routers/token");
    const verifyRouter = require("./routers/verify");
    const app = e();
    
    // 세션을 사용하기 위해 설치할 모듈
    // npm i express-session
    
    // ----------------------------------
    
    app.set("views", path.join(__dirname, "page"));
    app.set("view engine", "ejs");
    
    app.use(e.urlencoded({ extended: false }));
    
    app.use(
      session({
        // 세션을 발급할 때 사용할 키
        secret: process.env.KEY2,
        // 세션이 변경되거나 저장할 때나 불러올 때 다시 저장할지 여부
        resave: true,
        // 세션에 저장할 때 초기화 여부
        saveUninitialized: false,
      })
    );
    
    app.use(pageRouter);
    app.use(tokenRouter);
    app.use("/userVerify", verifyRouter);
    
    // 미들웨어로 실행
    
    app.listen(8000, () => {
      console.log("다른 폴더에서 성공했구나 이녀석...");
    });
    • session을 발급하기위해 미들웨어로 객체 설정을 해 주었다.
    • secret에서 사용된 키 KEY2는 세션을 발급 할 때 필요한 키이다. 이는 토큰을 발급할 때 필요한 KEY1과 다르다.
    • saveUninitialized는 app.js를 거쳤을때 초기화의 여부이다. 우리는 모든 경로가 app.js를 거치므로 초기화 시키면 안된다. 즉, 만료기간이 지나면 초기화시켜줘야 한다.
  • token.js⇒ 로그인시 토큰을 쿠키에 저장하는 파일
    // jsonwebtoken 설치
    
    const router = require("express").Router();
    const dot = require("dotenv").config();
    
    const jwt = require("jsonwebtoken");
    
    router.post("/login", (req, res) => {
      const name = "lee";
      const KEY = process.env.KEY;
      let token = jwt.sign(
        {
          type: "JWT",
          //   발급 받는 사람
          name: name,
        },
        KEY,
        {
          // 토큰의 유효시간 3분
          expiresIn: "3m",
          // 토큰 발급자
          issuer: name,
        }
      );
      req.session.token = token;
      res.render("page2");
    });
    
    // 다른곳에 로그인 했으면 로그인을 중복 로그인을 방지해주자
    //데이터베이스에 엑세스 토큰을 저장하고
    // 로그인을 하면 엑세스 토큰을 갱신시켜주는 작업
    
    module.exports = router;
    • 위에서 언급했다 시피 첫번째 매개변수는 payload,두번째 매개변수는 KEY,세번째 매개변수는 header이다.
    • req.session은 express-session이란 외부라이브러리를 설치해서 사용한 메서드이다.
    • jwt.sign으로 로그인시 토큰을 발급하고 session의 token을 우리가 설정한 token으로 바꿔주자
    • 이 때 key는 hash256으로 암호화 되고, 이를 기준으로 payload를 디코드 가능하게 한다. 주의해야 할 것은 key는 비가역(hash256이므로)이지만 payload는 가역적이라는 것이다.
  • verify.js⇒저장된 토큰을 유효한지 검증하는 파일
    const router = require("express").Router();
    const dot = require("dotenv").config();
    const jwt = require("jsonwebtoken");
    
    router.post("/", (req, res) => {
      const token = req.session.token;
      // 토큰이 유효한지 검증
      // verify 유효한 토큰인지 검증해주는 메서드
      // 첫번째 매개변수로 토큰을 전달하고
      // 두번째 매개변수로 key를 전달
      // 세번째 매개변수로 콜백함수 전달.
      // 콜백함수의 매개변수로 첫번째는 err내용 객체
      // 두번째는 해석된 객체
      const key = process.env.KEY;
      jwt.verify(token, key, (err, decoded) => {
        if (err) {
          console.log("썩은 토큰");
          res.send("토큰 썩었구나 or 변조되었구나");
        } else {
          console.log(decoded);
          res.send(decoded);
        }
      });
    });
    module.exports = router;
    • jwt.verify라는 내장 함수를 사용해서 검증 할 수 있다.
    • 만약 검증에 성공한다면 decode된 토큰 정보가 보내진다.
    • 성공적으로 검증됐을 때의 토큰 정보이다. 스크린샷 2023-05-12 오후 3.36.35.png

3.느낀점

💡 프로젝트를 만들 당시에는 로컬스토리지를 이용해서 세션을 유지하는 원시적인 방법을 썼는데. 토큰을 배움과 동시에 슬슬 실무에 가까워지고 있음을 느끼고 있다. 정처기를 준비하면서 보안과 관련된 기초지식을 쌓아 서버의 이해도를 높일 것이다. 앞으로 배울 mysql과 refresh토큰을 이용한 사용자 정보 처리도 기대가 된다.
profile
개발당시에 직면한 이슈를 정리하는 곳

1개의 댓글

comment-user-thumbnail
2023년 7월 21일

글이 잘 정리되어 있네요. 감사합니다.

답글 달기