[TIL] 211201

Lee Syong·2021년 12ė›” 1ėž
0

TIL

ëŠĐ록 ëģīęļ°
105/204
post-thumbnail

📝 ė˜Ī늘 한 ęēƒ

  1. res.locals / MongoStore / saveUninitialized / 도ëДėļ / expires / .env

📚 ë°°ėšī ęēƒ

user authentication

1. 로ę·ļėļ

ė–īė œ ęģĩëķ€ė—ė„œ ėīė–īė„œ

export const postLogin = (req, res) => {
  // ėƒëžĩ
  
  // userė— 대한 ė •ëģīëĨž ė„ļė…˜ė— ėķ”ę°€í•œë‹Ī
  req.session.loggedIn = true;
  req.session.user = user;
  return res.redirect("/");
};

ėīė œ req.sessionė— ėķ”ę°€í•œ loggedInęģž user 값ė€ ëŠĻ든 ėŧĻíŠļëĄĪ럮ė—ė„œ ė‚ŽėšĐ 가ëŠĨ하ë‹Ī.

1) 프론íŠļė—”ë“œė— 표ė‹œ (템플ëĶŋ ėˆ˜ė •)

ë‹ĪėŒėœžëĄœ userė—ęēŒ 로ę·ļėļė— ė„ąęģĩ했ėŒė„ ė•ŒëĶŽęļ° ėœ„í•ī ė„ļė…˜ė— ęļ°ë°˜í•ī 프론íŠļė—”ë“œė— ë‚īėšĐėī 표ė‹œë˜ë„록 템플ëĶŋė„ ėˆ˜ė •í•īė•ž 한ë‹Ī.
req.session.loggedInėī falseėž 때 base.pug 파ėžė—ė„œ Joinęģž Login 링큎ëĨž ëģīė—ŽėĢžęģ , trueėž 때 Logoutęģž profile 링큎ëĨž ëģīė—ŽėĢžë„록 í•īė•ž 한ë‹Ī.

ėīëĨž ėœ„í•īė„  템플ëĶŋė—ė„œ req.session.loggedIn 값ė„ ė‚ŽėšĐ할 ėˆ˜ ėžˆė–īė•ž 한ë‹Ī.
pugė™€ express는 res.renderëĨž ėīėšĐ하ė§€ ė•Šęģ ë„ express middlewareëĨž 만ë“Īė–ī res.localsëĨž ėīėšĐí•ī 템플ëĶŋė— ëģ€ėˆ˜ëĨž ė „ė—­ė ėœžëĄœ ëģī낾 ėˆ˜ ėžˆë‹Ī.
ėī ė ė„ ėīėšĐí•ī locals objectė— 로ę·ļėļ한 userëĨž ėķ”ę°€í•œ 후 ėīëĨž 템플ëĶŋė— 가ė ļė™€ 프론íŠļė—”ë“œė— 표ė‹œí•  ėˆ˜ ėžˆë‹Ī.

ëĻžė €, src íī더ė— middlewares.js 파ėžė„ 만든 후 localsMiddlewareëĨž 만ë“Īė–ī export 한ë‹Ī.
server.js íī더ė—ė„œ localsMiddleware가 session middleware ë’Īė— ė˜Ī도록 ėž‘ė„ąí•œë‹Ī.
ę·ļ래ė•žë§Œ localsMiddlewareė—ė„œ 로ę·ļėļ한 userė— 대한 ė •ëģī가 ėžˆëŠ” sessionė— ė ‘ę·ží•  ėˆ˜ ėžˆęļ° 때ëŽļėīë‹Ī.

// middlewares.js
export const localsMiddleware = (req, res, next) => {
  res.locals.siteName = "Wetube";
  res.locals.loggedIn = Boolean(req.session.loggedIn);
  res.locals.loggedInUser = req.ssesion.user;
  next();
};
// server.js
import session from "express-session";
import { localsMiddleware } from "./middlewares";

app.use(session({
  secret: "Hello!",
  resave: true,
  saveUninitialized: true,
}));

app.use(localsMiddleware);

res.localsė˜ loggedIn 값ė„ ęļ°ë°˜ėœžëĄœ base.pug 파ėžė˜ navëĨž ėˆ˜ė •í•œë‹Ī.

user가 로ę·ļėļ ėƒíƒœę°€ ė•„니띞ëĐī loggedInUser(ėĶ‰, req.session.user)는 undefined 값ė„ 가ė§ˆ ęēƒėīë‹Ī.
따띾ė„œ 프로필 ëДë‰ī는 user가 로ę·ļėļ ėƒíƒœėž ęē―ėš°ė—ë§Œ ëģīėī도록 한ë‹Ī.

nav
  ul
    if loggedIn
      li
        a(href="/logout") Log out
      li
        a(href="/my-profile") #{loggedInUser.name}ė˜ Profile
    else
      li
        a(href="/join") Join
        a(href="/login") Login

ėīė œ 로ę·ļėļ/로ę·ļ ė•„ė›ƒ ėƒíƒœė— 따띾 ëДë‰ī가 닮띾ė§€ëŠ” ęēƒė„ 확ėļ할 ėˆ˜ ėžˆë‹Ī.

ðŸ’Ą postLogin ėŧĻíŠļëĄĪ럮

📌 ė•„ėī디 맞는ė§€ 확ėļ
📌 ëđ„ë°€ëēˆí˜ļ 맞는ė§€ 확ėļ
📌 req.sessionė„ ėīėšĐí•ī ė„ļė…˜ė— user ė •ëģī ėķ”ę°€
📌 res.localsëĨž ėīėšĐí•ī 프론íŠļė—”ë“œė— 로ę·ļėļ ė—Žëķ€ė— 따띾 닮ëĶŽ 표ė‹œ (ëŊļë“Īė›Ļė–ī)

2) MongoStore

session id는 ėŋ í‚Īė— ė €ėžĨ되ė§€ë§Œ, session 데ėī터는 ė„œëē„ė— ė €ėžĨ된ë‹Ī.
ė„œëē„ė— ėžˆëŠ” session storage는 ęļ°ëģļė ėœžëĄœ memory storeėīë‹Ī.
따띾ė„œ, ė„œëē„ëĨž ėžŽė‹œėž‘하ëĐī session 데ėī터는 ė‚Žëžė§„ë‹Ī.
ė„œëē„ę°€ ėžŽė‹œėž‘í•ī도 sessionėī ė‚Žëžė§€ė§€ ė•Šë„록 하ë ĪëĐī sessionė„ mongoDBė™€ ė—°ęē°í•īė•ž 한ë‹Ī.

(1) connect-mongo ė„Īėđ˜

express - connect-mongo ė°ļęģ 

$ npm install connect-mongo

connect-mongoëĨž ė„Īėđ˜í•œ 후 server.js 파ėžė—ė„œ mongoStoreëĨž import 하ęģ  MongoDBė— sessionsëĨž 만든ë‹Ī.

import mongoStore from "connect-mongo"; // ėķ”ę°€ ❗

app.use(session({
  secret: "Hello!",
  resave: true,
  saveUninitialized: true,
  store: MongoStore.create({ mongoUrl: "mongodb://127.0.0.1:27017/wetube" }), // ėķ”ę°€ ❗
}));

MongoDB shellė—ė„œ collectionsëĨž ęē€ėƒ‰í•˜ëĐī sessions가 만ë“Īė–īė§„ ęēƒė„ 확ėļ할 ėˆ˜ ėžˆë‹Ī.

> show collections
sessions
users
videos

(2) session 만ë“Īęļ°

ė•„ė§ė€ sessionsė— ė•„ëŽī런 session도 ėĄīėžŽí•˜ė§€ ė•ŠëŠ”ë‹Ī.
sessionė„ 만ë“Īęļ° ėœ„í•ī ė›đ ė‚ŽėīíŠļëĨž ėƒˆëĄœęģ ėđĻ 한ë‹Ī.
로ę·ļėļė„ 하ëĐī Log outęģž 프로필 ëДë‰ī가 뜮ë‹Ī.
MongoDB shellė—ė„œ sessionėī 만ë“Īė–īė§„ ęēƒė„ 확ėļ할 ėˆ˜ ėžˆë‹Ī.

> db.sessions.find()
{ "_id" : "pc__EnaXP5gY37scJHXEEy8SktMy17oL", "expires" : ISODate("2021-12-15T06:49:53.129Z"), "session" : "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"},\"loggedIn\":true,\"user\":{\"_id\":\"61a6b4041bee12373c5042ff\",\"email\":\"syong@naver.com\",\"username\":\"syong\",\"password\":\"$2b$05$zShlHon.zCXKrsSbJGzOfeuIm9vDFHasBIDiq6PsKPFSE0WV4n9WG\",\"name\":\"leesyong\",\"location\":\"goyang\",\"__v\":0}}" }

ėīė œëŠ” ė„œëē„ëĨž ë‹Ŧė€ 후 ėžŽė‹œėž‘í•ī도 ė—Žė „히 로ę·ļėļ ėƒíƒœėļ ęąļ 확ėļ할 ėˆ˜ ėžˆë‹Ī. (âˆĩ ė›đ ė‚ŽėīíŠļ ëДë‰īė™€ mongodb sessions ę·ļ대로)
sessionėī ė„œëē„ę°€ ė•„니띞 데ėī터ëē ėīėŠĪė— ė €ėžĨ되ė–ī ėžˆęļ° 때ëŽļėīë‹Ī.

3) 로ę·ļėļ한 user만 데ėī터ëē ėīėŠĪė— ė €ėžĨ하ęļ°

현ėžŽ session ė―”ë“œ

app.use(session({
  secret: "Hello!",
  resave: true,
  saveUninitialized: true,
  store: MongoStore.create({ mongoUrl: "mongodb://127.0.0.1:27017/wetube" }),
}));

ė„œëē„는 ë°ĐëŽļ하는 'ëŠĻ든 user'ė—ęēŒ session idëĨž ėĢžęģ , í•īë‹đ userė— 대한 ė •ëģīëĨž 폎í•Ļ하는 ę·ļ sessionė€ 데ėī터ëē ėīėŠĪė— ė €ėžĨ된ë‹Ī.
ę·ļ런데 ëī‡ėī나 로ę·ļėļ 하ė§€ ė•Šęģ  ęĩŽęē―만 하는 userë“Īėī ë°ĐëŽļ한 ęē―ėš°ė—ë„ ę·ļë“Īė— 대한 ė •ëģīëĨž 폎í•Ļ하는 sessionėī 데ėī터ëē ėīėŠĪė— ė €ėžĨ된ë‹Ī.
ėĩ멅ė˜ useręđŒė§€ ė €ėžĨ하는 ęēƒė€ 너ëŽī ëđ„íšĻėœĻė ėīëŊ€ëĄœ ėīëĨž ėˆ˜ė •í•īė•ž 한ë‹Ī.

req.session.loggedIn = true;
req.session.user = user;

sessionėī ėƒˆëĄœ 만ë“Īė–īė§„ 후 ėˆ˜ė •ëœ ė ėī ė—†ėœžëĐī, unininitialized ėƒíƒœëžęģ  한ë‹Ī.
sessionė„ ėˆ˜ė •í•˜ëŠ” ęēƒė€ ė˜Īė§ ėŧĻíŠļëĄĪ럮ė—ė„œ 가ëŠĨ한데, postLogin ėŧĻíŠļëĄĪ럮ė—ė„œ sessionė„ ėˆ˜ė •í•˜ëŠ” ė―”드는 ėœ„ė™€ 같ë‹Ī.

app.use(session({
  secret: "Hello!",
  resave: false, // ėˆ˜ė • ❗
  saveUninitialized: false, // ėˆ˜ė • ❗
  store: MongoStore.create({ mongoUrl: "mongodb://127.0.0.1:27017/wetube" }),
}));

'똑같ė€ sessionė€ 한 ëēˆë§Œ ė €ėžĨ'하ęļ° ėœ„í•īė„œëŠ”, resave ė†ė„ąė˜ 값ė„ trueė—ė„œ false로 ėˆ˜ė •í•īė•ž 한ë‹Ī. (true띞ëĐī 'user'가 로ę·ļėļ할 때마ë‹Ī sessionė„ 데ėī터ëē ėīėŠĪė— ė €ėžĨ한ë‹Ī.)

'user가 로ę·ļėļ했ė„ 때만' sessionė„ 데ėī터ëē ėīėŠĪė— ė €ėžĨ하ęļ° ėœ„í•īė„œëŠ”, server.js 파ėžė˜ session ëķ€ëķ„ė—ė„œ saveuninitialized ė†ė„ąė˜ 값ė„ trueė—ė„œ false로 ėˆ˜ė •í•īė•ž 한ë‹Ī.
ėī는 ėīˆęļ°í™”되ė§€ ė•Šė€ sessionė€ storeė— ė €ėžĨ하ė§€ ė•ŠėŒė„ ė˜ëŊļ한ë‹Ī.
sessionėī ėˆ˜ė •ë˜ė—ˆė„ 때만(ėĶ‰, user가 로ę·ļėļ 했ė„ 때만) ė„œëē„는 ëļŒëžėš°ė €ė— ė„ļė…˜ idëĨž 넘ęēĻėĢžęģ , í•īë‹đ userė— 대한 ė •ëģī가 ë‹īęļī sessionė„ 데ėī터ëē ėīėŠĪė— ė €ėžĨ하도록 하는 ęēƒėīë‹Ī.
ėīė œ 로ę·ļėļ하ė§€ ė•Šė€ userė— 대한 ė •ëģī는 데ėī터ëē ėīėŠĪė— ė €ėžĨ되ė§€ ė•ŠëŠ”ë‹Ī.

4) ėŋ í‚Ī(cookie)

(1) 도ëДėļ(domain)

ėŋ í‚ĪëĨž 만든 ė„œëē„(ėŋ í‚ĪëĨž ė „ė†Ąí•īė•ž 하는 ė„œëē„)ëĨž 말한ë‹Ī.

(2) expires

만ëĢŒ 날ė§œëĨž ė§€ė •í•īėĢžė§€ ė•ŠėœžëĐī session cookie가 된ë‹Ī.
session cookie는 user가 ëļŒëžėš°ė €ëĨž ë‹ŦėœžëĐī ė‚Žëžė§„ë‹Ī.

Max-Age란 cookie가 만ëĢŒë˜ëŠ” 날ė§œëĨž 말한ë‹Ī.
1/1000ėīˆ ë‹Ļėœ„ëĄœ ėž‘ė„ąí•  ėˆ˜ ėžˆë‹Ī.

ė˜ˆëĨž ë“Īė–ī ė•„래ė™€ 같ėī ėž‘ė„ąí•˜ëĐī 로ę·ļėļ한 후ė— 10ėīˆę°€ ė§€ë‚˜ëĐī ėŋ í‚Ī는 ė‚Žëžė§€ęģ  ėžë™ėœžëĄœ 로ę·ļė•„ė›ƒëœë‹Ī.

app.use(session({
  cookie: {
    maxAge: 10000,
  },
}));

ėī는 ė˜ˆė‹œėž ëŋ, ė‹Īė œëĄœ 현ėžŽ 프로ė íŠļė—ė„œëŠ” 만ëĢŒ ė‹œė ė„ 따로 ė •í•īėĢžė§„ ė•Šė•˜ë‹Ī.

(3) secretęģž 데ėī터ëē ėīėŠĪ url ëģīí˜ļ

ëģīė•ˆė„ ėœ„í•ī secretęģž 데ėī터ëē ėīėŠĪ urlė€ ëģīėīė§€ ė•Šë„록 ëģīí˜ļ되ė–īė•ž 한ë‹Ī.

app.use(session({
  secret: "Hello!",
  resave: false,
  saveUninitialized: false,
  store: MongoStore.create({ mongoUrl: "mongodb://127.0.0.1:27017/wetube" }),
}));

secretėī란 ė„œëē„ę°€ ëļŒëžėš°ė €ė— ėŋ í‚ĪëĨž ėĪŽë‹Ī는 ęēƒė„ ėĶëŠ…하ęļ° ėœ„í•ī sign 할 때 ė‚ŽėšĐ하는 stringė„ 말한ë‹Ī.
ę·ļ런데 누ęĩ°ę°€ secretė„ ėīėšĐí•ī ėŋ í‚ĪëĨž 훔ėģ ëģļėļėī ę·ļ userėļė–‘ 할 ėˆ˜ ėžˆęļ° 때ëŽļė—(session hijack: ė„ļė…˜ ë‚Đėđ˜) secretė€ ęļļęēŒ ëŽīėž‘ėœ„ëĄœ 만ë“Īė–ī ė™ļëķ€ė— ë…ļėķœë˜ė§€ ė•Šë„록 í•īė•ž 한ë‹Ī.

한íŽļ, 데ėī터ëē ėīėŠĪ는 userė— 대한 ė •ëģīëĨž 가ė§€ęģ  ėžˆėœžëŊ€ëĄœ 데ėī터ëē ėīėŠĪ url 또한 ė™ļëķ€ė— ë…ļėķœë˜ė–īė„œëŠ” ė•ˆëœë‹Ī.

.env 파ėž ėƒė„ą

ėīëĨž ėœ„í•ī 환ęē― ëģ€ėˆ˜ëĨž 만ë“Īė–ī ė―”ë“œė— ë“Īė–ī가ëĐī ė•ˆë  ë‚īėšĐë“Īė„ ėķ”가할 ėˆ˜ ėžˆë‹Ī.
프로ė íŠļ íī더(package.json 파ėžėī ėžˆëŠ” ęģģ)ė— .env 파ėžė„ ėķ”ę°€í•œ 후 .gitignore 파ėžė— .envëĨž ėķ”ę°€í•œë‹Ī.
.env 파ėžė— ėķ”ę°€í•˜ëŠ” 값ė€ ęī€ėŠĩė ėœžëĄœ ëŠĻ두 대ëŽļėžëĄœ ė ė–īė•ž 한ë‹Ī.

// .env
COOKIE_SECRET=sdfjslkfjfoj23orjasfjslafsf9f
DB_URL=mongodb://127.0.0.1:27017/wetube

dotenv ė„Īėđ˜ 및 ė‚ŽėšĐ

dotenv는 .env 파ėžė„ ė―ė–ī 각각ė˜ ëģ€ėˆ˜ë“Īė„ process.env ė•ˆėœžëĄœ ë„Ģė–īėĪ€ë‹Ī.
ė„Īėđ˜ 후 가ëŠĨ한 한 ëđĻëĶŽ(파ėžė˜ ėœ„ėŠ―ė—ė„œ) ė‚ŽėšĐ하도록 한ë‹Ī.

$ npm i dotenv

가ëŠĨ한 한 ė•ą 파ėžė˜ 가ėžĨ ėœ„ėŠ―ė—ė„œ import 하도록 한ë‹Ī.
현ėžŽ 프로ė íŠļė˜ ęē―ėš° server.jsė™€ init.js 파ėžė„ ëķ„ëĶŽí–ˆęļ° 때ëŽļė— ė‹Īė œëĄœ ė•ąė„ ė‹Ī행ė‹œí‚Ī는 init.js 파ėžė˜ 가ėžĨ ėœ„ėŠ―ė—ė„œ import í•īėĢžė—ˆë‹Ī.

// init.js
import "dotenv/config";
import "./db";
// ėī하 ėƒëžĩ

process.env

ėīė œ process.envëĨž ėīėšĐí•ī .env 값ë“Īė— ė ‘ę·ží•ī ė‚ŽėšĐ할 ėˆ˜ ėžˆë‹Ī.

// server.js
app.use(session({
  secret: process.env.COOKIE_SECRET, // ėˆ˜ė • ❗
  resave: false,
  saveUninitialized: fasle,
  store: MongoStore.create({ mongoUrl: process.env.DB_URL }); // ėˆ˜ė • ❗
}));
// db.js
mongoose.connect(process.env.DB_URL); // ėˆ˜ė • ❗

âœĻ ë‚īėž 할 ęēƒ

  1. 강ė˜ ęģ„ė† ë“Ģęļ°
profile
ëŠĨ동ė ėœžëĄœ ė‚īėž, 행ëģĩ하ęēŒðŸ˜

0개ė˜ 댓ęļ€