NEXTJSを使用してTwitterをクローンする(ユーザーpassportを使用してログイン)


設定

npm i passport passport-local express-session
passportは名前のように、自分のサイトにアクセスする際にパスポートなどの役割を果たします.
passport-ローカルでログインを直接実施します.
express-sessionはpassportを使用してログインし、ユーザー情報をセッションに保存します.

passport設定


passport/local.js
const passport = require("passport");
const { Strategy: LocalStrategy } = require("passport-local");
const bcrypt = require("bcrypt");
const { User } = require("../models");

module.exports = () => {
  passport.use(
    new LocalStrategy(
      {
        usernameField: "email",
        passwordField: "password",
      },
      async (email, password, done) => {
        try {
          const user = await User.findOne({
            where: { email },
          });
          if (!user) {
            return done(null, false, { reason: "존재하지 않는 이메일입니다!" });
          }
          const result = await bcrypt.compare(password, user.password);
          if (result) {
            return done(null, user);
          }
          return done(null, false, { reason: "비밀번호가 틀렸습니다." });
        } catch (error) {
          console.error(error);
          return done(error);
        }
      }
    )
  );
};
usernameFieldと passwordFieldは、どのフォームフィールドからアイデンティティとパスワードを受信するかを設定するオプションです.上の場合はbodyのデータです  { email: 'JaeHoon', password: '123' }  これにより,後のコールバック関数のemail値はJaeHoon,password値は123となる.
Eメールとパスワードの値が受信された場合は、コールバック関数が実行され、その内容を表示すると、Eメールでユーザーが見つかり、ユーザーが存在しない場合はエラーメッセージが送信されます.
userが存在する場合は、パスワードbcryptを使用します.と呼びます.パスワードが一致しない場合は、パスワードが間違っていることを示すエラーメッセージが送信されます.
パスワードが正しい場合はdone(null,user)を返します.はい.
doneパラメータは3つのパラメータを受け入れます.
1つ目:データベース・クエリーなどの場合にサーバにエラーを追加します.無条件に失敗した場合にのみ使用します.
2つ目:成功したときに返される値を入力します.
3つ目:パスワードエラーを表示したい場合に使用できます.これはサーバエラーではなく、ユーザーがランダムに作成したエラーなので、ユーザーに直接エラーメッセージを書きます.

passport/index.js

const passport = require("passport");
const local = require("./local");
const { User } = require("../models");

module.exports = () => {
  passport.serializeUser((user, done) => {
    done(null, user.id);
  });

  passport.deserializeUser(async (id, done) => {
    try {
      const user = await User.findOne({ where: { id } });
      done(null, user); // req.user
    } catch (error) {
      console.error(error);
      done(error);
    }
  });

  local();
};
serializeUserはログインが成功したばかりの時に実行されます  done(null, user);からユーザオブジェクトを受信セッション(正確には、  req.session.passport.user). セッションがある場合は、ページの移動時にログイン情報が変更されない可能性があります.非user.私があなたにidをあげたのはメモリを節約するためです.
逆シーケンス化ユーザセッション情報(SerializeUserに格納)と実際のデータベースのデータ
比較する.対応するユーザ情報がある場合、doneの第2のパラメータはreq.userに保存され、要求処理時にユーザの情報はreq.userを介して伝達される.
SerializeUserからdoneのユーザーに移動します.idは逆シーケンス化ユーザの最初のパラメータとして渡されるため、2つのタイプは常に一致しなければならない.

app.js

const express = require("express");
const cors = require("cors");
const session = require("express-session");
const cookieParser = require("cookie-parser");
const passport = require("passport");
const dotenv = require("dotenv");

const postRouter = require("./routes/post.js");
const userRouter = require("./routes/user.js");
const db = require("./models");
const passportConfig = require("./passport");

dotenv.config();
const app = express();
db.sequelize
  .sync()
  .then(() => {
    console.log("DB 연결 성공");
  })
  .catch(err => {
    console.error(err);
  });
passportConfig();
app.use(
  cors({
    origin: true,
    credentials: false,
  })
); // argumnet로 {origin: true}로 해주면 *대신 보낸곳의 주소가 자동으로 들어간다.
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(
  session({
    saveUninitialized: false,
    resave: false,
    secret: process.env.COOKIE_SECRET,
  })
);
app.use(passport.initialize());
app.use(passport.session());
app.get("/", (req, res) => {
  res.send("Hi");
});
app.use("/post", postRouter);
app.use("/user", userRouter);

app.listen(3065, () => {
  console.log("서버 실행 중");
});
中間セッションの設定  session()passport.initialize()passport.session()  この部分を忘れずに入れてください前にはpassportConfig()もあります.入れますよ

router/user.js

const express = require("express");
const bcrypt = require("bcrypt");
const passport = require("passport");
const { User } = require("../models");

const router = express.Router();
...
router.post("/login", (req, res, next) => {
  passport.authenticate("local", (err, user, info) => {
    if (err) {
      console.error(error);
      return next(error);
    }
    if (info) {
      //401은 허가되지않음
      return res.status(401).send(info.reason);
    }
    return req.login(user, loginError => {
      if (loginError) {
        console.log(loginError);
        return next(loginError);
      }
      return res.status(200).json(user);
    });
  })(req, res, next);
});

module.exports = router;
顧客がrouterのloginを介してpostリクエストを送信すると、passport.authenticateが実行されます.この操作を実行すると、前に作成したポリシーが実行されます.(passport/local.js)ポリシーがdone()を返すと、passport.authenticateのコールバック関数が実行されます.コールバック関数のパラメータにはdoneのような3つのパラメータが含まれ、3つのパラメータがコールバック関数のパラメータとしてdoneに送信される.
doneに最初のパラメータ値を入れるとreturn next(error);はい.
3番目のパラメータ値をdoneに入れるとres.status(401)が返されます.send(info.reason);はい.
userをdoneとして2番目のパラメータ値に渡すとreqが返されます.loginが実行されます.passport/indexも併用します.js上のpassport.SerializeUserが実行されます.このセッションのuser.idを保存します.
その後res.status(200)に戻ります.json(user)を介してクッキーとuserをフロントに送信する.
クッキーでサーバpassportから.逆シーケンス化されたユーザによって、対応するCookieのユーザに割り当てられる.idを見つけてuserを見つけてreq.ユーザー情報をuserに挿入します.

logout

const express = require("express");
const bcrypt = require("bcrypt");
const passport = require("passport");
const { User } = require("../models");

const router = express.Router();
...
router.post("logout", (req, res) => {
	req.logout();
	req.session.destroy();
	res.send("ok"); 
})

module.exports = router;

Front


Postリクエストをバックエンドに送信し、ユーザーに関する情報を取得します.
function logInAPI(data) {
  return axios.post("/user/login", data);
}

function* logIn(action) {
  try {
    console.log("saga Login");
    const result = yield call(logInAPI, action.data); //call fork 차이 fork는 비동기 call은 동기
    yield put({
      type: LOG_IN_SUCCESS,
      data: result.data,
    });
  } catch (err) {
    yield put({
      //put은 dispatch라고 생각하면 된다.
      type: LOG_IN_FAILURE,
      error: err.response.data,
    });
  }
}

function* watchLogIn() {
  yield takeLatest(LOG_IN_REQUEST, logIn);
}

export default function* userSaga() {
  yield all([
    fork(watchLogIn), 
    fork(watchLogOut),
    fork(watchSignUp),
    fork(watchFollow),
    fork(watchUnfollow),
  ]);
}
インポートしたユーザー情報をstateに配置します.
const reducer = (state = initialState, action) => {
  return produce(state, draft => {
    switch (action.type) {
      // LOG IN
      case LOG_IN_REQUEST:
        draft.logInLoading = true;
        draft.logInError = false;
        draft.logInDone = null;
        break;
      case LOG_IN_SUCCESS:
        draft.logInLoading = false;
        draft.logInDone = true;
        draft.me = action.data;
        break;
      case LOG_IN_FAILURE:
        draft.logInLoading = false;
        draft.logInError = action.error;
        draft.logInDone = false;
        break;
      ...
    }
  });
};