세션 인증 방식

  1. 로그인을 통해 사용자를 확인하고, 사용자 정보를 세션에 저장하여 Session ID를 사용자에게 응답한다.
  2. 클라이언트는 요청마다 Header에 Session ID가 담긴 Cookie를 넣어서 통신하고, 
    서버에서는 Cookie를 검증하여 데이터를 응답한다.

JWT 기반 인증 방식 (Json Web Token)

  1. Claim 기반 방식을 사용한다. (Claim은 사용자에 대한 속성 값)
  2. 의미있는 토큰(사용자 상태 포함)으로 구성되어 있기 때문에, 
    AuthServer에 검증 요청을 보내야만 했던 과정을 생략하고, 각 서버에서 수행할 수 있게 되어
    비용 절감 및 Stateless 아키텍처를 구성할 수 있다.

동작 과정

  1. 클라이언트는 Auth Server에 로그인한다
  2. Auth Server에서 인증을 완료한 사용자는 JWT 토큰을 전달 받는다.
  3. 클라이언트는 특정 어플리케이션 서버에 리소스를 요청할 때, 전달받은 JWT 토큰을 Authorization Header에 넣는다.
  4. 어플리케이션 서버는 전달 받은 JWT토큰의 유효성을 직접 검사하여 사용자 인증을 할 수 있다.

특징

  • 확장성에 큰 장점이 있다. 
    • Session : 서버를 확장할 때마다, 각 서버에 세션 정보를 저장해야 한다. 
      특정 서버에서 로그인 인증을 받을 때, 다른 서버는 로그인했는지 알 수 없다.
    • JWT : 서버의 수와는 상관없이 토큰을 인증하는 방식을 알고 있으면 인증 과정에 문제가 없다.
  • 웹과 앱 간의 Cookie 세션 처리에도 유용하다.
    브라우저와 앱에서의 쿠키 처리 방법은 각기 다를 수 있기 때문에 JWT를 이용하는 것이 다양한 디바이스 차원에서 좋다고 함.
  • 사용자 인증 정보가 필요한 요청을 보낼 때, 헤더에 JWT 토큰값을 보내기 때문에 데이터가 증가해 네트워크 부하가 늘어날 수 있다.
  • 토큰 자체에 사용자 정보를 담고 있기에 JWT가 만료되기 전에 탈취당할 수 있다.
  • 한 번 만들어 클라이언트에게 전달하면 제어가 불가능하기 때문에 만료 시간을 필수적으로 넣어줘야 한다.
    • 짧은 만료 시간을 같는 JWT 형식의 Access Token과 긴 만료 시간을 갖는 JWT 형식의 Refresh Token 두 가지를 사용한다. ( RefreshToken을 Redis와 같은 DB에 저장하고 새로운 Access Token을 발급받기 위해 거쳐야 하는 추가적인 과정이 생김 )

Access Token과 Refresh Token

  • Access Token : 리소스(사용자의 정보)에 직접 접근할 수 있도록 하는 정보만을 가지고 있다. (for 인증)
    • 짧은 만료 기간을 가지고, 주로 Session에 담아 관리한다.
  • Refresh Token : 새로운 Access Token을 발급 받기 위한 정보를 담고 있다. (for accesstoken 발급)
    • 클라이언트가 Access Token이 없거나 만료된 상태라면, RefreshToken을 통해 AuthServer에 요청하여 새로운 AccessToken을 발급받을 수 있다.
    • 외부에 노출되지 않게 하기 위해 보통 DB에 저장한다. (필자는 Redis 인메모리 DB에 저장했다.)

서버 간의 통신이 잦은 경우, Access Token을 자주 주고 받을 수 밖에 없다.

  • 각 서버는 API 호출 요청에 대해서 전달받은 Access Token이 유효한 지를 확인해야 한다.
    • 이는 서버에서 클라이언트의 상태 (=AccessToken 유효성)를 관리하게 한다.
    • API를 호출할 때마다, Access Token이 유효한 지 매번 DB에서 조회하고 새로 갱신 시 업데이트 작업을 해주어야 한다.
    • 마이크로 서비스 개발처럼, 서버의 수가 많은 경우 각각의 서버가 AccessToken의 유효성 및 권한 확인을 Auth Server에 요청하기 때문에 병목 현상이 발생해 서버 부하로 이어질 수 있다.

 

+ [프로젝트에서 Access Token과 RefreshToken으로 구현한 로그인 과정을 다른 팀원들에게 설명한 내용]

AccessToken(1시간)과 RefreshToken(2개월)의 유효기간이 실 서비스에서 괄호에 있는 값으로 진행될 예정입니다.

<1시간이 지난 상황>
1시간이 지나면, AccessToken을 재발급하기 위해 'Token 재발급 API'를 프론트에서 서버에 정보를 요청합니다.
요청을 받은 서버에서는 프론트에 200 성공 코드를 보내면서 서버에서는 아직 유효기간이 길게 남은 Redis에 있는 RefreshToken으로부터 AccessToken을 새로 만들어서, header에 X-AUTH-TOKEN에 담아 보내줍니다!

<2개월이 지난 상황>
이제 2개월의 시간이 지나 refreshToken의 유효기간이 만료되면 Redis에 존재하는 refreshToken이 timeout으로 사라집니다. 그 와중에 2달이 지나기전에 발급된 accessToken의 유효기간인 1시간이 만료가 되면, 프론트에 401에러를 보내면서 'Token 재발급 API'를 요청하지만, 프론트가 가지고 온 Cookie에 있는 refreshToken과 서버의 Redis에 저장된 refreshToken을 비교했을때 refreshToken이 이미 사라진 상태이기 때문에 서버 측에서는 프론트에게 로그인 화면으로 전환하도록 401에러를 보냅니다.프론트는 401에러를 보고 사용자가 다시 로그인할 수 있게 로그인 화면으로 전환합니다. 서비스의 유저는 다시 로그인을 하면서 새로운 RefreshToken과 AccessToken을 발급받게 되면서 다시 이 서비스를 이용할 수 있게 됩니다.

1. Http Request : 사용자가 로그인 정보와 함께 인증 요청
2. AuthenticationFilter가 요청을 가로챈다. 이때 가로챈 정보를 통해 UsernamePasswordAuthenticationToken
객체 생성 
* UsernamePasswordAuthenticationToken : 사용자가 입력한 데이터를 기반으로 생성, 즉 현상태는 미검증 Authentication
3. AuthenticationManager의 구현체인 ProviderManager에게 UsernamePasswordAuthenticationToken 객체를 전달
4. AuthenticationProvider 에 UsernamePasswordAuthenticationToken 객체를 전달
5. 실제 DB로부터 사용자 인증 정보를 가져오는 UserDetailService에 사용자 정보를 넘겨준다.
* 실질적인 인증 과정은 사용자가 입력한 데이터와 UserDetailService의 loadUserByUsername() 메서드가 반환하는 UserDetails 객체를 비교하면서 동작한다.
* UserDetailService와 UserDetails 구현을 어떻게 하느냐에 따라서 인증의 세부 과정이 달라진다.
6. 넘겨받은 정보로 DB에서 찾은 사용자 정보인 UserDetails 객체를 생성
7. AuthenticationProvider는 UserDetails를 넘겨받고 사용자 정보를 비교
8. 인증이 완료되면, 사용자 정보를 담은 Authentication 객체를 반환
9. 최초의 AuthenticationFilter에 Authentication 객체를 반환
10. Authentication 객체를 SecurityContext에 저장
이 책을 읽게 된 동기

 

팀 프로젝트에 참여하면서 User를 담당하고 있는데, 
Spring Security 관련 코드를 주먹구구식으로 작성했던 것 같아

정확한 의미를 알고, 리펙토링을 하기 위해 이 책을 읽기로 했다. 

 

무엇보다 소셜로그인을 당장 개발해야 하는 만큼 Oauth2.0 관련 내용을 집중적으로 다룰 예정이다. 

 

 

 

로드맵

총 20개의 장

  1. 소프트웨어 애플리케이션에서 보안의 중요성과 보안 및 취약성을 어떻게 생각해야 하는지 논의하고 스프링 시큐리티로 이러한 문제를 방지하는 방법을 배운다. (책의 나머지 부분을 위한 준비 과정이다.)
  2.  스프링 시큐리티를 이용해 간단한 스프링 부트 프로젝트를 만드는 방법을 배우고 스프링 시큐리티 인증과 권한 부여 아키텍처, 그리고 해당 구성 요소를 살펴본다. (이 책 전체에서 꾸준히 이러한 구성 요소를 세부적으로 맞춤 구성하는 방법을 배운다.)
  3. 사용자 관리에 관한 스프링 시큐리티 구성 요소를 이용하는 방법을 배운다. 
  4. 스프링 시큐리티로 사용자의 암호를 관리하는 방법을 배우고, 암호화와 해싱을 주제로 논의하며 암호 검증과 관련한 스프링 시큐리티 구성 요소를 살펴본다.
  5. 스프링 시큐리티 구성 요소로 애플리케이션의 인증 논리를 맞춤 구성하는 방법을 배운다. (2장 바탕으로)
  6. 실전 예제로 2~5장에서 배운 모든 내용을 바탕으로 완전하게 작동하는 하나의 앱을 만든다.
  7. 권한 부여 구성에 관한 논의를 시작하여 권한 부여 제약 조건을 구성하는 방법을 배운다. 
    2~6장까지는 인증을 관리하는 방법을 배운것이고, 인증된 사용자에게 특정한 작업을 실행할 수 있는 이용 권리를 부여할 차례다. 이를 위해 요청을 위한 접근을 거부하거나 허용하는 방법을 배운다.
  8. 권한 부여에 관한 논의를 계속하며 특정 HTTP 요청에 권한 부여 제약 조건을 적용하는 방법을 배운다. 
  9. 필터 체인의 맞춤 구성을 주제로 논의하고, 필터 체인이 인증과 권한 부여 구성을 적용하기 위해 HTTP 요청을 가로채는 책임 체인이라는 점을 배운다.
  10. 사이트 간 요청 위조 방지 기능에 관해 설명하고, 스프링 시큐리티로 맞춤 구성하는 방법을 알아본다. 
    CORS 정책을 구성하는 방법에 대해서도 배운다.
  11. 두 번째 실전 예제로 맞춤 구성된 인증과 권한 부여가 구현된 애플리케이션을 작성한다. 
    + 토큰이 무엇이고 권한 부여에서도 어떤 역할을 하는지 배운다
  12. 12~15장에서는 OAuth2를 다룬다. 클라이언트가 백엔드 애플리케이션에서 노출한 엔드포인트를 호출하기 위해 액세스 토큰을 얻는 흐름을 설명한다.
  13. 스프링 시큐리티를 이용해 맞춤형 OAuth 2 권한 부여 서버를 구축하는 방법을 배운다.
  14. 스프링 시큐리티를 이용해 OAuth 2 시스템에 리소스 서버를 구축하는 방법과 권한 부여 서버에서 발급한 토큰을 리소스 서버가 검증하는 방법을 배운다.
  15. 시스템이 JSON 웹 토큰을 권한 부여에 이용하는 방법을 설명하면서 OAuth 2 주제를 마무리한다.
  16. 메서드 수준에 권한 부여 구성을 적용하는 방법을 설명한다.
  17. 메서드의 입출력을 나타내는 필터 값에 권한 부여 구성을 적용하는 방법을 배운다.
  18. 12~17장까지 배운 내용을 적용한 세 번째 실전 예제를 진행하며 부가적으로 OAuth 2 시스템에서 타사 툴 KeyCloak을 권한 부여 서버로 이용하는 방법을 배운다.
  19. 스프링 프레임워크로 개발한 리액티브 애플리케이션에 보안 구성을 적용하는 방법을 배운다.
  20. 보안 구성을 위한 통합 테스트를 작성하는 방법을 배운다.

+ Recent posts