프레임워크/Spring Boot

[Token]토큰 기반 인증

빨대도둑 2023. 8. 29. 20:19

사용자가 서버에 접근할 때 이 사용자가 인증된 사용자인지 확인하는 방법은 다양하게 존재합니다.

대표적인 사용자 인증 확인 방법에는 ***‘서버 기반 인증’***과 ***‘토큰 기반 인증’***이 있습니다.

스프링 시큐리티에서는 기본적으로 세션 기반 인증을 제공합니다.

기본적으로 제공해주는 세션 기반 인증을 사용해서 사용자의 정보를 담은 세션을 생성하고 저장해서 인증을 합니다.

이것을 세션 기반 인증이라고 합니다.

토큰 기반 인증은 토큰을 사용하는 방식입니다.

토큰은 서버에서 클라이언트를 구분하기 위한 유일한 값인데, 서버가 토큰을 생성해서 클라이언트에 제공하면, 클라이언트는 이 토큰을 가지고 있다고 여러 요청과 이 토큰을 함께 신청합니다.

그럼 서버는 토큰만 보고 유효한 사용자인지 검증합니다.

 


토큰을 전달하고 인증받는 과정

 

  1. 클라이언트가 아이디와 비밀번호를 서버에게 전달하면서 인증을 요청한다
  2. 서버는 아이디와 비밀번호를 확인해 유효한 사용자인지 검증한다. 유효한 사용자이면 토큰을 생성해서 응답한다.
  3. 클라이언트는 서버에서 준 토큰을 저장한다
  4. 이후 인증이 필요한 API를 사용할 때 토큰을 함께 보낸다
  5. 그러면 서버는 토큰이 유효한지 검증한다
  6. 토큰이 유효하다면 클라이언트가 요청한 내용을 처리한다.

 


토큰 기반 인증의 특징(무상태성, 확장성, 무경성)

무상태성

무상태성은 사용자의 인증 정보가 담겨 있는 토큰이 서버가 아닌 클라이언트에 있으므로 서버에 저장할 필요가 없습니다.

서버가 데이터를 유지하고 있으려면 그만큼 자원을 소비해야 하지만, 토큰 기반 인증에서는 클라이언트에서 인증 정보가 담긴 토큰을 생성하고 인증합니다.

따라서 클라이언트에서는 사용자의 인증 상태를 유지하면서 이후 요청을 처리해야 하는데 이것을 상태를 관리한다고 합니다.

이렇게 하면 서버 입장에서는 클라이언트의 인증 정보를 저장하거나 유지하지 않아도 되기 때문에 완전한 무상태(stateless) 효율적인 검증을 할 수 있습니다.


확장성

무상태성은 확장성에 영향을 줍니다.

서버를 확장할 때 상태 관리를 신경 쓸 필요가 없으므로 서버 확장에도 용이합니다.

세션 인증 기반은 각각 API에서 인증을 해야되는 것과는 달리 토큰 기반 인증에서는 토큰을 가지는 주체는 서버가 아니라 클라이언트이기 때문에

가지고 있는 하나의 토큰으로 각자 다른 서버에 요청을 보낼 수 있습니다.

추가로 소셜 로그인과 같이 토큰 기반 인증을 사용하는 다른 시스템에 접근해 로그인 방식을 확장할 수도 있고, 이를 활용해 다른 서비스에 권한을 공유할 수 있습니다.


무결성

토큰 방식은 HMAC(hash-based message authentication)기법이라고도 부르는데, 토큰을 발급한 이후에는 토큰 정보를 변경하는 행위는 할 수 없기 때문입니다.

즉, 토큰의 무결성이 보장됩니다. 만약에 누군가 토큰을 한 글자라도 변경하면 서버에서는 유효하지 않은 토큰이라고 판단을 합니다.


 

JWT

발급받은 jwt를 이용해 인증을 하려면 http 요청 헤더중에 Authorization 키 값에 Bearer+JWT 토큰값을 넣어서 전송해야 합니다.

 

JWT 토큰의 구조

 

jwt는 .(점)을 기준으로 헤더(header), 내용(payload), 서명(signature)으로 구성되어 있습니다.

헤더는 토큰의 타입과 해싱 알고리즘을 지정하는 정보를 담습니다.

{
"type":"JWT",
"alg":"hs2019"
}

typ: 토큰의 타입을 지정합니다.

alg 해싱 알고리즘을 지정합니다.

 

내용에는 토큰과 관련된 정보를 담습니다.

내용의 한 덩어리를 클레임(claim)이라고 부르며, 클레임은 키값의 한 쌍으로 이루어져 있습니다.

클레임은 등록된 클레임, 공개 클레임, 비공개 클레임으로 나눌 수 있습니다.

 


등록된 클레임은 토큰에 대한 정보를 담는데 사용됨니다.

다음은 그 종류 입니다.

이름 설명

iss 토큰 발급자(issuser)
sub 토큰 제목(subject)
aud 토큰 대상자(audience)
exp 토큰의 만료시간(expiraton)
nbf 토큰의 활성 날자와 비슷한 개념으로 not Before를 의미합니다.
iat 토큰이 발급된 시간(issued at)
jti jwt의 고유 식별자로서 주로 일회용 토큰에 사용합니다.

 

공개 클레임은 공개되어도 상관없는 클레임을 의미합니다.

충돌을 방지할 수 있는 이름을 가져야 하며, 보통 클레임 이름을 URL로 짓습니다.

 

비공개 클레임은 공개되면 안되는 클레임을 의미합니다.

주로 클라이언트와 서버 간의 통신에 사용합니다.

 

서명은 해당 토큰이 조작되었거나 변경되지 않았음을 확인하는 용도로 사용하며, 헤더의 인코딩 값과 내용의 인코딩 값을 합친 후 주어진 비밀키를 사용해 해시값을 생서합니다 .

 

 


토큰 유효기간

토큰의 유효기간이 하루라면, 하루동안 해당 토큰으로 무엇이든 할 수 있습니다.

하지만 토큰의 유효기간이 짧으면 사용자 입장에서는 받은 토큰을 너무 짧은 시간만 사용할 수 있으므로 불편합니다.

이러한 불편한 점을 해결하기 위해서 리프레시 토큰이 등장했습니다.

리프레시 토큰은 액세스 토큰과 별개의 토큰입니다.

사용자 인증을 하기 위한 용도가 아닌 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급하기 위해 사용합니다.

액세스 토큰의 유효기간은 짧게 설정하고, 리프레시 토큰의 유효기간은 길게 설정하면 공격자가 액세스 토큰을 탈취해도 몇분 뒤에는 사용할 수 없는 토큰이 되어 안전합니다.

 

 

  1. 클라이언트가 서버에게 인증을 요청합니다
  2. 서버는 클라이언트에서 전달한 정보를 바탕으로 인증 정보가 유효한지 확인한 뒤, 액세스 토큰과 리프레시 토큰을 만들어 클라이언트에게 전달합니다. 클라이언트는 전달받은 토큰을 저장합니다.
  3. 서버에서 생성한 리프레시 토큰은 DB에도 저장합니다
  4. 인증을 필요로 하는 API를 호출할 때 클라이언트에서 저장된 액세스 토큰과 함께 API를 요청합니다.
  5. 서버에서 전달받은 액세스 토큰이 유효한지 검사한 뒤에 유효하다면 클라이언트에서 요청한 내용을 처리합니다
  6. 시간이 지나고 액세스 토큰이 만료된 뒤에 클라이언트에서 원하는 정보를 얻기 위해 서버에게 API 요청을 보냅니다.
  7. 서버에서 액세스 토큰이 유효한지 검사합니다. 만료된 토큰이면 유효하지 않기 때문에 토큰이 완료되었다는 에러를 전달합니다
  8. 클라이언트에서는 이 응답을 받고 저장해둔 리프레시 토큰과 함께 새로운 액세스 토큰을 발급하는 요청을 전달합니다.
  9. 서버에서는 전달받은 리프레시 토큰이 유효한지, DB에서 리프레시 토큰을 조회한 후 저장해둔 리프레시 토큰과 같은지 확인합니다.
  10. 만약 유효한 리프레시 토큰이라면 새로운 액세스 토큰을 생성한 뒤 응답합니다.
  11. 이후 클라이언트는 요청과 같이 다시 API를 요청합니다.

 


토큰 필터 구현

필터는 실제로 각종 요청을 처리하기 위한 로직으로 전달되기 전후에 URL 패턴과 맞는 모든 요청을 처리하는 기능을 제공합니다.

요청이 오면 헤더값과 비교해서 토큰이 있는지 확인하고 유효 토큰이라면 시큐리티 콘텍스트 홀더에 인증 정보를 저장합니다.

시큐리티 컨텍스트는 인증 객체가 저장되는 보관소 입니다.

여기서 인증 정보가 필요할 때 언제든지 인증 객체를 꺼내 사용할 수 있습니다.

이 클래스는 스레드마다 공간을 할등하는 스레드 로컬에 저장되므로 코드의 아무곳에서나 참고할 수 있습니다.

또한 다른 스레드와 공유하지 않으므로 독립적으로 사용할 수 있습니다.

그리고 이러한 시큐리티 컨텍스트 객체를 저장하는 객체가 시큐리티 컨텍스트 홀더 입니다.

'프레임워크 > Spring Boot' 카테고리의 다른 글

OAuth  (0) 2023.08.29
Cookie: 쿠키  (0) 2023.08.29
스프링 시큐리티(spring security)  (0) 2023.08.29
타임리프: Thymeleaf  (0) 2023.08.29
[JSON]자바 직렬화와 역직렬화  (0) 2023.08.29