[Node.js]auth-session

이정원·2022년 8월 16일
0

초기세팅

.env.example파일을 .env파일로 바꿔주고 안의 비밀번호를 초기에 설정한 값으로 세팅을 해준다.
.env파일에 적혀있는 이름대로 데이터베이스를 생성해준다.
mkcert로 받은 인증서를 server폴더에 복사한다.

서버구현

우선 CORS 및 세션 옵션을 설정해준다.
index.js

const express = require('express');
const cors = require('cors');
const session = require('express-session');
const logger = require('morgan');
const fs = require('fs');
const https = require('https');
const usersRouter = require('./routes/user');

const app = express();

const FILL_ME_IN = 'FILL_ME_IN';

const PORT = process.env.PORT || 4000;

// TODO: express-session 라이브러리를 이용해 쿠키 설정을 해줄 수 있습니다.
app.use(
  session({
    secret: '@codestates',
    resave: false,
    saveUninitialized: true,
    cookie: {
      domain: 'localhost',
      path: '/',
      maxAge: 24 * 6 * 60 * 10000,
      sameSite: 'none',
      httpOnly: 'true',
      secure: 'true',
    },
  })
);
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

// TODO: CORS 설정이 필요합니다. 클라이언트가 어떤 origin인지에 따라 달리 설정할 수 있습니다.
// 메서드는 GET, POST, OPTIONS를 허용합니다.
app.use(cors());
/**
 * /users 요청에 대해서 라우터를 이용하기 때문에,
 * 반드시 아래와 같은 주소와 메서드로 요청을 보내야 합니다.
 *
 * POST https://localhost:4000/users/login,
 * POST https://localhost:4000/users/logout,
 * GET https://localhost:4000/users/userinfo
 */
app.use('/users', usersRouter);

let server;

// 인증서 파일들이 존재하는 경우에만 https 프로토콜을 사용하는 서버를 실행합니다.
// 만약 인증서 파일이 존재하지 않는경우, http 프로토콜을 사용하는 서버를 실행합니다.
// 파일 존재여부를 확인하는 폴더는 서버 폴더의 package.json이 위치한 곳입니다.
if (fs.existsSync("./key.pem") && fs.existsSync("./cert.pem")) {
  server = https
    .createServer(
      {
        key: fs.readFileSync(__dirname + `/` + 'key.pem', 'utf-8'),
        cert: fs.readFileSync(__dirname + `/` + 'cert.pem', 'utf-8'),
      },
      app
    )
    .listen(PORT);
} else {
  server = app.listen(PORT)
}
module.exports = server;

일치하는 유저가 있을경우 세션에 정보를 저장하고, 그렇지 않은경우 요청을 거절한다고 하였으니, db로 부터 받은 userInfo가 일치하는지 일치하지 않는지 조건문을 통해 작성하면 된다. 또한 세션에 저장시 req.session.save메소드를 통하여 저장할 수 있다.
login.js

// 해당 모델의 인스턴스를 models/index.js에서 가져옵니다.
const { Users } = require("../../models");

module.exports = {
    post: async (req, res) => {
        // userInfo는 유저정보가 데이터베이스에 존재하고, 완벽히 일치하는 경우에만 데이터가 존재합니다.
        // 만약 userInfo가 NULL 혹은 빈 객체라면 전달받은 유저정보가 데이터베이스에 존재하는지 확인해 보세요
        const userInfo = await Users.findOne({
            where: { userId: req.body.userId, password: req.body.password },
        });

        // TODO: userInfo 결과 존재 여부에 따라 응답을 구현하세요.
        // 결과가 존재하는 경우 세션 객체에 password를 제외한 유저 정보가 저장되어야 합니다.
        if (!userInfo) {
            res.status(404).send({message: 'not authorized'})
            // your code here
        } else {
            req.session.save(function(){
                req.session.userId = userInfo.userId;
                res.json({data: userInfo, message: 'ok'})
            })
            // your code here
            // HINT: req.session을 사용하세요.
        }
    },
};

세션에 저장한 값이 존재하면 세션을 삭제하는 코드를 작성하면 된다.
logout.js

module.exports = {
  post: (req, res) => {

    // TODO: 세션 아이디를 통해 고유한 세션 객체에 접근할 수 있습니다.
    // 앞서 로그인시 세션 객체에 저장했던 값이 존재할 경우, 이미 로그인한 상태로 판단할 수 있습니다.
    // 세션 객체에 담긴 값의 존재 여부에 따라 응답을 구현하세요.

    if (!req.session.userId) {
      res.status(400).send();
      // your code here
    } else {
      res.status(200).send();
      req.session.destroy();
      // your code
      // TODO: 로그아웃 요청은 세션을 삭제하는 과정을 포함해야 합니다.
    }
  },
};

세션에 저장한 값이 존재하면 조회 후 요청에 응답으로 전달하고, 그렇지 않으면 요청을 거절한다.
userinfo.js

const { Users } = require("../../models");

module.exports = {
    get: async (req, res) => {
        // TODO: 세션 객체에 담긴 값의 존재 여부에 따라 응답을 구현하세요.
        // HINT: 세션 객체에 담긴 정보가 궁금하다면 req.session을 콘솔로 출력해보세요

        if (!req.session.userId) {
            res.status(400).send({message: 'not authorized'})
            // your code here
        } else {
            const result = await Users.findOne({
                where: { userId: req.session.userId },
            });
            res.status(200).json({data: result, message: 'ok'})
            // your code here
            // TODO: 세션 객체에 저장되어 있는 사용자 정보를 반환합니다.
        }
    },
};

클라이언트 구현

클라이언트를 구현하기에 앞서 axios라는 생소한 api를 접하게 되었다. axios는 promis기반의 http클라이언트로써 요청한 get, post를 전송하고 핸들링 하는 api이다.

공식문서 axios

axios를 통하여 Login.js와 MyPage.js를 구현해보자
공식문서를 참고하면 복잡하지 않게 작성해 볼 수 있다.
Login.js

import React, { Component } from 'react';
import axios from 'axios';

class Login extends Component {
  constructor(props) {
    super(props);
    this.state = {
      username: '',
      password: '',
    };
    this.inputHandler = this.inputHandler.bind(this);
    this.loginRequestHandler = this.loginRequestHandler.bind(this);
  }

  inputHandler(e) {
    this.setState({ [e.target.name]: e.target.value });
  }

  loginRequestHandler() {
    // TODO: 로그인 요청을 보내세요.
    //
    // 로그인에 성공하면
    // - props로 전달받은 함수를 호출해, 로그인 상태를 변경하세요.
    // - GET /users/userinfo 를 통해 사용자 정보를 요청하세요
    //
    // 사용자 정보를 받아온 후
    // - props로 전달받은 함수를 호출해, 사용자 정보를 변경하세요.
    axios({
      url: 'https://localhost:4000/users/login', 
      method: 'post',
      data: {
        userId: `${this.state.username}`,
        password: `${this.state.password}`,
      },
    }).then((res) => {
      this.props.loginHandler();
      axios.get('https://localhost:4000/users/userinfo',{
        withCredentials: true,
        
      })
      this.props.setUserInfo(res.data.data);
      
    });
  }

  render() {
    return (
      <div className='loginContainer'>
        <div className='inputField'>
          <div>Username</div>
          <input
            name='username'
            onChange={(e) => this.inputHandler(e)}
            value={this.state.username}
            type='text'
          />
        </div>
        <div className='inputField'>
          <div>Password</div>
          <input
            name='password'
            onChange={(e) => this.inputHandler(e)}
            value={this.state.password}
            type='password'
          />
        </div>
        <div className='passwordField'>
          <button onClick={this.loginRequestHandler} className='loginBtn'>
            Login
          </button>
        </div>
      </div>
    );
  }
}

export default Login;

MyPage.js

import React from 'react';
import axios from 'axios';


function Mypage(props) {
  const handleLogout = () => {
    // TODO: 서버에 로그아웃 요청을 보낸다음 요청이 성공하면 props.logoutHandler를 호출하여 로그인 상태를 업데이트 해야 합니다.
    axios
    .post('https://localhost:4000/users/logout', {
      withCredentials: true,
    })
    .then(() => {
      props.logoutHandler();
    })
  };
  return props.userData === null ? (
    <div>Loading...</div>
  ) : (
      <div>
        <div className='mypageContainer'>
          <div>
            <span className='title'>Mypage</span>
            <button className='logoutBtn' onClick={handleLogout}>
              logout
            </button>
          </div>
          <hr />

          <div>
            안녕하세요. <span className='name'>{props.userData.userId}</span>님! 로그인이 완료되었습니다.
          </div>
          <br />
          <div className='item'>
            나의 유저 네임: {props.userData.userId}
          </div>
          <div className='item'>
            나의 이메일 주소: {props.userData.email}
          </div>
        </div>
      </div>
    );
}

export default Mypage;
profile
Study.log

0개의 댓글