본문 바로가기
Do it Node.js/EJS BackEnd

20. 로그인, 토큰

by 갱생angel 2024. 3. 19.

쿠키 : 웹 브라우저를 이용할 때 사용자 컴퓨터에 저장되는 정보

  -cookie-parser : 웹 애플리케이션에서 쿠키를 만들거나 구문을 분석할 떄 사용

>> npm install cookie-parser

세션 : 웹 브라우저를 이용할 때  서버에 저장되는 정보

캐시 : 데이터를 미리 복사해 놓는 임시 저장 장소

 

토큰 

  -세션과는 다르게 서버에 저장하지 않음

  -사용자 정보, 위조를 막는 서명 등 인증에 필요한 모든 정보가 담김

  -서버 간 공유할 수도 있음

JWT : 노드에서 자주 사용하는 JSON 형식의 토큰 방식 모듈, 헤더/페이로드/서명 3영역으로 나뉨

  -헤더 : 알고리즘과 유형이 담김

  -페이로드 : 사용자 인증 정보, 내용이 담김

  -서명 : 비밀 키로 .env 파일같이 안전한 곳에 저장해서 사용

>> npm install jsonwebtoken

[로그인 페이지]

<%- include('./include/_home_header') %>
<main id="site-main">
  <div class="home-container">
    <h3>로그인</h3>
    <p>로그인이 필요한 서비스입니다.</p>
    <form action="/" method="POST" class="login">
      <label for="username"><b>Username</b></label>
      <input
        type="text"
        placeholder="사용자 아이디"
        name="username"
        id="username"
      />
      <label for="password"><b>Password</b></label>
      <input
        type="password"
        placeholder="비밀번호"
        name="password"
        id="password"
      />
      <button type="submit">로그인</button>
    </form>
  </div>
</main>
<%- include('./include/_footer') %>

[토큰 생성]

 

<.env> : 토큰 비밀키를 지정

DB_CONNECT = mongodb+srv://<username>:<password>@cluster0.oimz7n5.mongodb.net/myContact
//커넥션 스트링(데이터베이스)를 변수에 지정
JWT_SECRET = '비밀키' // 토큰 비밀키 지정

 

controllers - <loginController.js> : 로그인 시 토큰 생성

.findOne() : 조건에 맞는 도큐먼트가 여러 개일 경우 첫 번째 도큐번트를 찾는 함수

bcrypt.compare(data, encrypted, callback) : 입력한 비밀번호와 저장된 비밀번호가 같은 지 비교하는 함수

  -data : 입력값

  -encrypted : 저장된 입력값

  -callback : 콜백 함수

jwt.sign(페이로드, 비밀키, [옵션, 콜백]) : 토큰을 생성하는 함수

res.cookie(name, value, option) : 쿠키를 생성하는 함수

  -httpOnly : true : HTTP 통신에서만 쿠키를 생성하도록 제한

(...)
require("dotenv").config; //.env 파일 불러오기
const jwt = require("jsonwebtoken"); //jsonwebtoken 불러오기
const jwtSecret = process.env.JWT_SECRET; //.env 파일에 비밀키 JWT_SECRET 지정

(...)

//Post Login User, /
const loginUser = asynchHandler(async (req, res) => {
  const { username, password } = req.body;
  
  const user = await User.findOne({ username }); //입력값이 DB에 username으로 있는 지 조회
  if (!user) { //입력값이 DB에 username으로 없는 경우
    return res.status(401).json({ message: "일치하는 사용자가 없습니다." });
  }
  
  const isMatch = await bcrypt.compare(password, user.password); //입력값과 DB에 저장된 비밀번호 비교
  if (!isMatch) { //입력값이 DB에 비밀번호로 없는 경우
    return res.status(401).json({ message: "비밀번호가 일치하지 않습니다." });
  }
  
  const token = jwt.sign({ id: user._id }, jwtSecret); //사용자 ID를 기반으로 JWT 토큰 생성
  res.cookie("token", token, { httpOnly: true }); //생성된 토큰을 쿠키에 저장
  res.redirect("/contact"); //로그인 성공 시 /contact로 이동
});

(...)

[로그인 여부 확인]

 

middlewares - <checkLogin.js> : 로그인 여부 확인 미들웨어

"Cache-Control", "no-cache, no-store, must-revalidate"

  -Cache-Control : 캐시 설정

  -no-cache : 브라우저에서 캐시를 사용하지 않고 서버에서 응답을 매번 다시 받음

  -no-store : 서버의 응답을 캐시에 저장하지 않음

  -must-revalidate : 만일 캐시에 있는 정보를 사용하더라도 반드시 서버에 다시 확인해야 함

jwt.verify(토큰, 비밀키, [옵션]) : 서버에서 토큰을 검증하고 해석하는 함수

const jwt = require("jsonwebtoken"); //jsonwebtoken 불러오기
require("dotenv").config; //.env 파일 불러오기
const jwtSecret = process.env.JWT_SECRET; //.env 파일에 비밀키 JWT_SECRET 지정

const checkLogin = async (req, res, next) => {
  res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); //브라우저 캐시를 막음
  
  const token = req.cookies.token; //요청한 쿠키에서 토큰값 가져오기
  if (!token) { //토큰이 없을 경우(로그인이 안 되있을 경우)
    return res.redirect("/");
  }

  try { //토큰이 있을 경우(로그인을 했을 경우)
    const decoded = jwt.verify(token, jwtSecret); //토큰 해석
    req.username = decoded.username; //해석한 토큰을 사용자 이름에 저장
    next();
  } catch (error) {
    return res.status(401).json({ message: "로그인이 필요합니다." });
  }
};

module.exports = checkLogin;

 

routes - <contactRoutes.js> : checkLogin.js 미들웨어 등록

const express = require("express");
const router = express.Router();
const cookieParser = require("cookie-parser"); //cookie-parser 불러오기
const checkLogin = require("../middlewares/checkLogin"); //checkLogin 미들웨어 불러오기
const {
  getAllContact,
  createContect,
  getContact,
  updateContact,
  deleteContact,
  addContactForm,
} = require("../controllers/contactController");

router.use(cookieParser()); //cookie-parser 등록

router.route("/").get(checkLogin, getAllContact);
router.route("/add").get(checkLogin, addContactForm).post(checkLogin, createContect);
router.route("/:id").get(checkLogin, getContact).put(checkLogin, updateContact)
                    .delete(checkLogin, deleteContact);

module.exports = router;

[로그아웃]

 

views - include - <_header.ejs> : 로그아웃 추가

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>연락처 관리하기</title>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
    />
    <link rel="stylesheet" href="/css/style.css" />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
      integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
    />
  </head>
  <body>
    <header class="border-shadow">
      <div class="container">
        <nav>
          <a href="/"><i class="fa-solid fa-address-book"></i> My Contacts</a>
        </nav>
        <div class="login-box">
          <a href="/logout">로그아웃</a> <!--로그아웃-->
        </div>
      </div>
    </header>
  </body>
</html>

 

controllers - <loginController.js> : 로그아웃 추가

res.clearCookie (name) : 쿠키 삭제

(...)

//Get Logout, /logout : 로그아웃
const logout = (req, res) => {
  res.clearCookie("token"); //쿠키에 있는 토큰 삭제
  res.redirect("/");
};

module.exports = { getLogin, loginUser, getRegister, registerUser, logout }; //logout 내보냄

 

routes - <loginRoutes.js> : 로그아웃 기능 라우팅

const express = require("express");
const router = express.Router();
const {
  getLogin,
  loginUser,
  getRegister,
  registerUser,
  logout,
} = require("../controllers/loginController");

router.route("/").get(getLogin).post(loginUser); //로그인 페이지
router.route("/register").get(getRegister).post(registerUser);
router.route("/logout").get(logout); //로그아웃 기능

module.exports = router;

'Do it Node.js > EJS BackEnd' 카테고리의 다른 글

19. 회원가입, bcrypt  (0) 2024.03.19
18. 연락처 추가, 삭제  (0) 2024.03.16
17. 연락처 추가  (0) 2024.03.15
16. include, 연락처 표시  (0) 2024.03.14
15. EJS  (0) 2024.03.13