token jwt vs cookie session and next ssr, cors, credentials

반응형

 

쿠키란 클라이언트 측에서는 Cookie, 서버 측에서는 Set-Cookie 라는 헤더의 값을 통해 클라이언트와 서버가 정보를 전달하는 방식이다.

Authentication 은 Authorization 과 구분된다. 인증 (Authentication) 은 아래의 여기서 설명하고 있는 키값을 통한 로그인을 의미하며 인가 (Authorization) 는 간단히 보면 이렇게 인증된 것만으로 인가여부가 결정될 수도 있으나 여기에 더해서 관리자, 일반사용자 와 같이 role 에 따른 권한 부여 절차까지를 의미한다.

 

cookie session 방식:

세션 쿠키 (만료 시간 지정하지 않은 쿠키) 와 서버측의 세션 저장소를 통한 인증 방식. 서버측으로 인증정보 (아이디, 비번) 을 전송하면 서버에서는 Set-Cookie 헤더를 통해 클라이언트로 암호화 해싱된 세션키 값을 내려준다. 클라이언트에서는 Cookie 헤더에 매 http 요청시 마다 암호화 해싱된 세션키값을 넣어 서버측에 요청을 보낸다. 서버는 해당 사용자의 아이디와 세션키값의 룩업테이블 (세션저장소) 의 정보를 비교하여 일치하면 인증된 것으로 간주하고 해당 사용자의 이름이나 닉네임, 나이 등의 정보를 데이터베이스로 부터 가져와서 돌려주거나 또한 글쓰기 같은 것을 허용 또는 거부같은 작업을 해준다.

 

token 방식

토큰은 Json Web Token 규약을 대부분 사용한다. 클라이언트 측에서 서버측으로 인증정보 (아이디, 비번) 을 전송하면 서버에서는 토큰을 응답으로 내려준다. 해당 토큰을 Authorization: Bearer ...JWT... 와 같이 매 http 요청시 마다  Authorization 헤더에 담아 서버측에 요청을 보낸다. 서버는 해당 토큰에 담긴 정보를 해석하여 유효한 토큰일 경우 인증된 것으로 간주하고 그에 따른 작업을 수행한다.

 

앱, 웹브라우저 와 같이 다양한 플랫폼을 대상으로 하는 경우 토큰 방식이 더욱 유용하다. 백엔드 서버의 스케일 확장에 있어서도 Stateless 한 방식을 사용하는 것이 좋다. token 방식은 Stateless 가 가능하다. 세션을 사용할 경우 또한 서버 측에 세션 저장소 라는 것을 가지고 있어야 하며 Stateful 하다. http 가 websocket 과 다르게 클라이언트와 서버의 연결이 지속되는 것이 아니기 때문에 매번 요청시 마다 인증정보를 보내는 것에 있어서는 동일하지만 세션방식은 그 시점에 백엔드 서버가 세션저장소를 이용하여 상태를 유지하는 방식이기 때문에 Statuful 한 것이다. JWT 의 경우 토큰 안에 관련 정보가 저장되기 때문에 별도의 정보를 서버측에 필수적으로 저장할 필요는 없다.

 

OAuth2 또한 JWT 을 사용하지만 access_token 과 만료일이 긴 refresh_token 을 함께 사용하여 access_token 은 refresh_token 과 함께 추가정보를 통해 발급받을 수 있게 하여 보안을 강화한다. OAuth2 글을 참고하면 전체적인 구조를 볼 수 있다. thewavelet.tistory.com/30 이 글도 참고해보라. openid connect 는 인증을 위한 oauth2 의 최상위 레이어이다. 페북, 구글, 네이버, 카카오 소셜 로그인 시 나오는 화면을 openid 라고 보아도 될 것이다. oauth2 는 반면 해당 로그인 후 글을 쓰거나 관련 정보를 얻거나 하는 것에 대한 인가를 하는 부분이라 할 수 있다. 어떤 행위가 인가된 토큰을 발행하는 것. velog.io/@jakeseo_me/Oauth-2.0과-OpenID-Connect-프로토콜-정리 , d2.naver.com/helloworld/24942 이 블로그들에 괜찮은 설명들이 있다.

 

 

next.js Server Side Rendering 과 관련하여 아래와 같은 cors, credentials 문제가 고려될 수 있다. SSR 은 브라우저, 백엔드서버 외에도 프론트 서버라는 것이 기능하는 부분이 많기 때문에 프론트 서버와 백엔드 서버 간의 이슈도 고려되어야 한다.

 

express 백엔드 서버

// origin: true 로 해두면 * 대신 보낸 곳의 주소가 자동으로 들어가 편리하다.
// 실제로는 이렇게 cors 를 열어두면 좋지 않다. proxy 방식으로 메인 도메인으로 요청시 백엔드로 요청을 넘겨주고 받는 식으로 해준다.
// 백단 api 서버 측에서 cors 를 열어두면 해킹의 위협이 높아질 수도 있기 때문에 보통은 프록시 방식을 사용한다.
// [[[ cors 는 브라우저의 정책이다. ]]] 브라우저가 자신이 자원을 받아온 곳 외의 도메인(포트 포함) 으로 요청을 할 경우 기본적으로 그 요청 응답을 막아버린다.
// [[[ 서버 대 서버 (프론트서버, 백엔드서버)의 요청에서는 cors 라는 것이 존재하지 않는다. ]]] 브라우저를 해킹한다면 cors 가 발생하지 않을 것이다.
// 서버 측에서 res.setHeader('Access-Control-Allow-Origin', '*'); 해주는 것이 app.use(cors({origin: '*'})); 와 같은 것.
// 그냥 app.use(cors()); 해주면 위와 같다.
// 서버 측에서 Access-Control-Allow-Origin 을 헤더에 담아 응답주면 브라우저는 cors 를 허용해준다.
app.use(cors({
  origin: 'http://localhost:3060',
  // [[[ Cookie 또한 프론트가 백단과 도메인이 다르면 서로 전달이 안된다. credentials 가 그것도 허용해주는 옵션. ]]]
  // [[[ credentials 의 경우 서버 대 서버 의 요청에도 적용된다. ]]] 즉, 기본적으로는 도메인이 다르면 쿠키가 동작하지 않는다. credentials 를 true 해주어야 한다.
  // res.setHeader('Access-Control-Allow-Credentials', true); 와 같다.
  // xhr 의 경우 xhr.withCredentials = true; fetch 의 경우 fetch(url, { credentials: true }); 와 의미가 같다.
  // 따라서 프론트 단의 소스에서도 axios 의 옵션에 withCredentials: true 를 넣어주어야 한다. axios.defaults.withCredentials = true;
  // 그리고 이렇게 withCredentials 를 true 로 하면 Access-Control-Allow-Origin 은 * 가 될 수 없다.
  // 백 단과 프론트 단이 민감한 점보를 주고 받으므로 * 보다 특정 도메인을 지정해주어야 한다. * 하면 브라우저에서 에러를 준다.
  credentials: true,  // 기본값은 false 이다.
}));

next.js 프론트엔드 서버

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { END } from 'redux-saga';
import axios from 'axios';
import wrapper from '../store/configureStore';
import { LOAD_ME } from '../reducers/user';
import { LOAD_ARTICLES } from '../reducers/article';

const Home = () => {
  const dispatch = useDispatch();
  const { me } = useSelector((state) => state.user);
  
  return (
	...
  );
};


// next 8 버전에서는 getInitialProps 라는 것을 사용했었는데 이는 조만간 없어질 듯 하다.
// next 9 에서는 다음이 추가되었다. getStaticProps, getStaticPath, getServerSideProps
// 이 메소드가 ssr 위해서 위의 리액트 코드 수행 전에 서버측에서 먼저 수행되어 모두 셋팅된 후 클라이언트로 내려준다.
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
  console.log('getServerSideProps start');
  console.log(context.req.headers);

  // cors 의 경우 브라우저만의 정책이라 프론트 서버 측에는 고려하지 않아도 되지만
  // credentials 의 경우 서버 대 서버의 요청에도 적용되기 때문에 이를 처리해주어야 한다. 기본적으로 도메인이 다르면 쿠키가 동작하지 않기 때문에
  // credentials 를 true 해주어야 하며 현재 axios.defaults.credentials = true 로 해준 상태이기 때문에 이 또한 문제는 없다.
  // 백엔드 서버에서도 Access-Control-Allow-Credentials 를 true 로 해놓았기 때문에 문제가 없다.
  // 문제는 브라우저일 경우 쿠키를 자동으로 담아서 서버에 요청하지만
  // ssr 을 위한 next 의 프론트 서버일 경우 이러한 것을 자동으로 해주지 않기 때문에 직접 이를 해주어야 한다.
  // 즉, 이 부분에서 쿠키를 담아서 백엔드로 요청을 보내는 작업을 해주어야 한다.
  const cookie = context.req ? context.req.headers.cookie : '';

  // 이곳은 프론트 서버 측의 수행 로직이기 때문에 각 브라우저에서 쿠키가 셋팅되는 것과 달리 프론트 서버는 여러 사람이 하나의 프론트 서버를 사용하기 때문에
  // 아래와 같이 쿠키를 우선 삭제해주고 context.req.headers.cookie 가 존재할 경우에만 axios.defaults.headers.Cookie 에 담아주도록 한다.
  // context.req.headers.cookie 가 브라우저에서 프론트서버로 요청할 때의 요청 컨텍스트 (req, res 가진) 이다.
  // 이렇게 '' 삭제 처리 안해줄 경우 바로 전의 브라우저에서 로그인 한 사용자의 쿠키가 그대로 적용되어
  // 다른 브라우저의 사용자가 바로 전 브라우저의 사용자의 로그인을 가진 상태가 될 수가 있다.
  axios.defaults.headers.Cookie = '';
  if (context.req && cookie) {
    axios.defaults.headers.Cookie = cookie;
  }

  context.store.dispatch({
    type: LOAD_ME,
  });
  context.store.dispatch({
    type: LOAD_ARTICLES,
  });

  // saga 에서 END 라는 액션을 불러와서 dispatch 해준다.
  // 이렇게 해야 saga 에서 실제 백엔드 서버 쪽 요청 로직을 응답까지 받아서 모두 셋팅해준다.
  // 이걸 안해주면 위의 saga 를 dispatch 하지만 완료될 때까지 기다리지 않고 브라우저로 전송하기 때문에 리덕스에 원하는 값이 들어있지 않게된다.
  context.store.dispatch(END);
  console.log('getServerSideProps end');
  // configureStore.js 에 sagaTask 를 store 에 등록하였다. ssr 을 위해서.
  await context.store.sagaTask.toPromise();
});

export default Home;

 

 

 

 

security.stackexchange.com/questions/81756/session-authentication-vs-token-authentication

 

Session Authentication vs Token Authentication

I am trying to get a handle on some terms and mechanisms and find out how they relate to each other or how they overlap. Authenticating a theoretical web application and mobile application is the f...

security.stackexchange.com

 

 

dzone.com/articles/cookies-vs-tokens-the-definitive-guide

 

Cookies vs. Tokens: The Definitive Guide - DZone Integration

We examine cookie and token-based authentication, advantages of using tokens, and address common questions developers have regarding token-based auth.

dzone.com

 

hackernoon.com/using-session-cookies-vs-jwt-for-authentication-sd2v3vci

 

Using Session Cookies Vs. JWT for Authentication | Hacker Noon

HTTP is a stateless protocol and is used to transmit data. It enables the communication between the client side and the server side. It was originally established to build a connection between web browsers and web servers.

hackernoon.com

 

auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

 

Refresh Tokens: When to Use Them and How They Interact with JWTs

Learn about refresh tokens and how they fit in the modern web. Get a working sample of how to implement it with NodeJS

auth0.com

 

 

 

 

 

 

 

반응형

'REACT & NODE' 카테고리의 다른 글

package-lock.json vs yarn.lock  (0) 2021.04.20
oauth2  (0) 2021.04.03
대륙의 실수 ( vue, antd, echarts )  (0) 2021.04.01
express 와 async await 그리고 error 처리  (0) 2021.03.20
React Native vs Android / Swift  (0) 2021.03.11

댓글

Designed by JB FACTORY