๊ด€๋ฆฌ ๋ฉ”๋‰ด

Daily Front_Minhhk

[session] sprint-auth-session ๋ณธ๋ฌธ

Code๊ฐœ๋ฐœ์ผ์ง€

[session] sprint-auth-session

Minhhk 2023. 1. 7. 20:58
๐Ÿ”ฅ ์„œ๋ฒ„์—ด๊ธฐ์™€ Client ๋Š” ์•ž ์„  sprint-auth-cookie ์™€ ๋™์ผ!!! cookie → session ์œผ๋กœ ๋ฐ”๋€” ๋ฟ์ด๋‹ค! ์ฐธ๊ณ ๋กœ ๊ฐ™์ด ๋„ฃ์–ด ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ Server ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

๐Ÿ’ก ์„œ๋ฒ„์—ด๊ธฐ

 

HTTPS ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก

์„œ๋ฒ„์˜ index.js ํŒŒ์ผ์„ ํ™•์ธํ•ด๋ณด๋ฉด ์ธ์ฆ์„œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š”๋ฐ,

// ์ธ์ฆ์„œ ํŒŒ์ผ๋“ค์ด ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ https ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
// ๋งŒ์•ฝ ์ธ์ฆ์„œ ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š”๊ฒฝ์šฐ, http ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
// ํŒŒ์ผ ์กด์žฌ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ํด๋”๋Š” package.json์ด ์œ„์น˜ํ•œ server ํด๋”์ž…๋‹ˆ๋‹ค.
let server;
if (fs.existsSync("./key.pem") && fs.existsSync("./cert.pem")) {
  const privateKey = fs.readFileSync(__dirname + "/key.pem", "utf8");
  const certificate = fs.readFileSync(__dirname + "/cert.pem", "utf8");
  const credentials = {
    key: privateKey,
    cert: certificate,
  };

ํ•ด๋‹น ํด๋”์— ์ธ์ฆ์„œ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— HTTPS ์„œ๋ฒ„๊ฐ€ ์•„๋‹Œ ๊ทธ๋ƒฅ HTTP ์„œ๋ฒ„๊ฐ€ ์—ด๋ ค์„œ

  • ์„œ๋ฒ„ ํด๋” ์•ˆ์—์„œ mkcert๋ฅผ ์‚ฌ์šฉํ•ด ์ธ์ฆ์„œ๋ฅผ ๋งŒ๋“ค์–ด์„œ HTTPS ์„œ๋ฒ„๋กœ ๋ณ€๊ฒฝ
  • mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1

๋‚˜๋จธ์ง€ ๊ฑด๋“ค์ง€ ๋ง๊ณ  corsOptions ์— Cors ์„ค์ •ํ•ด์ค€๋‹ค.

server/index.js

const corsOptions = {
  /* TODO: CORS ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์–ด๋–ค origin์ธ์ง€์— ๋”ฐ๋ผ ๋‹ฌ๋ฆฌ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
   * ๋ฉ”์„œ๋“œ๋Š” GET, POST, OPTIONS๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
   */
  // client๋Š” <http://localhost:3000> ์„ ์ด์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  origin: "<http://localhost:3000>",

  // cookie๋Š” ์ธ์ฆ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์œผ๋ฏ€๋กœ credentials๋„ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค.
  credentials: true,

  // ํ—ˆ์šฉํ•  ๋ฉ”์†Œ๋“œ๋ฅผ ๋ฐฐ์—ด์— ๋‹ด์•„์„œ ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.
  methods: ["GET", "POST", "OPTION"],
};

app.use(cors(corsOptions));

app.post("/login", controllers.login);
app.post("/logout", controllers.logout);
app.get("/userinfo", controllers.userInfo);

ํ˜น์‹œ ํฌ๋กฌ https ์—๋Ÿฌ๊ฐ€ ๋œฌ๋‹ค๋ฉด

ํฌ๋กฌ ์—…๋ฐ์ดํŠธ ํ•ด์ค์‹œ๋‹ค(์•ˆ๋œ ์ด์œ  ์ €๋Š”,,)

์•„๋ž˜ ๋งํฌ์— ๋˜ ์ž์„ธํžˆ ์„ค๋ช…! ์ฐธ์กฐ

 

 

10 Ways to Fix the NET::ERR_CERT_DATE_INVALID Error

The NET::ERR_CERT_DATE_INVALID error occurs when browsers don't trust the reliability of a site's SSL certificate. Here's how to fix it.

kinsta.com

 

 

๐Ÿช Client

 

client/app.js

→ ์ „๋‹ฌ ํ•ด ์ค„ props ๋ฐ›์•„ ๋‚ด๋ ค์˜ค๊ธฐ

import "./App.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Login from "./pages/Login";
import Mypage from "./pages/Mypage";
import React, { useEffect, useState } from "react";
import axios from "axios";

// ๋ชจ๋“  ์š”์ฒญ์— withCredentials๊ฐ€ true๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค.
axios.defaults.withCredentials = true;

function App() {
  const [isLogin, setIsLogin] = useState(false);
  const [userInfo, setUserInfo] = useState(null);

  const authHandler = () => {
    return axios
      .get("<https://localhost:4000/userinfo>")
      .then((res) => {
        setIsLogin(true);
        setUserInfo(res.data);
      })
      .catch((err) => {
        console.log(err.response.data);
      });
  };

  useEffect(() => {
    // ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ ์‹œ ์•„๋ž˜ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
    authHandler();
  }, []);

  return (
    <BrowserRouter>
      <div className="main">
        <Routes>
          <Route
            path="/"
            element={
              isLogin ? (
                <Mypage
                  /*
                TODO: ๋ Œ๋”๋ง์— ํ•„์š”ํ•œ App์˜ ์ƒํƒœ์™€ ์ด๋ฅผ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก props๋ฅผ ์ „๋‹ฌํ•˜์„ธ์š”. 
                */
                  userInfo={userInfo}
                  setIsLogin={setIsLogin}
                  setUserInfo={setUserInfo}
                />
              ) : (
                <Login
                  /*
                TODO: App์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก props๋ฅผ ์ „๋‹ฌํ•˜์„ธ์š”. 
                */
                  setIsLogin={setIsLogin}
                  setUserInfo={setUserInfo}
                />
              )
            }
          />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

client/pages/Login.js

→ props๋กœ ์ „๋‹ฌ ๋ฐ›์€ ๊ฒƒ ์ ์šฉ

→ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์— ๋„˜๊ฒจ์ค„ ํ•ธ๋“ค๋Ÿฌ

export default function Login({ setIsLogin, setUserInfo }) {
  const [loginInfo, setLoginInfo] = useState({
    userId: "",
    password: "",
  });
  const [checkedKeepLogin, setCheckedKeepLogin] = useState(false);
  const [errorMessage, setErrorMessage] = useState("");
  const handleInputValue = (key) => (e) => {
    setLoginInfo({ ...loginInfo, [key]: e.target.value });
  };
  const loginRequestHandler = () => {
    // TODO: Login ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” state๋ฅผ ์ด์šฉํ•ด ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
    // ๋กœ๊ทธ์ธ์— ํ•„์š”ํ•œ ์œ ์ €์ •๋ณด๊ฐ€ ์ถฉ๋ถ„ํžˆ ์ œ๊ณต๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์—๋Ÿฌ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜ํƒ€๋‚˜๋„๋ก ๊ตฌํ˜„ํ•˜์„ธ์š”.
    if (!loginInfo.userId || !loginInfo.password) {
      setErrorMessage("์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”");
      // ์ž…๋ ฅ๋˜์ง€ ์•Š์€ ๊ฐ’์ด ์žˆ๋Š”๊ฑฐ๋‹ˆ๊นŒ ์š”์ฒญ์„ ๋ณด๋‚ด๋ณผ ํ•„์š”๋„ ์—†์ด ๋ฐ”๋กœ ๋ฆฌํ„ดํ•ด์ค๋‹ˆ๋‹ค.
      return;
    }
    return axios
      .post("<https://localhost:4000/login>", { loginInfo, checkedKeepLogin })
      .then((res) => {
        // ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด ์‘๋‹ต์œผ๋กœ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๊ฐ€ Mypage์— ๋ Œ๋”๋ง๋˜๋„๋ก State๋ฅผ ๋ณ€๊ฒฝํ•˜์„ธ์š”.
        setIsLogin(true);
        setUserInfo(res.data);
        //์—ฌ๊ธฐ์—์„œ ์—๋Ÿฌ ์ดˆ๊ธฐํ™”
        setErrorMessage("");
      })
      .catch((err) => {
        // ๋กœ๊ทธ์ธ์— ์‹คํŒจํ–ˆ๋‹ค๋ฉด ๊ทธ์— ๋Œ€ํ•œ ์—๋Ÿฌ ํ•ธ๋“ค๋ง
        if (err.response.status === 401) {
          setErrorMessage("๋กœ๊ทธ์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.");
          alert('๋กœ๊ทธ์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค!')
        }
      });
  };

client/pages/Mypage.js

→ props๋กœ ์ „๋‹ฌ ๋ฐ›์€ ๊ฒƒ ์ ์šฉ

→ ๋กœ๊ทธ์•„์›ƒ์€ ๋กœ๊ทธ์ธ๊ณผ ๋ฐ˜๋Œ€๋กœ

 โžก๏ธ setIsLogin(false); setUserInfo(null);

 

export default function Mypage({ userInfo, setIsLogin, setUserInfo }) {
  const logoutHandler = () => {
    // TODO: Logout ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ์‹œ Login ํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•˜์„ธ์š”.
    return axios
      .post("<https://localhost:4000/logout>")
      .then((res) => {
        // ๋กœ๊ทธ์•„์›ƒ์— ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด App์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์„ธ์š”.
        setIsLogin(false);
        setUserInfo(null);
      })
      .catch((err) => {
        // ๋กœ๊ทธ์•„์›ƒ์— ์‹คํŒจํ–ˆ๋‹ค๋ฉด ๊ทธ์— ๋Œ€ํ•œ ์—๋Ÿฌ ํ•ธ๋“ค๋ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.
        console.log(err.response.data);
        alert(err);
      });
  };

 

 

 

 

 ๐Ÿช Server

 

index.js ๋Š” ๋งจ ์ฒซ๋ถ€๋ถ„ ์„œ๋ฒ„ ์—ด๊ธฐ์—์„œ ํ™•์ธ!

server/controllers/users/login.js

const { USER_DATA } = require("../../db/data");
module.exports = (req, res) => {
  const { userId, password } = req.body.loginInfo;
  const { checkedKeepLogin } = req.body;
  const userInfo = {
    ...USER_DATA.filter(
      (user) => user.userId === userId && user.password === password
    )[0],
  };
  /*
   * TODO: ๋กœ๊ทธ์ธ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.
   *
   * userInfo์—๋Š” ์š”์ฒญ์˜ ๋ฐ”๋””๋ฅผ ์ด์šฉํ•ด db์—์„œ ์กฐํšŒํ•œ ์œ ์ €์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ˜์†”์—์„œ userInfo๋ฅผ ์ถœ๋ ฅํ•ด๋ณด์„ธ์š”.
   * ์œ ์ €์˜ ์ •๋ณด๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค๋ฉด ํ•ด๋‹น ์œ ์ €๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒƒ์ž„์œผ๋กœ ๋กœ๊ทธ์ธ ์„ฑ๊ณต์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์ „์†กํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
   * ๋งŒ์•ฝ undefined๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค๋ฉด ํ•ด๋‹นํ•˜๋Š” ์œ ์ €๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž„์œผ๋กœ ๋กœ๊ทธ์ธ ์‹คํŒจ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์ „์†กํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
   *
   * ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ์—๋Š” ํด๋ผ์ด์–ธํŠธ์— ์„ธ์…˜ ์•„์ด๋””๋ฅผ ์ „์†กํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
   * req.session์„ ์‚ฌ์šฉํ•ด ์„ธ์…˜ ๊ฐ์ฒด์— userInfo.id๋ฅผ ์ €์žฅํ•˜์„ธ์š”.
   * ์˜์†์„ฑ์žˆ๋Š” ์ฟ ํ‚ค๋กœ ์„ธ์…˜ ์•„์ด๋””๋ฅผ ๋ณด๋‚ด๋ ค๋ฉด max-age ๋˜๋Š” expires ์˜ต์…˜์„ ์„ค์ •ํ•˜์„ธ์š”.
   *
   * ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ”๋กœ ์‘๋‹ต์„ ๋ณด๋‚ด์ง€์•Š๊ณ  ์„œ๋ฒ„์˜ /useinfo๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
   * express์˜ res.redirect ๋ฉ”์„œ๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์„œ๋ฒ„์˜ /userinfo๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•˜์„ธ์š”.
   */
  if (!userInfo.id) {
    res.status(401).send("Not Authorized");
  } else if (checkedKeepLogin) {
    req.session.sessionId = userInfo.id;

    // ์„ธ์…˜ ๊ตฌ์กฐ ๋“ค์—ฌ๋‹ค๋ณด๊ธฐ -> ํ„ฐ๋ฏธ๋„์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค
    console.log(req.session);

    // ์ฟ ํ‚ค ์˜ต์…˜ ์„ค์ •ํ•˜๊ธฐ
    req.session.cookie.maxAge = 1000 * 60 * 30;

    res.redirect("/userinfo");
  } else {
    req.session.sessionId = userInfo.id;
    res.redirect("/userinfo");
  }
};

server/controllers/users/logout.js

module.exports = (req, res) => {
  /*
   * TODO: ๋กœ๊ทธ์•„์›ƒ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”. ๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ์€ ์„ธ์…˜์„ ์‚ญ์ œํ•˜๋Š” ๊ณผ์ •์„ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
   *
   * ์„ธ์…˜ ์•„์ด๋””๋ฅผ ํ†ตํ•ด ๊ณ ์œ ํ•œ ์„ธ์…˜ ๊ฐ์ฒด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
   * ์•ž์„œ ๋กœ๊ทธ์ธ์‹œ ์„ธ์…˜ ๊ฐ์ฒด์— ์ €์žฅํ–ˆ๋˜ ๊ฐ’์ด ์กด์žฌํ•  ๊ฒฝ์šฐ, ์ด๋ฏธ ๋กœ๊ทธ์ธํ•œ ์ƒํƒœ๋กœ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
   * ์„ธ์…˜ ๊ฐ์ฒด์— ๋‹ด๊ธด ๊ฐ’์˜ ์กด์žฌ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์‘๋‹ต์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.
   */

    req.session.destroy();
    res.status(205).send("Logged Out Successfully");

};

server/controllers/users/userInfo.js

const { USER_DATA } = require("../../db/data");

module.exports = (req, res) => {
  /*
   * TODO: ์„ธ์…˜ ๊ฐ์ฒด์— ๋‹ด๊ธด ๊ฐ’์˜ ์กด์žฌ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์œ ์ € ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.
   *
   * ์„ธ์…˜ ๊ฐ์ฒด์— ๋‹ด๊ธด ์ •๋ณด๊ฐ€ ๊ถ๊ธˆํ•˜๋‹ค๋ฉด, req.session์„ ์ฝ˜์†”๋กœ ์ถœ๋ ฅํ•ด๋ณด์„ธ์š”.
   * ๋กœ๊ทธ์ธ ์‹œ ์„ธ์…˜ ๊ฐ์ฒด์— ์ €์žฅํ•œ userInfo.id๋ฅผ ์ด์šฉํ•ด ํ•ด๋‹นํ•˜๋Š” ์œ ์ €๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
   */
  const sessionId = req.session.sessionId;
  const userInfo = {
    ...USER_DATA.filter((user) => user.id === sessionId)[0],
  };

  if (!sessionId || !userInfo.id) {
    res.status(401).send("Not Authorized");
  } else {
		// ์„ธ์…˜์—์„œ๋Š” ํŒจ์Šค์›Œ๋“œ๋ฅผ ์ œ๊ฑฐ ํ•ด์ค€๋‹ค
    delete userInfo.password;
    res.send(userInfo);
  }
};

 

 

npm test → ํ…Œ์ŠคํŠธ ํ†ต๊ณผใ„ฑใ„ฑ

'Code๊ฐœ๋ฐœ์ผ์ง€' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

OAuth,, sprint-auth-OAuth ์ฐธ์กฐ  (0) 2023.01.10
Token,, sprint-auth-token  (0) 2023.01.07
Session  (0) 2023.01.07
[cookie] sprint-auth-cookie  (0) 2023.01.07
Cookie  (0) 2023.01.07