본문 바로가기

ExpressJS

Chapter5 - Session & JWT

1. Session과 JWT 개념

  • Session과 JWT(JSON Web Token)은 모두 사용자 인증 및 식별에 사용되는 기술입니다.
  • 세션은 사용자가 로그인하면 서버에서 세션을 생성하고 사용자의 상태 정보를 서버에 저장합니다. 클라이언트는 서버에 저장된 세션 정보를 통해 인증을 유지합니다
  • JWT는 사용자가 로그인하면 서버는 JWT를 발급하고 클라이언트에게 전달합니다. 클라이언트는 이 JWT를 갖고 서버에 요청을 보내며, 서버는 JWT를 검증하여 사용자를 인증합니다.
  • 세션은 서버에 상태를 유지하므로 서버 부하가 발생할 수 있고, 세션을 사용하려면 서버가 중앙 집중식으로 상태를 관리해야 하는 점을 고려해야 합니다.
  • JWT는 토큰이 탈취될 경우 보안에 취약할 수 있습니다. 또한, 토큰의 크기가 크고, 한 번 발급되면 변경이 불가능하므로 만료 기간을 적절히 설정해야 합니다.

 

2. Session 구현

  • 우선 app.js를 아래와 같이 수정하여 express-session 모듈을 추가해줍니다.
const express = require("express");
const session = require("express-session");

const app = express();
const port = process.env.PORT || 3000;

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(
  session({
    secret: "1q2w3e4r!",
    resave: false,
    saveUninitialized: true,
  })
);

app.use("/customer-api", require("./api/customers/customerController"));

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

 

 

  • 그리고 컨트롤러 부분에 아래와 같이 로그인 기능을 구현합니다.
  • 일반적으로는 ID와 Password를 이용해 로그인 기능을 구현하지만, 현재 DB에는 해당 정보가 없기 때문에 e-mail을 통한 로그인 방식으로 구현합니다.
router.post("/v1/login", async (req, res) => {
  const email = req.body.email;

  const customer = await customerService.getCustomerByEmail(email);
  if (!customer) {
    res.status(404).send();
  } else {
    req.session.email = req.body.email;
    res.json(customer);
  }
});

 

 

  • 위에 코드에서 사용된 getCustomerByEmail는 아래와 같이 구현합니다.
const getCustomerByEmail = async (email) => {
  try {
    const customer = await Customer.findOne({ where: { email: email } });

    return customer;
  } catch (error) {
    throw new Error("Error getting customer by email:", error);
  }
};

 

 

  • 위의 과정을 통해 로그인을 진행하게 되면 서버에 세션정보를 저장하게 됩니다.
  • 이제 해당 세션을 통해 인증하는 과정을 구현해보겠습니다. lib 디렉토리에 session.js를 생성하고 아래와 같이 작성해 줍니다.
const isAuthenticated = (req, res, next) => {
  if (req.session && req.session.email) {
    return next();
  } else {
    res.status(401).send();
  }
};

module.exports = {
  isAuthenticated,
};

 

 

  • 그리고 컨트롤러에서 인증이 필요한 라우터에 해당 미들웨어를 추가해줍니다.
/** 모든 고객 정보 가져오기 */
router.get("/v1/customer", isAuthenticated, async (req, res) => {
  try {
    const customers = await customerService.getAllCustomers();
    res.json(customers);
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: "Internal Server Error" });
  }
});

/** 특정 고객 정보 가져오기 */
router.get("/v1/customer/:customerId", isAuthenticated, async (req, res) => {
  const customerId = Number(req.params.customerId);

  const customer = await customerService.getCustomerById(customerId);
  if (!customer) {
    res.status(404).send();
  }
  res.json(customer);
});

 

 

  • 해당 서버에 로그인 정보 없이 고객정보에 접근하려고 하면 401에러가 발생합니다.

 

  • 없는 메일으로 로그인하면 404에러가 발생하고

 

  • 존재하는 메일로 로그인 한 후

 

  • 고객정보에 접근하면, 정상적으로 조회할 수 있습니다.
  • 해당 기능을 고도화한다면, 타인의 정보는 접근 할 수 없고 본인 정보에만 접근 할 수 있게끔 수정 할 수도 있겠습니다.

 

  • 참고로 postman에서 send아래 있는 cookies버튼을 눌러, 통신하고 있는 도메인을 추가해야 정상적인 테스트가 가능합니다.

 

3. JWT 구현

  • lib디렉토리에 jwt.js파일을 생성하고 아래와 같이 작성합니다.
const jwt = require("jsonwebtoken");

const secretKey = "1q2w3e4r!";

function generateToken(payload) {
  return jwt.sign(payload, secretKey, { expiresIn: "1h" });
}

function authenticateToken(req, res, next) {
  const token = req.headers["authorization"];

  if (!token) {
    return res.status(403).json({ error: "Forbidden(Token)." });
  }

  jwt.verify(token, secretKey, (err, decoded) => {
    if (err) {
      return res.status(401).json({ error: "Unauthorized(Token)." });
    }

    req.user = decoded;
    next();
  });
}

module.exports = {
  generateToken,
  authenticateToken,
};

 

 

  • 그리고 컨트롤러 부분에 아래와 같이 로그인 기능을 구현합니다.
  • 토큰을 생성하여 reponse합니다. front-end에서는 해당 토큰을 이용해 인증을 진행합니다.
router.post("/v1/login", async (req, res) => {
  const email = req.body.email;

  const customer = await customerService.getCustomerByEmail(email);
  if (!customer) {
    res.status(404).send();
  } else {
    const token = jwt.generateToken({
      email: email,
    });
    res.status(200).json({ token: token });
  }
});

 

 

  • 위와 동일하게, 인증이 필요한 라우터에 미들웨어를 추가해줍니다.
/** 모든 고객 정보 가져오기 */
router.get("/v1/customer", jwt.authenticateToken, async (req, res) => {
  const customers = await customerService.getAllCustomers();

  res.json(customers);
});

/** 특정 고객 정보 가져오기 */
router.get("/v1/customer/:customerId", jwt.authenticateToken, async (req, res) => {
  const customerId = Number(req.params.customerId);

  const customer = await customerService.getCustomerById(customerId);
  if (!customer) {
    res.status(404).send();
  }
  res.json(customer);
});

 

 

  • 토큰 없이 접근하면, 아래와 같이 401 에러가 발생합니다.

 

  • 로그인을 통해 토큰을 획득하고

 

  • 헤더에 토큰을 추가한뒤에 API에 접근하면 정상적으로 데이터를 확인 할 수 있습니다.

'ExpressJS' 카테고리의 다른 글

Chapter4 - Redis  (0) 2023.11.20
Chapter3 - DB연결  (0) 2023.09.19
Chapter2 - 프로젝트 구조  (0) 2023.09.17
Chapter1. 프로젝트 생성  (0) 2023.09.01