kiwi

인증과 인가, spring security[Session], jwt[Token]

by 키위먹고싶다

인증과 인가

인증(Authentication) : 식별 가능한 정보로 서비스에 등록된 유저의 신원을 입증하는 과정

인가(Authorization) : 인증된 사용자에 대한 자원 접근 권한 확인

 

클라이언트와 서버는 HTTP로 통신을 한다는 개념을 알고 있을 것이다.

- (HTTP에 대한 이해가 부족하시면 아래 글을 읽어주세요)

 

HTTP 의 진화

HTTP 의미 먼저 사전적 의미의 HTTP란, HTTP(HyperText Transfer Protocol)는 클라이언트와 서버 사이에 이루어지는 요청/응답(request/response) 프로토콜이다. 예를 들면, 클라이언트인 웹 브라우저가 HTT..

kiwideveloper.tistory.com

 

이때 HTTP에서 중요한 속성은 무상태성이다. (Stateless)

서버는 클라이언트가 보낸 요청과 그 다음 요청 사이에 연관관계가 없다고 생각하고 요청을 받고 처리한다.

 

그런데 생각해보자. HTTP는 무상태성이라 각 요청에 대한 통신상태가 저장되지 않는다. 그런데 우리는 웹서비스를 이용하면서 새로운 페이지를 요청할 때마다 로그인하지 않는다. 그 이유가 무엇일까?

 

보통 인증과 인가를 Session과 Token과 같은 방식으로 사용한다. 

보통 유저가 로그인을 시도할때 서버에서 일치하는 유저 정보를 찾는다면(DB에서 신원 조회) 인증(Authentication) 확인의 표시로 세션이나 토큰을 발급한다. 

 

이후 웹브라우저 측에서 세션/토큰 정보를 간직하고 있다가 새로운 요청을 보낼 때 인가(Authorization)를 위해 해당 세션/토큰을 함께 보낸다. 이 둘은 비슷해 보이지만 차이점이 존재한다. 

 


 

세션과 토큰 차이점

1. 저장위치

세션은 DB서버에 저장된다. 그러나 토큰은 클라이언트 측에서 저장한다. 

 

2. 확장성

대부분 웹 서비스가 토큰 방식인 이유가 바로 확장성 때문이다. 서버 성능을 위해 서버가 여러 대일 경우 (서버가 분산/클러스터 환경), HTTP의 Stateless, Connectionless와 같은 속성으로 인해 request마다 내가 접속한 서버가 달라진다. 

 

세션의 저장 위치가 서버라고 설명했는데 만약 Session정보가 없는 다른 서버에 접속한다면 그때마다 계속 로그인해야 한다. 

 

3. 사이즈

세션 < 토큰

세션의 크기는 토큰에 비해 매우 작다. 

 

 


인증방식

세션 - 쿠키 방식

인증의 흐름은 다음과 같다. 

 

맨 처음 인증 완료가(서버가 클라이언트의 신원을 확인하고 세션 ID를 전달함) 되면 클라이언트는 매 요청마다 쿠키에 세션 id를 담아 헤더를 통해 전달한다. 서버는 쿠키의 정보를 세션 저장소에 있는 세션 id와 대조해 인증 상태를 확인한다. 물론 세션 id 자체는 유의미한 값을 갖지 않는다. 그러나 이 HTTP 요청을 중간에 누군가 가로채서 Cookie를 탈취하고 그 사람이 인증된 사용자의 Cookie를 실어 서버에 요청을 보내면 서버는 인증된 사용자인지, 가로챈 해커인지 구별할 방법이 없다. 이럴 경우 HHTPS를 사용하기, 세션에 유효시간 지정하기 등등 해결책이 있다. 

 

 

Spring Security

스프링 시큐리티는 스프링 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크이다. 스프링 시큐리티에서는 주로 서블릿 필터(filter)와 이들로 구성된 필터 체인으로 구성된 위임 모델을 사용한다. 보안과 관련해서 체계적인 옵션을 제공하기 때문에 개발자는 보안 로직을 작성하지 않아도 된다는 장점이 있다. Filter기반으로 동작하기 때문에 MVC와 분리하여 관리, 동작하며 위에서 설명한 세션/쿠키 방식으로 인증한다.  

 

만약 스프링 시큐리티를 사용하지 않으면 자체적으로 세션을 체크해야 한다. 그리고 Redirect를 일일이 설정해야 한다. 

최근 버전은 xml설정을 이용하지 않고 bean으로 설정할 수 있다. 

 

 

 

Spring Security의 세션/쿠키 인증 방식은 다음과 같다. 

 

  • http request요청
  • AuthenticationFilter를 통해 (2→6) User까지
  • User에 있는 사용자라면 UserDetails에서 session생성 
  • spring security의 인메모리 세션 저장소인 SecurityContextHolder에 저장
  • 사용자에게 session ID와 함께 응답을 내려줌
  • 이후 요청에서는 요청 쿠키에서 JSESSIONID를 까 봐서 검증 후 유효하면 Authentication 되는 것이다. 

 

결론 : 모든 접근 주체(=유저)는 Authentication를 생성한다. 이것은 SecurityContext에 보관되고 사용된다.

즉 security의 세션들은 내부 메모리(SecurityContextHolder)에 쌓고 꺼내 쓰는 것이다.

 


인증방식

토큰 기반 인증 방식

Token(인증을 위해 사용되는 암호화된 문자열)

  • 사용자가 인증에 성공하면 서버는 토큰을 생성해서 클라이언트로 보낸다. 
  • 토큰도 세션과 마찬가지로 사용자가 보내는 요청에 포함된다. 
  • 세션 인증에서는 서버가 세션 ID를 저장하고 클라이언트가 쿠키에 실어보낸 세션ID를 저장소에 ID와 대조해서 확인하지만 토큰을 사용하면 요청을 받은 서버는 토큰이 유효한지만 확인하므로 세션 인증에 비해 서버 운영의 효율이 좋다. 

 

JWT

세션/쿠키와 함께 가장 대표적인 인증 수단이다. JWT(JSON Web Token)의 약자로 인증에 필요한 정보들을 암호화시킨 토큰을 뜻한다. Access Token을 HTTP 헤더에 실어 서버로 보낸다. 

 

 

토큰을 만들기 위해 크게 3가지가 필요하다. 

 

 

https://velopert.com/2389

 

헤더 (Header)

Header는 두 가지의 정보를 가진다.

{
    "alg": "HS256", // 서명 알고리즘
    "typ": "JWT" // 토큰 유형
}

바로 토큰의 유형, 사용 중인 서명 알고리즘 두 부분으로 구성된다. 

 

Payload

{
    "exp": 1622920907,    // 등록된 클레임
    "iat": 1620328907,    // 등록된 클레임
    "jti": "a4cd64c8-4abf-49e8-9662-3c2ac1331c2a", // 등록된 클레임
    "id": "kjj924",    // 비공개 클레임
    "email": "kjj924@naver.com" // 비공개 클레임
}

여기서 말하는 등록된 클레임은 서비스에 필요한 정보가 아닌 토큰에 대한 정보를 나타내기 위한 클레임이다.

공개 클레임은 공개용 정보를 전달하기 위해 사용되며 비공개 클레임은 클라이언트와 서버가 정보를 공유하기 위한 사용자 지정 클레임이다. 

 

Signature

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

Base64 방식으로 인코딩한 Header,payload 그리고 SECRET KEY를 더한 후 서명 (header와 payload정보로 암호화)

 

 

 

Header와 payload는 인코딩 될뿐 따로 암호화 하지 않으므로 누구나 디코딩하면 확인 할 수 있다.  그러나 Signature는 secret key를 알지 못하면 복호화 할 수 없다. 

 

EX> A사용자가 토큰을 조작하여 B사용자의 데이터를 훔치려고 한다. payload에 있던 A의 ID를 B의 ID로 바꿔 다시 인코딩 한 후 토큰을 서버로 보낸다. 그러면 서버는 처음에 암호화 된 Signature를 확인한다. 그러나 payload에는 B의 정보가 들어있고 Signature는 A의 Payload를 기반으로 암호화 되었기 때문에 유효하지 않는 토큰으로 간주하게 된다. 그래서 사용자는 SECRET KEY를 알지 못하는 이상 토큰을 조작할 수 없다는 것이다.

 

 

일단 JWT는 토큰을 사용한다는 점에 있어서 인증정보를 서버에 저장하는 세션방식과 달리 서버에 저장되지 않는다. 그러므로 서버를 확장시키기에 적합하지만 JWT토큰이 탈취당할 경우 서버에서 해당 토큰을 무력화 하기 어렵기 때문에 토큰의 만료기간을 짧게 가져가야 하며 payload에 사용자 정보가 있기 때문에 탈취 당하면 정보 노출이 발생하므로 토큰 자체에 중요한 개인정보를 넣지 않아야 한다. 결론은 서버가 인증을 위해 암호화를 하냐 별도의 저장소를 이용하냐의 차이이다. 

'spring' 카테고리의 다른 글

DI 컨테이너  (0) 2022.05.11
cors와 spring security filter chain  (0) 2022.05.04
AOP(Aspect-Oriented-Programming)  (0) 2022.03.13
빈 후처리기  (0) 2022.03.10
파일 업로드  (0) 2022.02.19

블로그의 정보

kiwi

키위먹고싶다

활동하기