반응형

 

 

 

 

 

 

토큰 기반 인증

 

 

➤ 토큰기반 인증을 쓰는 이유?

 

 

세션기반 인증 = 서버(혹은 데이터베이스)에 유저 정보를 담는 방식

매번 요청할 때마다 서버(와 데이터베이스)를 살펴봐야하기 때문에 서버의 부담이 크다.

이 부담을 클라이언트에게 넘겨줄 수 없을까 하는 고민에서 Token 인증 방식이 생겨났다.

대표적 토큰 기반 인증 : JWT(JSON Web Token)

 

 

 

➤ 토큰(Token)이 무엇인가?

 

화폐로 사용하는 토큰

오락실

행사에 입장할 때 사용하는 토큰

 

➡️ 돈을 이미 지불했고 이 서비스를 이용할 수 있다는 정보를 담고 있음

 

 

 

 ➤ 토큰을 클라이언트에 저장하는 것은 위험하지 않을까?

 

토큰을 클라이언트가 가지고 있으면 XSS 공격에 노출되는 위험이 있지 않을까?

토큰은 유저정보를 암호화한 상태로 담을 수 있고, 암호화했기 때문에 클라이언트에 담아도 위험하지 않다.

 

 

 

➤ JWT란?

 

토큰 기반 인증 방식은 다양하다.

그 중에서 가장 많이 사용하는 것이 JWT이다.

JSON Web Token의 약자

JSON 포맷으로 사용자에 대한 속성을 저장하는 웹 토큰이다.

토큰 인증 방식중 가장 일반적으로 사용된다.

 

 

 

➤ JWT의 구조

 

 

JWT 예시

 

. 을 기준으로 세 부분으로 나눠진다.

 

1. Header

토큰의 타입(JSON), 어떤 해시 암호화 알고리즘으로 sign할지가 적혀있다.

➡️ JSON 타입을 base64방식으로 인코딩한다.

 

2. Payload

유저의 정보가 담겨있다.

어떤 정보에 접근 가능한지에 대한 권한을 담을 수도 있고, 사용자의 유저 이름 등 필요한 데이터를 담을 수 있다.

민감한 정보가 들어가선 안된다.

➡️ JSON 타입을 base64방식으로 인코딩한다.

 

3. Signature

Header, Payload를 base64 인코딩한 값과 salt값의 조합으로 암호화된 값

Signature에서는 원하는 비밀키(암호화에 추가할 salt)와 Header에서 지정한 알고리즘을 사용하여 암호화한다.

 

base64 인코딩은 누구나 쉽게 디코딩할 수 있지만 비밀키를 사용해 암호화한 값(시그니처)은 비밀키를 보유하고 있는게 아니라면 해독하는게 엄청 어렵다.

 

ex) HMAC SHA256 알고리즘을 사용한 Signature 생성

HMASHA256(base64urlEncode(header) + “.” + base64urlEncode(payload), secret);

 

 

 

 

➤ JWT의 종류

 

JWT는 다음의 두가지 종류의 토큰을 이용해 인증을 구현한다.

클라이언트는 처음 인증을 받게 될 때 두가지의 토큰을 받는다.

 

 

▶️ 액세스 토큰(Access Token)

 

보호된 정보들(유저의 이메일, 연락처 등)에 접근할 수 있는 권한부여에 사용한다.

실제로 권한을 얻는데 사용하는 토큰이다.

 

 

▶️ 리프레시 토큰(Refresh Token)

 

액세스 토큰이 탈취되는 것을 방지하기 위해서 액세스 토큰은 비교적 짧은 유효기간을 가지고 있다.

이때 액세스 토큰의 유효기간이 만료되면 리프레시 토큰을 사용하여 새로운 액세스 토큰을 발급받는다. ➡️ 유저가 다시 로그인할 필요 없음

액세스 토큰에 비해 유효기간이 길다.

리프레시 토큰까지 악의적인 유저가 얻어낸다면 큰 문제가 된다.

그렇기 때문에 유저의 편의보다는 정보를 지키는 것이 더 중요한 웹사이트들은 리프레시 토큰을 사용하지 않는 곳이 많다.

 

 

 

➤ 토큰 기반 인증 절차

 

토큰 기반 인증 절차

 

1. 클라이언트가 서버에 아이디/비밀번호를 담아 로그인 요청을 보낸다.

 

2. 아이디/비밀번호가 일치하는지 확인하고 클라이언트에게 보낼 암호화된 토큰을 생성한다.

액세스 / 리프레시 토큰을 모두 생성한다.

토큰에 담길정보(payload)는 유저를 식별할 정보, 권한이 부여된 카테고리 등이 될 수 있다.

두 종류의 토큰이 같은 정보를 담을 필요는 없다.

 

3. 클라이언트는 토큰을 저장한다.

Local Storage, cookie, Session Storage 등 다양한 공간에 저장 가능

 

4. 클라이언트가 HTTP 헤더(Authorization Header) 또는 쿠키에 토큰을 담아 보낸다.

bearer authentication을 이용한다.

 

5. 서버는 토큰을 해독하여 발급한 토큰과 동일하다고 판단될 경우 클라이언트의 요청을 처리한 후 응답을 보내준다.

 

 

참고) Bearer Token

참고) Bearer Token Details

 

 

 

 

JWT의 장점과 단점

 

 

➤ JWT를 통한 인증의 장점

 

1. Statelessness & Scalability(무상태성 & 확장성)

 

🍒 서버는 클라이언트에 대한 정보를 저장할 필요가 없다. (토큰 해독이 되는지만 판단한다.)

🍒 클라이언트는 요청을 보낼 때마다 토큰을 헤더에 포함시켜서 인증을 한다.

🍒 서버를 여러개 가지고 있는 서비스에서 더 효율적이다. 같은 토큰을 여러 서버에서 인증 가능하기 때문이다. 세션의 경우는 모든 서버가 유저의 정보를 공유하고 있어야 한다.

 

 

2. 안정성

 

🍒 암호화 한 토큰을 사용

🍒 암호화한 키를 노출할 필요가 없음

 

 

3. 어디서나 생산 가능

 

🍒 토큰을 확인하는 서버가 꼭 토큰을 만들지 않아도 됨

🍒 토큰 생성용 서버를 만들거나, 다른 회사에 토큰 생성만 맡기는 등 다양한 활용이 가능하다.

 

 

4. 권한 부여에 용이

 

🍒 토큰의 payload(내용물) 안에 어떤 정보에 접근 가능한지 정의할 수 있다.

 

 

 

➤ JWT를 통한 인증의 단점

 

 

1. Payload를 해독할 수 있음

 

Payload는 base64로 인코딩 되어있기 때문에 토큰을 탈취하여 Payload를 해독하면 데이터를 확인할 수 있다. ➡️ 민감하고 중요한 정보는 Payload에 저장 x

 

 

2. 토큰의 길이가 길어지면 네트워크에 부하를 줄 수 있다.

 

토큰에 저장하는 정보의 양이 길어지면 토큰의 길이가 길어진다. 요청마다 긴 토큰과 함께 전송하면 네트워크에 부하를 줄 수 있다.

 

 

3. 토큰은 자동으로 생성되지 않는다.

 

JWT는 상태를 저장하지 않기 때문에 자동으로 삭제되지 않아서 토큰 유효기간(만료시간)을 반드시 설정해야 한다.

토큰이 탈취되면 기한이 만료될 때까지 대처가 불가능하기 때문에 만료시간을 너무 길게 설정하면 안된다.

 

 

4. 토큰은 어딘가에 저장되어야 한다.

 

토큰은 클라이언트가 가지고 있다가 인증이 필요한 요청을 보낼 때마다 함께 전송할 수 있어야 한다.

 

 

 

 

Spring Security 에서의 JWT 인증

 

 

➤ 프로젝트 사전 준비

 

 

▶️ 의존성 추가

 

  • Spring Boot DevTools
  • Lombok
  • Spring Web
  • Spring Security
  • Spring Data JPA
  • H2 Database

+ JWT 의존성 추가

implementation 'com.auth0:java-jwt:4.0.0'

 

 

▶️ application.yml 설정

 

spring:
  h2:
    console:
      enabled: true
      path: /h2
    datasource:
      url: jdbc:h2:mem:test
    jpa:
      hibernate:
        ddl-auto: create
      show-sql: true

 

 

 

➤ JWT에 필요한 Security 설정

 

 

▶️ 유저 entity에 role 추가

 

유저 entity 필드값에 인가 등급을 저장하기 위한 role 변수 추가

 

 

▶️ SecurityConfig 클래스 생성

 

🍑 JWT를 사용하기 위한 기본 설정들

  • JWT는 headers에 Authorization 값에 토큰을 보내는 방식이다. (-> Bearer)
  • 토큰 정보는 노출되면 안되지만 노출되어도 큰 위험은 없다. (유효시간을 지정했기 때문)

 

 

🍑 http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

  • Web은 기본적으로 stateless인데 session이나 cookie를 사용할 수 있다.
  • session / cookie 를 만들지 않고 STATELESS로 진행하겠다는 의미이다.

 

 

🍑 .formLogin().disable()

  • form Login 을 사용하지 않는다.

 

 

🍑 .httpBasic().disable()

  • http 통신을 할 때 headers에 Authorization 값을 ID, Password를 입력하는 방식이다.
  • https를 사용하면 ID와 Password가 암호화되어 전달된다.
  • http 로그인 방식을 사용하지 않는다.

 

 

▶️ CorsConfig 클래스 생성

 

⚠️ 주의) CorsFilter 클래스 import

CorsFilter를 import 해올 때 두가지 경로에 존재하는데 실습데로 구현하려면 아래의 경로에서 import 해와야 한다.

import org.springframework.web.filter.CorsFilter;

 

 

🍈 .setAllowCredentials(true)

  • true : 서버가 응답할 때 json을 자바스크립에서 처리할 수 있게함

 

 

🍈 .addAllowedOrigin(“*”)

  • 모든(*) ip에 응답 허용

 

 

🍈 .addAllowedHeader(“*”)

  • 모든(*) header에 응답 허용

 

 

🍈 .addAllowedMethod(“*”)

  • 모든 http 메서드(post, get, patch, delete) 요청 허용

 

 

 

 

 

JWT Bearer 인증

 

 

➤ Session & Cookie 인증 방식

 

사용자가 로그인 요청을 보내면 사용자를 확인 후 session ID를 발급한다.

발급한 ID를 이용해 다른 요청과 응답을 처리하는 방식이다.

 

 

➤ Token 인증 방식

 

서버에 별도의 저장소가 필요없다.

로그인 시 클라이언트에 토큰을 발급해주고 클라이언트가 요청을 보낼 때마다 발급받은 토큰을 헤더를 통해 전달받아 응답받는 방식이다.

 

 

▶️ 장점

 

🍅 토큰 기반 인증은 쿠키나 세션을 이용한 인증보다 보안성이 강하고 효율적이다.

  • 쿠키 인증은 HTTP 방식 통신을 사용하는 경우 정보가 유출되기 쉽다.
  • 세션 인증은 서버에서 처리해야하기 때문에 추가적인 데이터베이스 공간이 필요하므로 사용자가 점점 많아지면 부담이 될 수 있다.

 

🍅 토큰은 누구나 디코딩하여 데이터가 유출될 수 있어도 Signature 필드가 header와 payload를 통해 만들어져 데이터 변조 후 재전송을 막을 수 있다.

 

🍅 stateless(무상태성) 서버를 만들 수 있다.

  • 서버에 상태를 저장하지 않기 때문이다.

 

🍅 인증정보를 OAuth로 이용할 수 있다.

 

 

일반적으로 토큰은 request header의 Authorization 필드에 담겨져 보내진다.

request header의 Authorization 필드 구조 : Authorization: <type> <credentials>

 

 

 

➤ Bearer 인증 방식

 

JWT or OAuth 토큰을 사용한다.

 

 

➤ Filter 생성

 

Spring Security

 

▶️ addFilterBefore() / addFilterAfter()

 

SecurityFilterChain에 있는 필터 전/후로 필터를 적용해주는 메서드이다.

addFilterBefore(필터, 특정 필터 이름.class)

ex) http.addFilterAfter(new FirstFilter(), BasicAuthenticationFilter.class);

 

 

 

➤ Token 적용

 

▶️ HttpServletRequest

  • ServletRequest를 상속한다.
  • Http 프로토콜의 request 정보를 서블릿에 전달하기 위한 목적으로 사용
  • Header 정보, Parameter, cookie, URI, URL 등의 정보를 읽어들이는 메서드를 가진 클래스
  • Body의 Stream을 읽어들이는 메서드를 가지고 있다.

 

 

▶️ HttpServletResponse

  • ServletResponse를 상속한다.
  • Servlet이 ServletResponse 객체에 Content Type, 응답코드, 응답 메세지 등을 담아서 전송한다.

 

 

 

참고) JWT 공식 사이트

 

 

 

 

 

감사합니다.

오개념에 대한 지적은 늘 환영입니다. 🤩

 

 

 

반응형