๐ŸŒฑ → ๐ŸŒณ

[node.js] passport(local ๋กœ๊ทธ์ธ, ๋กœ๊ทธ์•„์›ƒ ๊ตฌํ˜„) ๋ณธ๋ฌธ

Server/Node.js

[node.js] passport(local ๋กœ๊ทธ์ธ, ๋กœ๊ทธ์•„์›ƒ ๊ตฌํ˜„)

BAY 2022. 9. 24. 18:14
728x90

๐Ÿ“Œ passport

: ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๋‹ค๋ฐฉ๋ฉด์œผ๋กœ ๋„์™€์ฃผ๋Š” ๋ชจ๋“ˆ 

google, facebook, github ๋“ฑ ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„์„ ์œ„ํ•œ ๊ธฐ๋Šฅ ์ œ๊ณต

 

โœ… passport ์„ค์น˜

์†Œ์…œ๋กœ๊ทธ์ธ์ด ์•„๋‹Œ local ๋ฐฉ์‹ ๊ตฌํ˜„์„ ์œ„ํ•ด passport-local๋„ ๊ฐ™์ด ์„ค์น˜ 

npm i passport
npm i passport-local

 

์„œ๋ฒ„ ์ฝ”๋“œ์— ๋ชจ๋“ˆ ์ถ”๊ฐ€(passport: ์„ธ์…˜์„ ์‚ฌ์šฉ)

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

// Session
app.use(
  session({
    secret: 'bay',
    resave: false,
    saveUninitialized: true,
    cookie: {
      maxAge: 1000 * 60 * 60,
    },
  })
);

// Passport
app.use(passport.initialize());
app.use(passport.session());

 

โœ… passport, local ์ „๋žต ์„ค์ • 

  • passport๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋กœ๊ทธ์ธ์„ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ• ์ง€ ๋ฏธ๋ฆฌ ์ •ํ•ด๋†“์Œ
  • ์‹ค์ œ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋Š” passport.authenticate ๋ฉ”์†Œ๋“œ ์ด์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌ
  • ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์–ด๋–ค ํ‚ค ๊ฐ’์œผ๋กœ ๋ฐ›์„์ง€ ์„ค์ •
  • ์ธ์ฆ ์ ˆ์ฐจ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜ ์„ค์ •
  • ์ธ์ฆ ์ ˆ์ฐจ ์ˆ˜ํ–‰์˜ ๊ฒฐ๊ณผ๋Š” callback์— ๋‹ด์•„์„œ ์ฒ˜๋ฆฌ 

id, pw๋กœ ์‚ฌ์šฉํ•  ํ‚ค ๊ฐ’ ์„ค์ •

new LocalStrategy(
      {
        usernameField: 'id',
        passwordField: 'password',
      },

 

์‹ค์ œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๋ถ€๋ถ„

async (id, password, cb) => {
// ์„œ๋ฒ„์— ์œ ์ € ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•˜์—ฌ id ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
const client = await mongoClient.connect();
const userCursor = client.db('db๋ช…').collection('users');
const idResult = await userCursor.findOne({ id });
// id๊ฐ€ ์กด์žฌํ•˜๋ฉด ๋น„๋ฐ€๋ฒˆํ˜ธ ๊นŒ์ง€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ
if (idResult !== null) {
  const result = await userCursor.findOne({
    id,
    password,
  });
  // ๋น„๋ฐ€๋ฒˆํ˜ธ ๊นŒ์ง€ ์ผ์น˜ํ•˜๋ฉด ์ฝœ๋ฐฑ ํ•จ์ˆ˜์— ์ฐพ์€ ์œ ์ € ์ •๋ณด๋ฅผ ์ „๋‹ฌ
  if (result !== null) {
    cb(null, result);
  } else {
    // ๊ฐ๊ฐ ์ƒํ™ฉ์— ๋งž๋Š” ์—๋Ÿฌ ๋ฉ”์„ธ์ง€ ์ „๋‹ฌ
    cb(null, false, { message: '๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.' });
  }
} else {
  cb(null, false, { message: 'ํ•ด๋‹น id ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.' });
}
}

 

์ „์ฒด ์ฝ”๋“œ

passport.use(
  new LocalStrategy(
    {
      usernameField: 'id',
      passwordField: 'password',
    },
    async (id, password, cb) => {
      const client = await mongoClient.connect();
      const userCursor = client.db('kdt1').collection('users');
      const idResult = await userCursor.findOne({ id });
      if (idResult !== null) {
        const result = await userCursor.findOne({
          id,
          password,
        });
        if (result !== null) {
          cb(null, result);
        } else {
          cb(null, false, { message: '๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.' });
        }
      } else {
        cb(null, false, { message: 'ํ•ด๋‹น id ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.' });
      }
    }
  )
);

 

โœ… passport, serialzeUser

  • passport๋„ session์„ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ์—ฌ๋ถ€ ํŒ๋‹จ
  • ์œ„ ์ฝ”๋“œ์—์„œ callbackํ•จ์ˆ˜์— ๋‹ด๊ธด ๋‚ด์šฉ์€ serialzeUser ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ›์•„์„œ ์„ธ์…˜์„ ๋งŒ๋“ค์–ด ์ „๋‹ฌ ๋ฐ›์€ ์œ ์ € ์ •๋ณด๋ฅผ ์„ธ์…˜์— ์ €์žฅ
  • ํ•ด๋‹น ์„ธ์…˜์€ req.user๋กœ ์ ‘๊ทผํ•˜์—ฌ ์ •๋ณด ํ™•์ธ ๊ฐ€๋Šฅ
  • ์ฝœ๋ฐฑ ํ•จ์ˆ˜์— ์˜ํ•ด user.id ๋ถ€๋ถ„์ด ์„ธ์…˜์— ์ €์žฅ๋˜๋Š” ์ฝ”๋“œ 
passport.serializeUser((user, cb) => {
  cb(null, user.id);
});

 

โœ… passport, deserializeUser

  • passport๋Š” ๋” ๊นŒ๋‹ค๋กญ๊ฒŒ ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค๋ฅธ ํŽ˜์ด์ง€์— ์ ‘์†ํ•˜๋ ค๊ณ  ํ•  ๋•Œ์—๋„ ๊ณ„์† ์ธ์ฆ์„ ์š”๊ตฌ
  • ํ•ด๋‹น ๊ธฐ๋Šฅ์„ deserializeUser๊ฐ€ ์ˆ˜ํ–‰
  • session์— ์ €์žฅ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ํ†ตํ•ด์„œ db์— ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ œ๋กœ ์žˆ๋Š”์ง€ ๊พธ์ค€ํžˆ ํ™•์ธํ•˜๋Š” ๊ณผ์ • ๊ฑฐ์นจ
passport.deserializeUser(async (id, cb) => {
  const client = await mongoClient.connect();
  const userCursor = client.db('db๋ช…').collection('users');
  const result = await userCursor.findOne({ id });
  if (result) cb(null, result);
});

 

๐Ÿ”

session -> ๋กœ๊ทธ์ธ์ด ๋˜๋ฉด ์ดํ›„๋Š” ๊ณ„์† ๋ฏฟ๊ณ  ๊ฐ

passport -> ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ๋Š”์ง€ ๊พธ์ค€ํžˆ ์ฒดํฌ ๋ฐ ์œ ์ € ํ™•์ธ

 

โœ… passport ๋กœ๊ทธ์ธ

passport.authenticate ๋ฉ”์†Œ๋“œ๊ฐ€ ์ฒ˜๋ฆฌ

passport.authenticate('local', (err, user, info) => {

์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ : err ๋งค๊ฐœ๋ณ€์ˆ˜์— ์—๋Ÿฌ ๊ฐ’์ด ๋‹ด๊น€

๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ: user์— ์ „๋‹ฌํ•œ user ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ์„œ ๋„˜์–ด์˜ด

๋กœ๊ทธ์ธ ์‹คํŒจ ์‹œ: user์€ null๋กœ ๋ฆฌํ„ด๋˜๋ฉฐ, ์ „๋‹ฌํ•œ ๋ฉ”์‹œ์ง€๋Š” info ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ์ฒด์— ๋‹ด๊ฒจ์„œ ์ „๋‹ฌ๋จ

 

๋งค๊ฐœ๋ณ€์ˆ˜ ์‚ฌ์ง„ ์„ค๋ช…

 

router.post('/', (req, res, next) => {
  passport.authenticate('local', (err, user, info) => {
    if (err) next(err);
    if (!user) {
      return res.send(
        `${info.message}<br><a href="/login">๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™</a>`
      );
    }
    req.logIn(user, (err) => {
      if (err) next(err);
      res.redirect('/board');
    });
  })(req, res, next);
});

 

โœ… passport ๋กœ๊ทธ์•„์›ƒ

req.logout ๋ฉ”์†Œ๋“œ ์‚ฌ์šฉ 

router.get('/logout', (req, res, next) => {
  req.logout((err) => {
    if (err) {
      return next(err);
    }
    return res.redirect('/');
  });
});

 

โœ… ๋กœ๊ทธ์ธ ์—ฌ๋ถ€์— ๋”ฐ๋ฅธ ๊ฒŒ์‹œํŒ ์„œ๋น„์Šค ๋ณ€๊ฒฝ 

passport์— ์˜ํ•ด login๋œ ์œ ์ € session์€ req.user์— ๋‹ด๊ธฐ๊ฒŒ ๋˜๋ฏ€๋กœ isLogin ํ•จ์ˆ˜์— ์กฐ๊ฑด ์ถ”๊ฐ€ 

function isLogin(req, res, next) {
  if (req.session.login || req.user) {
    next();
  } else {
    res.send('๋กœ๊ทธ์ธ ํ•ด์ฃผ์„ธ์š”.<br><a href="/login">๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™</a>');
  }
}

 

ํšŒ์› ์•„์ด๋”” ๊ฐ’์ด req.session.userId๋ฟ๋งŒ์•„๋‹ˆ๋ผ, req.user.id๋„ ์ƒ๊ฒผ์œผ๋ฏ€๋กœ ํ•ด๋‹น ๋ถ€๋ถ„๋„ ์ˆ˜์ •

 ๊ฒŒ์‹œํŒ ๋ Œ๋”๋ง ๋ถ€๋ถ„ ์ˆ˜์ • 

// ๊ฒŒ์‹œํŒ ์ฒ˜์Œ ๋ Œ๋”๋ง ํ•  ๋•Œ 
router.get('/', isLogin, async (req, res) => {  
  const client = await mongoClient.connect();
  const cursor = client.db('kdt1').collection('board');
  const ARTICLE = await cursor.find({}).toArray();
  
  const articleLen = ARTICLE.length;
  res.render('board', {
    ARTICLE,
    articleCounts: articleLen,
    userId: req.session.userId ? req.session.userId : req.user.id,
  });  
});

 ๊ฒŒ์‹œํŒ ๊ธ€์“ฐ๊ธฐ ๋ถ€๋ถ„ ์ˆ˜์ •

router.post('/', isLogin, async (req, res) => {
  if (req.body) {
    if (req.body.title && req.body.content) {
      const newArticle = {
        id: req.session.userId ? req.session.userId : req.user.id,
        title: req.body.title,
        content: req.body.content,
      };
      
      const client = await mongoClient.connect();
      const cursor = client.db('kdt1').collection('board');
      await cursor.insertOne(newArticle);
      res.redirect('/board');
    } else {
      const err = new Error('์š”์ฒญ ์ด์ƒ');
      err.statusCode = 404;
      throw err;
    }
  } else {
    const err = new Error('์š”์ฒญ์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค');
    err.statusCode = 404;
    throw err;
  }
});

 

๐Ÿ‘Š๐Ÿป ์„œ๋ฒ„ ์ฝ”๋“œ ์ •๋ฆฌ (LocalStrategy ๋ชจ๋“ˆํ™”)

localStratege.js ํŒŒ์ผ ์ƒ์„ฑ 

-> passport, LocalStrategy ๋ชจ๋“ˆ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

-> module.exports๋ฅผ ํ•˜๋‚˜์˜ ์ต๋ช… ํ•จ์ˆ˜๋กœ ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌ

-> serialzeUser, deserialzeUser ๋„ ๊ฐ™์ด ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌ

 

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

const verifyModule = require('./register').verifyPassword;

const mongoClient = require('./mongo');

// ์ต๋ช…ํ•จ์ˆ˜ ์ „๋‹ฌ
module.exports = () => {
  passport.use(
    new LocalStrategy(
      {
        usernameField: 'id',
        passwordField: 'password',
      },
      async (id, password, cb) => {
        const client = await mongoClient.connect();
        const userCursor = client.db('node1').collection('users');
        // {id : id} = {id}
        const idResult = await userCursor.findOne({ id });
        // console.log(idResult);
        if (idResult !== null) {
          if (idResult.salt !== undefined) {
            const pwResult = verifyModule(
              password,
              idResult.salt,
              idResult.password
            );
            if (pwResult) {
              cb(null, idResult);
            } else {
              cb(null, false, { message: '๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค.' });
            }
          } else if (idResult.password === password) {
            cb(null, idResult);
          } else {
            cb(null, false, { message: '๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค.' });
          }
        } else {
          cb(null, false, { message: 'ํ•ด๋‹น id๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.' });
        }
      }
    )
  );

  // idResult -> user
  passport.serializeUser((user, cb) => {
    cb(null, user.id);
  });

  // ํ†ต์‹ ์„ ํ•ด์•ผํ•˜๋‹ˆ๊นŒ async์‚ฌ์šฉ
  passport.deserializeUser(async (id, cb) => {
    const client = await mongoClient.connect();
    const userCursor = client.db('node1').collection('users');
    const result = await userCursor.findOne({ id });
    if (result !== null) cb(null, result);
  });

 

๋ฉ”์ธ ์„œ๋ฒ„ ์ˆ˜์ • 

const localStrategy = require('./routes/localStrategy’);
localStrategy();
728x90