SOPT : 6차 세미나 - restAPI, jwt

김가영·2020년 11월 21일
0

Node.js

목록 보기
20/34
post-thumbnail

추가자료 - neity16 velog

jwt

authentication

API 요청에 대해 사용 가능한 사용자인지 확인하는 절차

HTTP 특징

  • 비연결성 : 한 번 클라와 서버가 연결을 맺은 후, 서버가 응답을 마치면 연결이 끝남
  • 무 상태(state less) 비연결성으로 인해 서버는 클라이언트를 식별할 수 없음

HTTP에서 상태를 기억하는 방법

key - value 쌍으로 구성된, 사용자에 컴퓨터에 저장되는 작은 기록 정보 파일

세션

일정 시간동안 같은 사용자로부터 들어오는 일련의 요구를 하나의 상태로 보고, 그 상태를 일정하게 유지시키는 기술

쿠키 세션의 문제점

쿠키는 클라이언트 로컬에 저장되기 때문에 정보가 변질되거나 스나이핑 당할 수 있음
세션은 서버에서 세션 저장소를 사용하므로 추가적인 저장공간이 필요. 세션 저장소에 장애가 발생하면 인증에 문제

JWT

Json Web Token
클레임 토큰 기반 인증방식.
클라이언트의 세션 상태를 저장하는 것이 아니라 필요한 정보를 토큰 body에 저장해서 클라이언트가 가지고 이를 증명서처럼 사용

JWT 구성

Header.Payload.Verify_Signature
Header : 서명에 사용된 알고리즘의 정보
Payload : 토큰의 정보를 작성, 클레임이라고 표현- 클라이언트에 대한 정보가 담겨 있음.
Signature : 토큰이 중간에 변경되지 않았다는 것을 검증 - header에서 지정한 알고리즘과 secret key로 header와 payload를 담는다.

npm install jsonwebtoken

  • config/secretKey.js
module.exports = {
    secretKey: 'SecretKeySoptSeRvER',
    options: {
        algorithm: "HS256",
        expiresIn: "1h",
        issuer: "kgy",
    },
    refreshOptions: {
        algorithm: "HS256",
        expiresIn: "7d",
        issuer: "kgy",
    },

}
  • module/jwt.js
const jwt = require('jsonwebtoken');
const { secretKey, options, refreshOptions } = require('../config/secretKey');
const TOKEN_EXPIRED = -3;
const TOKEN_INVALID = -2;

module.exports = {
  // jwt 생성
  sign: async (user) => {
    const payload = {
      id: user.id,
      name: user.userName
    };
    const result = {
      accessToken: jwt.sign(payload, secretKey, options),
      refreshToken: jwt.sign(payload, secretKey, refreshOptions),
    };
    return result;
  },
  // jwt 식별
  verify: async (token) => {
    let decoded;
    try {
      decoded = jwt.verify(token, secretKey);
    } catch (err) {
      if (err.message === 'jwt expired') {
        console.log('expired token');
        return TOKEN_EXPIRED;
      } else if (err.message === 'invalid token') {
        console.log('invalid token');
        console.log(TOKEN_INVALID);
        return TOKEN_INVALID;
      } else {
        console.log("invalid token");
        return TOKEN_INVALID;
      }
    }
    return decoded;
  }
}
  • userController.js
signin : async (req, res) => {
        const { email, password } = req.body; // 1. req.body에서 데이터 가져오기
        //2. request data 확인하기, email, password, userName data가 없다면 NullValue 반환
        if (!email || !password) {
            console.log('필요한 값이 없습니다!');
            return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, responseMessage.NULL_VALUE));
        }
        try{
            //3. 존재하는 아이디인지 확인하기. 존재하지 않는 아이디면 NO USER 반환
            const user = await User.findOne({
                where : {
                    email : email
                },
            });
            //4. 비밀번호 확인하기 - 로그인할 email의 salt를 DB에서 가져와서 사용자가 request로 보낸 password와 암호화를 한후 디비에 저장되어있는 password와 일치하면 true
            // 일치하지 않으면 Miss Match password 반환
            const {id, userName, salt, password : hashedPassword} = user;
            const inputPassword = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('base64');
            
            if (inputPassword != hashedPassword) {
                console.log('비밀번호가 일치하지 않습니다.');
                return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.OK, responseMessage.MISS_MATCH_PW));
            }
            const {accessToken, refreshToken} = await jwt.sign(user);

            //5. status: 200 ,message: SIGN_IN_SUCCESS, data: id, email, userName 반환
            return res.status(statusCode.OK).send(util.success(statusCode.OK, responseMessage.SIGN_IN_SUCCESS, {accessToken, refreshToken}));
        } catch (error) {
            console.error(error);
            return res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, responseMessage.SIGN_IN_FAIL));
        }
    },

middle ware


const express = require('express');
const router = express.Router();
const ut = require('../modules/util');
const sc = require('../modules/statusCode');
const rm = require('../modules/responseMessage');
const jwt = require('../modules/jwt');
const TOKEN_EXPIRED = -3
const TOKEN_INVALID = -2

const authUtil = {
    checkToken : async (req, res, next) => {
        const token = req.headers.jwt;
        if (!token) {
          return res.json(ut.fail(sc.BAD_REQUEST, rm.EMPTY_TOKEN));
        }
        const user = await jwt.verify(token);
        console.log('hello', user);
        if (user === TOKEN_EXPIRED) {
          return res.status(sc.UNAUTHORIZED).send(ut.fail(sc.UNAUTHORIZED, rm.EXPIRED_TOKEN));
        }
        if (user === TOKEN_INVALID) {
          return res.status(sc.UNAUTHORIZED).send(ut.fail(sc.UNAUTHORIZED, rm.INVALID_TOKEN));
        }
        if (user.id === undefined) {
          return res.status(sc.UNAUTHORIZED).send(ut.fail(sc.UNAUTHORIZED, rm.INVALID_TOKEN));
        }
        req.decoded = user;
        next();
      },
}


module.exports = authUtil;

REST API

  • URI : Uniform Resource Indentifier 인터넷에 있는 자원을 나타내는 유일한 주소
  • URL : Uniform Resource Locator 네트워크 상에서 자원이 어디있는지 알려주기 위한 규약
    uri 가 url을 포함하는 개념

REST API

: REpresentational State Transfer API

구성

자원 RESOURCE - URI
행위 : - HTTP METHOD
표현(Representations)

REST API 디자인 가이드

1. URI 는 정보의 자원을 표현하여야 한다.

리소스명은 동사보다는 명사

GET/members/show x
GET/hahaha x
GET/members o

2. 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE)

REST API 주의사항

1. /(슬래시)는 계층관계를 나타나는 데 사용

2. URI 마지막 문자로 슬래시를 포함하지 않는다.

3. -(하이픈)은 URI 가독성을 높이는 데 사용한다

4. _ (밑줄)은 URI에 사용하지 않는다

5. URI 경로에는 소문자가 적합

6. 파일 확장자는 URI 에 포함시키지 않는다.

GET/sopt/server.png x
GET/sopt/server/png o

7. 컬렉션은 복수, 도큐먼트는 단수로 표현

document는 하나의 객체(문서), collection은 그 객체들의 집합

http://sopt.org/parts/server/users/2

API 명세서

API: Application Programming Interface

클라이언트가 서버에서 만든 API 를 사용하기 위해 참고하는 사용 설명서

API 명세서 구성 요소

  • API 이름
  • HTTP METHOD
  • Content-Type
  • 요청 헤더/바디
  • 응답 바디

CORS

  • Cross Origin Response Sharing

    schema, host, port 세가지가 모두 같으면 same-origin, 하나라도 다르면 cross-origin

web, server의 서버가 다르면 오류

npm install cors

-app.js에서

const cors = require('cors');
app.use(cors());
profile
개발블로그

0개의 댓글