Express.js passport-local 로그인 상태 관리 / 접근제한(글 수정)

개발정리·2022년 5월 13일
0
post-thumbnail

소개

이 글은 passport를 대강 학습했지만 passport를 배우면서
당연하게 들수있는 의문들을 해결해주는 글입니다.
passport가 처음이라면 글 나가셔서 생활코딩 - web5 passport 들으시는걸 추천드립니다

passport-local로 로그인 성공/실패처리와
로그인이 되어있지않으면 튕겨내고 로그인이 되어있으면
수정이나 삭제는 글 작성자가 아니면 접근하지못하게 튕겨내게 해보겠습니다.

db.json

export default {
  User : [
    {
      Name: "Zeta",
      ID: "zeta1232",
      PWD: "pwd",
    },
    {
      Name: "First",
      ID: "First1",
      PWD: "pwd",
    },
    {
      Name: "Egoing",
      ID: "Egoing123",
      PWD: "12345",
    },
  ],
  Topic: [
    {
      Title: "What is Node js?...",
      Desc: "Node js is .....",
      Writer: "zeta1232",
      id: 1,
    },
    {
      Title: "What is Python?...",
      Desc: "Python is .....",
      Writer: "First1",
      id: 2,
    },
  ],
};

passport와 접근제한을 생활코딩(Egoing)님을 보면서했기때문에
존경에 의미로다가 유저에 추가했습니다.

model.js

import db from "../db/index.js";

//FindOne(obj)
//

export default {
  FindOne: (ID) => {
    //id로 유저를 찾음
    return db.User.find((user) => user.ID == ID);
  },
  SignUp: (info) => {
    //검사하고 푸쉬
    db.User.push(info);
  },

  AllUser: () => {
    // 테스트용
    db.User.map((user) => {
      console.log(user);
    });
  },

  //   {
  //     Title: "What is Node js?...",
  //     Desc: "Node js is .....",
  //     Writer: "zeta1232",
  //     id : db.Topic.length
  //   },
  Create: (info) => {
    // obj받아와서 id property추가 후 푸쉬
    info.id = db.Topic.length + 1;
    db.Topic.push(info);
  },
  FindPost: (id) => {
    return db.Topic.find((post) => post.id == id);
  },
  getIndex: (id) => {
    return db.Topic.findIndex((data) => data.id === id);
  },
  Delete: (id) => {
    return db.Topic.splice(id, 1);
  },
  Edit: (id, post) => {
    return (db.Topic[id] = post);
  },
  AllPost: () => {
    return db.Topic;
  },
};

app.js

import express from "express";
import path from "path";

const app = express();
const PORT = 3000;
const __dirname = path.resolve();

import index from "./router/index.js";
import login from "./router/auth.js";

import session from "express-session";
import sessionStore from "session-file-store";
const FileStore = sessionStore(session);
import passport from "./lib/passport.js";

app.set("views", __dirname + "/views");
app.set("view engine", "ejs");

app.use(
  session({
    secret: "sknfienf123",
    resave: false,
    saveUninitialized: true,
    store: new FileStore(),
  })
);

passport(app);

app.use(express.static("public"));
app.use(express.urlencoded({ extended: false }));

app.use("/", index);
app.use("/auth", login);

app.listen(PORT, () => {
  console.log(`http://localhost:${PORT}`);
});

passport.js

import passport from "passport";
import { Strategy } from "passport-local";
import model from "./model.js";

export default (app) => {
  app.use(passport.initialize());
  app.use(passport.session());

  passport.use(
    new Strategy(
      {
        usernameField: "id",
        passwordField: "pwd",
      },
      (id, pwd, done) => {
        const user = model.FindOne(id);
        if (user && user.PWD == pwd) {
          console.log("Login Success");
          return done(null, user);
        }
        console.log("invalid Userinfo");
        return done(null, false);
      }
    )
  );

  passport.serializeUser(function (user, done) {
    console.log(`Serialize`);
    done(null, user.ID);
  });

  passport.deserializeUser(function (ID, done) {
    const user = model.FindOne(ID);
    done(null, user);
  });
};

app.js가 중요한게아니라 passport.js가 중요합니다.

passport.js 해석

router.post(
  "/login",
  passport.authenticate("local", {
    failureRedirect: "/auth/login",
  }),
  function (req, res) {
    req.session.save(function () {
      res.redirect("/main");
    });
  }
);

실패했을때는 로그인 페이지로 이동시키고
성공하면 세션 저장 후 /main으로 리다이렉트 시킵니다.

passport.authenticate 함수가 실행되면

Strategy 의 두번째인자인 콜백함수가 실행됩니다.

첫번쨰 인자로

{
	usernameField: "id",
	passwordField: "pwd",
},

이렇게 생긴걸 넘겨주는데 이건

<input type='text' name="id">
<input type='text' name="pwd">

name의 이름을 넘겨줘야합니다.

이런 passport.js나 다른 express의 미들웨어들을

배울때마다 느끼는거지만 이해할필요가없는거같습니다.

이해가아니라 그냥 문법중 하나라고 생각하고 넘기는거지

이게 뭘까 뭘까 하면서 자꾸 생각하면 길을 잃어버리게됩니다.

때문에 중요한 포인트 몇가지만 알려드리겠습니다.

두번째 콜백함수

(id,pwd,done) => {
 여기서 id pwd는 받아온 아이디와 패스워드입니다
 여기서 mongoose를 사용한다면 로그인 성공 실패처리를
 하면되는데 done함수를 통해서 성공/실패 처리를 할수있습니다.
 성공 : done(null, 유저아이디)
 실패 : done(null , false)
}

serializeUser,deserializeUser

시리얼라이즈 : 로그인 성공시 한번 실행됨
디시얼라이즈 : 유저정보를 찾을때 실행됨

역할은 이와 같습니다.

성공했을때 done의 두번째 인자로 준 유저의 아이디가

시리얼라이즈에게 넘겨주는데 이걸 또

디시리얼라이즈에게 넘겨줍니다.

뭔가 이상하죠 저도 그랬습니다. 제

  passport.serializeUser(function (user, done) {
    console.log(`Serialize`);
    done(null, user.ID);
  });

  passport.deserializeUser(function (ID, done) {
    const user = model.FindOne(ID);
    done(null, user);
  });

시리얼라이즈의 done을 통해서 디시리얼라이즈에게 가는데

왜 굳이 이렇게 할까요 왜냐면 시리얼라이즈는 디시리얼라이즈에게 넘겨줄

정보를 세션에 저장하기때문입니다.

만약에 유저정보를 그대로 세션에 저장해버리면

혹시라도 세션이 복호화된다면 아이디 비밀번호가 모두 털릴테고

그걸 고려하지않더라도 디시리얼라이즈가 존재하는데 굳이 모든 유저정보를

세션에 저장할필요가없습니다.

index.js

import express from "express";
import model from "../lib/model.js";

router.get("/main", (req, res) => {
  if (!req.user) return res.redirect("/auth/login");
  const name = req.user.Name;
  const Topic = model.AllPost();
  res.render("home/home", { name: name, Topic: Topic });
});

// router.get("/create", (req, res) => {});

export default router;

passport가 로그인에 성공하면
request 객체에 user라는 property를 추가해주니까
만약에 user가 비어있으면 로그인을 성공하지못했다는거죠

if (!req.user) return res.redirect("/auth/login");

이렇게 튕겨줍니다.

접근제한

로그인까진 어찌저찌했지만
비공개글이나 수정,삭제는 글의 작성자인지 아닌지 확인을 하고 페이지를 띄워줘야합니다

const isWriter = (req) => {
  let ID;
  try {
    ID = req.user.ID;
  } catch {
    return false;
  }
  const paramID = req.params.id;
  const post = model.FindPost(paramID);
  if (post && post.Writer == ID) return post;
  else if (post == undefined) return undefined;
  return false;
};

const invalidAccess = (res, text) => {
  res.send(`<script>alert('${text}'); history.back(0);</script>`);
};

router.get("/edit/:id", (req, res, next) => {
  // 1.만약에 유저가 쓴 글이면 수정페이지를 띄움
  // 2.유저가 쓴 글이 아니라면 튕겨냄
  // 3. id를 통해 찾는 글이 존재하지않으면 존재하지않는 페이지라고 해줌
  const post = isWriter(req);
  if (post)
    return res.render("edit.ejs", { Title: post.Title, desc: post.Desc });
  post == undefined
    ? invalidAccess(res, "존재하지않는 페이지입니다")
    : invalidAccess(res, "접근할수없습니다.");
});

router.delete("/delete/:id", (req, res, next) => {
  const post = isWriter(req);
  if (post) {
    const idx = model.getIndex(post.id);
    model.Delete(idx);
    return res.json({ message: "삭제" });
  }
  res.json({ message: "접근할수없습니다." });
});

isWriter함수로 글의 작성자인지,글의 id가 유효한지 확인할수있습니다.

https://github.com/leejw05/Learning-to-passport.git

profile
배운거 정리해요

0개의 댓글