[Spring Boot] Spring Boot, JWT, OAuth2 (1)

2021. 3. 4. 01:38Spring/Spring Boot

1. JWT, OAuth2

1) 서버 기반 인증 방식

JWT에 대해 알기 위해서는 서버 기반 인증 방식에 대해서 알아야한다. 서버 기반 인증 방식의 핵심은 서버측에 유저 정보를 저장하는 것이다. 대표적으로 세션을 이용하여 처리하는 방법이 있다. 즉, 사용자가 로그인을 하면 서버는 해당 유저의 세션을 만들고 서버의 메모리와 데이터베이스에 저장을 한다. 그렇기 때문에 사용자가 증가하여 서버를 확장하게 되면 모든 서버에게 세션의 정보를 공유해야하기에 별도의 중앙 세션 관리 서버를 만든다.

 

일반적으로 2개 이상의 서버를 운영할 때 세션 동기화 설정을 해주는 번거로움을 해결하기 위해서는 Redis와 같은 메모리 DB에 세션을 저장소로 사용하여 구현한다.

2) Access Token, Refresh Token

Access Token은 리소스에 직접 접근할 수 있도록 해주는 정보만을 가지고 있다. Refresh Token에 비해 짧은 만료 기간을 가지며, 주로 세션에 담아 관리한다.

 

Refresh Token은 새로은 Access Token을 발급받기 위한 정보를 담고 있다. 클라이언트가 Access Token이 없거나 만료되면 Refresh Token을 통해 Auth Server에 요청하여 새로운 Access Token을 발급 받을 수 있다. 외부에 노출되지 않도록 관리를 위해 데이터베이스에 저장한다.

3) OAuth 2.0

OAuth 2.0은 인증을 위한 표준 프로토콜이다. 구글, 페이스북, 네이버 등에서 제공하는 Authorization Server를 통해 회원정보를 인증하고 Access Token을 발급받는다. 그리고 발급받은 Access Token을 이용해 타사의 API 서비스를 이용할 수 있다.

 

서버는 API 호출 요청에 대해서 Token이 유효한지를 확인할 필요가 있다. 이는 서버에서 클라이언트의 상태(토큰의 유효성)를 관리하게끔 하며, 또 API를 호출 할 때마다 그 토큰(AccessToken)이 유효한지 매번 DB등에서 조회하고 새로 갱신시 업데이트 작업을 해주어야 한다.

4) JWT

JWT는 정보를 JSON 객체 형태로 주고 받기 위해 표준규약에 따라 생성한 암호화된 문자열(Token)이다. JWT는 Claim 기반이라는 방식을 사용하는데, Claim이란 사용자에 대한 프로퍼티 속성을 이야기 한다.

{
    "id":"test",
    "role":"user"
}

여기서 JWT의 Token은 의미있는 토큰(유저의 상태를 포함한)으로 구성되어 있기 때문에 Auth 서버 쪽의 비용을 절감하면서 stateless한 아키텍처를 구성 할 수 있다.

 

중요한 점은 application server가 더이상 로그인한 사용자의 session을 관리 하지 않는다는 것이다. 단지 전달받은 JWT가 유효한 Token인지만 확인한다.

  • 클라이언트는 Auth Server에 로그인을 한다. 이때 Auth Server는 application server 내에 위치할 수도 있으며, google과 naver와 같은 제 3자가 될 수도 있다.
  • Auth Server에서 인증을 완료한 사용자는 JWT Token을 전달 받는다.
  • 클라이언트는 application server에 resource를 요청할때 앞서 전달받은 JWT Token을 Authorization Header에 전달한다.
  • application server는 전달받은 JWT Token이 유효하면 200 ok와 함께 data를 response 한다.

 

3) JWT 장점

- 확장성

만약 세션을 이용한다면, 서버를 스케일 아웃(확장)할 때마다 각 서버에 세션 정보를 저장하게 된다. 그럴 경우, 특정 서버에서 로그인 인증을 받을 때, 사용자가 증가하여 다른 서버에서 스케일 아웃이 일어났을 때 다른 서버에서 로그인을 했는지 알 수 없다는 단점이 있다.

  • A 사용자가 A 서버로부터 로그인 인증을 받음
  • 사용자 증가로 B 서버에서 스케일 아웃
  • B 서버는 A 사용자가 로그인을 했는지 알 수 없음

하지만 JWT는 서버가 늘어나도 토큰을 인증하는 방식만 알고 있다면 사용자 인증에 문제가 없다. 물론, JWT를 사용하지 않고 세션 전용 서버를 만들어 DB에 저장하는 방법도 있지만, 이 경우 사용자의 인증이 필요한 API의 모든 요청에 대해서 서버에 조회를 해서 DB의 값을 읽어야 하기에 DB 부하를 만들 수 있다.

 

가장 중요한 것은, 웹 앱 간의 쿠키 세션 처리에 좋다는 점이다. 브라우저에서의 쿠키 처리 방법과 앱에서의 쿠키 처리 방법은 다르기 때문에 JWT를 이용하는 것이 다양한 디바이스 차원에서 좋다.

 

- 보안성

JWT는 보안성 측면에서도 클라이언트가 서버로 요청할 때 쿠키를 전달하지 않기 때문에 쿠키를 사용함으로서 발생하는 취약점이 사라진다.

 

서버 기반 인증 시스템의 문제인 CORS 문제를 해결해준다. 쿠키는 발행한 서버에서만 유효하기 때문에 'www.one.ozofwerid.com'에서 발행한 쿠키가 'www.two.ozofweird.com'에서 사용할 수 없다. 하지만 JWT는 어떠한 도메인에서도 토큰만 유효하다면 처리가 가능하다.

4) JWT 단점

JWT를 사용하게 되면 모든 요청에 대해 토큰이 전송되기 때문에 데이터 증가하면서 따른 네트워크 부하가 증가할 수 있다. 또한 토큰 자체에 정보를 담고 있기 때문에 JWT가 만료되기 전에 탈취당하면 서버에서 처리할 수 있는 일이 없다.

 

JWT는 상태를 저장하지 않기 때문에 한번 만들어지면 제어가 불가능해준다. 토큰을 임의로 삭제하는 것이 불가능하기 때문에 만료시간은 필수적으로 넣어주어야한다.

 

 

 

 

 

2. JWT OAuth2 문제점

간혹 OAuth2 예제들을 따라하다보면 어노테이션 사용이 안되는 경우가 있다.

  • @EnableAuthorizationServer
  • @EnableResourceServer

 

확인해본 결과, Authorization Server에 대한 지원 종료를 한다는 언급이 있다. 공식 문서에 따르면 Spring Security 에서 Authorization Server지원이 적합하지 않다고 판단했고, 오픈 소스 인증 서버가 많이 있다고 생각해 더이상 지원하지 않기로 결정했다고 한다.


[참고] github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide

 

Spring Boot 2.0.0 M5 Release Notes

OAuth 2.0 Support Functionality from the Spring Security OAuth project is being migrated to core Spring Security. OAuth 2.0 client support has already been added and additional features will be migrated in due course.

If you depend on Spring Security OAuth features that have not yet been migrated you will need to add org.springframework.security.oauth:spring-security-oauth2 and configure things manually. If you only need OAuth 2.0 client support you can use the auto-configuration provided by Spring Boot 2.0. We’re also continuing to support Spring Boot 1.5 so older applications can continue to use that until an upgrade path is provided. (출처)

위 글을 통해 알 수 있는 것은, Spring Security OAuth 프로젝트의 기능들이 일부 Spring Security로 마이그레이션이 되어 Spring Boot 2.x 버전대에는 기존에 OAuth 프로젝트로는 지원이 안되는 기능들이 생겼다는 것을 알 수 있다.

 

이 문제를 해결하기 위한 방법으로는 2가지가 있다.

  • Spring Boot를 1.x 버전대로 낮추어 해결
  • 2.x 버전대와 1.x 버전의 호환성을 위한 dependency 추가 (아래 글 참고)

OAuth2 Autoconfig

If you have spring-security-oauth2 on your classpath you can take advantage of some auto-configuration to make it easy to set up Authorization or Resource Server. For full details, see the Spring Security OAuth 2 Developers Guide.

This project is a port of the Spring Security OAuth support that came with Spring Boot 1.x. Support was removed in favor of Spring Security 5’s first class OAuth support. To ease migration, this project exists as a bridge between the old Spring Security OAuth support and Spring Boot 2.x. (출처)

 

대부분의 예제가 다음과 같이 적용이 되어있다면,

dependencies {
	compile('org.springframework.security.oauth:spring-security-oauth2')
}

이를 수정하여 2.x 버전대와 1.x 버전의 호환하도록 설정하면 문제를 해결할 수 있다.

repositories {
	mavenCentral()
	maven { url 'https://repo.spring.io/snapshot' }
}

dependencies {
	compile 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.1.BUILD-SNAPSHOT'
}

또 다른 방안으로는 spring-cloud-security를 이용하는 방법이 있다.

repositories {
	mavenCentral()
	//아래 버전의 경우
	maven { url "https://repo.spring.io/milestone" }
}
dependencies {
	compile 'org.springframework.cloud:spring-cloud-security:2.0.0.M3'
}

 

그 외 발생할 수 있는 문제점으로는, Spring Security 5.x버전대로의 변화로 Password Encoder 방식이 변경이 되어 'Full authentication is required to access this resource' 문제가 발생하거나 잘못된 경로로 토큰을 요청하는 등이 있다.


[참고] covenant.tistory.com/201

[참고] hue9010.github.io/spring/OAuth2/

[참고] wonyong-jang.github.io/posts/spring/

[참고] lemontia.tistory.com/927

[참고] velog.io/@ehdrms2034/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%BD%94%EB%94%A9-%EA%B3%B5%EC%9E%91%EC%86%8C-7%EC%9E%A5-AuthorizationServer%EC%84%A4%EC%A0%95-%EC%8B%9C-Annotation-NotFoundFull-authentication-is-required-to-access-this-resource-%EB%B0%9C%EC%83%9D-%EB%AC%B8%EC%A0%9C

[참고] www.tutorialspoint.com/spring_boot/spring_boot_oauth2_with_jwt.htm

[참고] www.pixeltrice.com/spring-boot-security-using-oauth2-with-jwt/

 

728x90