CS

SpringSecurity와 인증, 인가, JWT의 구조, 동작 과정

jungmin.park 2023. 12. 27. 13:35

 

Spring Security의 구조와 JWT 발급 과정에 대해 설명해주실 수 있을까요?

 

 

Spring Security란?

 

스프링 시큐리티는 인증(Authentication), 인가(Authorize) 부여 및 보호 기능을 제공하는 프레임워크이다.


인증(Authentication)과 인가(Authorize)란?

출처: 스파르타 코딩교육

인증(Authentication) 인가(Authorization)
해당 유저가 실제 유저인지 인증하는 개념 해당 유저가 특정 리소스에 접근이 가능한지 허가를 확인하는 개념
ex) 스마트폰 지문인식, 사이트 로그인 ex) 해당페이지 - 관리자 권한

 

 

웹 애플리케이션 인증(Authentication)

  • 일반적으로 서버-클라이언트 구조로 되어있고, 실제로 두가지 요소는 아주 멀리 떨어져있다.
  • 그리고 Http 프로토콜을 이용하는데 이 통신은 비연결성(Connectionless) 무상태(Stateless)로 이루어집니다.

비연결성(Connectionless)

  • 서버와 클라이언트가 연결되어있지 않다는 것입니다.
  • 그 이유는 리소스를 절약하기 위해서인데, 만약 서버와 클라이언트가 실제로 계속 연결이 되어있다면 서버의 비용이 기하급수적으로 늘어나기 때문입니다.
  • 그래서 리소스를 절약하기 위해 서버는 실제로 하나의 요청에 하나의 응답을 내리고 연결을 끊어버리고있다라고 생각하면 된다.

무상태(Stateless)

  • 서버가 클라이언트의 상태를 저장하지 않는다는 것이다.
  • 기존의 상태를 저장하는 것들도 마찬가지로 서버의 비용과 부담을 증가시키는 것이기 때문에 기존의 상태가 없다고 가정하는 프로토콜을 이용해 구현되어 있다.
  • 실제로 서버는 클라이언트가 직전에, 혹은 그 전에 어떠한 요청을 보냈는지 관심도 없고 전혀 알지 못한다.

 

비연결성, 무상태 프로토콜에서 '유저가 인증되었다'라는 정보는 어떻게 유지시키는 것인가?

 

인증의 방식

쿠키(Cookie) 인증

  • 서버가 '특정 유저가 로그인 되었다'는 상태를 저장하는 방식 인증과 관련된 아주 약간의 정보만 서버가 가지게 됨
  • 인증과 관련된 최소한의 정보는 저장해서 로그인을 유지시킨다는 개념이다.
  1. 사용자가 로그인 요청을 보낸다.
  2. 서버는 DB의 유저 테이블을 뒤져서 아이디 비밀번호를 대조해서 사용자를 확인
  3. 사용자가 로그인을 하면, 서버는 ID, PW 정보를 쿠키에 담아 브라우저로 다시 보낸다.
  4. 이후 브라우저에서 요청할때마다 로그인 정보가 담긴 쿠키를 함께 서버로 보낸다. 브라우저에서 매번 요청할때마다 서버 입장에서는 로그인 정보가 담긴 쿠키를 받게 된다.

쿠키의 장점

  • 기존 로그인 정보를 사용하기 때문에 인증을 위한 추가적인 데이터 저장이 필요없다.
  • 쿠키는 서버가 아닌 클라이언트 웹 브라우저에 한다.

쿠키의 단점

  • 사용자의 주요 정보를 매번 요청에 담기 때문에 보안상 문제가 있다.
  • 클라이언트에서 쿠키 정보를 쉽게 변경, 삭제할 수 있고, 가로채기 당할 수 있다.
  • 쿠키 사이즈가 커질수록 네트워크 부하가 심해진다.

쿠키의 구성요소

  • Name(이름) : 쿠키를 구별하는데 사용되는 키(중복불가)
  • Value(값) : 쿠키의 값
  • Domain(도메인) : 쿠키가 저장된 도메인
  • Path(경로) : 쿠키가 사용되는 경로
  • Expires(만료기한) : 쿠키의 만료기한

쿠키-세션 방식의 인증

  1. 사용자가 로그인 요청을 보냅니다.
  2. 서버는 DB의 유저 테이블에서 ID, PW 찾아 대조해본다.
  3. 실제 유저테이블의 정보와 일치한다면 인증을 통과한 것을 보고 "세션 저장소"에 해당 유조가 로그인 되었다는 정보를 넣는다.
  4. 세션 저장소에서는 유저의 정보와는 관련 없는 난수인 session-id를 발급한다.
  5. 서버는 로그인 요청의 응답으로 session-id를 내어줍니다.
  6. 클라이언트는 그 session-id를 쿠키(세션 쿠키)라는 저장소에 보관하고 앞으로의 요청마다 세션아이디를 같이 보냅니다.(주로 HTTP header에 담아서 보냅니다)
  7. 클라이언트의 요청에서 쿠키를 발견했다면 서버는 세션 저장소에서 쿠키를 검증합니다.
  8. 만약 유저 정보를 받아왔다면 로그인사용자로 인지한다.
  9. 이후 로그인 된 유저에 따른 응답을 내어준다.

세션의 장점

  • 사용자의 로그인 정보를 주고 받지 않기 때문에 상대적으로 안전하다.
  • 사용자마다 고유의 세션ID가 발급되기 때문에, 요청이 들어올때마다 회원DB를 찾지 않아도 된다.

세션의 단점

  • 사용자를 식별할 수 있는 값인 세션ID를 생성하고, 서버에 저장해야하는 작업이 생긴다.
  • 서버 세션 저장소를 사용하므로 요청이 많아지면 서버 부하가 심해진다.

쿠키와 세션이란?

쿠키와 세션 모두 HTTP에 상태 정보를 유지(Stateful)하기 위해 사용된다. 즉, 쿠키와 세션을 통해 서버에서 클라이언트 별로 인증 및 인가를 할 수 있게 된다.
  쿠키(Cookie) 세션(Session)
  클라이언트에 저장될 목적으로 생성한 작은 정보를 담은 파일 서버에서 일정시간 동안 클라이언트 상태를 유지하기 위해 사용
저장 위치 클라이언트(웹 브라우저) 웹 서버
사용 예 사이트 팝업의 "오늘 다시보지 않기" 정보 저장 로그인 정보 저장
만료 시점 쿠키 저장 시 만료일시 설정 가능
(브라우저 종료시도 유지 가능)
다음 조건 중 하나가 만족될 경우 만료됨
1. 브라우저 종료 시까지
2. 클라이언트 로그아웃시까지
3. 서버에 설정한 유지기간까지 해당 클라이언트의 재요청이 없는 경우
용량 제한 브라우저 별로 다름(크롬 기준)
- 하나의 도메인 당 180개
- 하나의 쿠키 당 4KB(=4096byte)
개수 제한 없음
(단, 세션 저장소 크기 이상 저장 불가능)
보안 취약
(클라이언트에서 쿠키 정보를 쉽게 변경, 삭제 및 가로채기 당할 수 있음)
비교적 안전
(서버에 저장되기 때문에 상대적으로 안전)

 

 


JWT 기반 인증

JWT 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별

  • 사용자가 로그인 요청을 보낸다.
  • 서버는 DB의 유저 테이블을 뒤져 아이디 비밀번호를 대조해본다.
  • 실제 유저테이블의 정보와 일치한다면 인증을 통과한 것으로 보고 유저의 정보를 JWT로 암호화 해서 보낸다.
  • 서버는 로그인 요청의 응답으로 jwt 토큰을 내어준다.
  • 클라이언트는 그 토큰을 저장소에 보관하고 앞으로의 요청마다 토큰을 같이 보낸다.
  • 클라이언트의 요청에서 토큰을 발견했다면 서버는 토큰을 검증한다.
  • 이후에는 로그인 된 유저에 따른 응답을 내어준다.

JWT을 사용하는 이유

  • 모든 서버에서 동일한 Secret key 소유한다.
  • Secret key 통한 암호화/ 위조 검증(복호화 시)

 

JWT 장점

  • 동시 접속자가 많을 때 서버 측 부하 낮춤
  • Client, Server가 다른 도메인을 사용할때
    • 카카오 OAuth2 로그인 시 JWT Token 사용
  • 인증 정보를 서버에 별도로 저장할 필요가 없다 -> 서버의 Stateless 특성이 유지된다.

JWT 단점

  • 구현의 복잡도 증가
  • JWT에 담는 내용이 커질수록 네트워크 비용 증가(클라이언트 -> 서버)
  • 생성된 JWT를 일부만 만료시킬 방법이 없음
  • Secret key 유출 시 JWT 조작 가능
  • PayLoad 자체는 암호화되지 않기 때문에 사용자의 중요한 정보는 담을 수 없다.

Spring Security 구조

 

  • Http Request 수신
    • 사용자가 로그인 정보와 함께 인증 요청을 한다.
  • 유저 자격을 기반으로 인증토큰을 생성
    • AuthenticationFilter가 요청을 가로채고, 가로챈 정보를 통해
    • UsernamePasswordAuthenticationToken의 인증용 객체를 생성한다.
  • Filter를 통해 AuthenticationToken을 AuthenticationManager로 위임
    • AuthenticationManager의 구현체인 ProviderManager에게 생성한 UsernamePasswordToken 객체를 전달한다.
  • AuthenticationProvider의 목록으로 인증을 시도
    • AuthenticationManager는 등록된 AuthenticationProvider들을 조회하며 인증을 요구한다.
  • UserDetailsService의 요구
    • 실제 데이터베이스에서 사용자 인증정보를 가져오는 UserDetailsService에 사용자 정보를 넘겨준다.
  • User 객체 정보들을 UserDetailsrk UserDetailsService(LoginService)로 전달
    • AuthenticationProvider들은 UserDetails를 넘겨받고 사용자 정보를 비교한다.
  • 인증 객체 or AuthenticationException
    • 인증이 완려가 되면 권한 등의 사용자 정보를 담은 Authentication 객체를 반환한다.
  • 인증 끝
    • 다시 최초의 AuthenticationFilterdp Authentication 객체가 반환된다.
  • SecurityContext에 인증 객체를 설정
    • Authentication 객체를 Security Context에 저장한다.
  • 최종적으로 SecurityContextHolder는 세션 영역에 있는 SecurityContext에 Authentication객체를 저장한다.
    • 사용자 정보를 저장한다는 것은 스프링 시큐리티가 전통적인 세션-쿠키 기반의 인증 방식을 사용한다는 것을 의미한다.

 

 


 

 

사용자는 id와 pw를 입력하여 로그인 요청을 시도합니다. 서버는 입력받은 id와 pw로 사용자 인증로직을 수행합니다.

모든 요청은 DispatcherServlet을 통과하게 되고 이후에 각 요청을 담당하는 Controller로 분배됩니다.

각 요청에 대해서 공통적으로 처리해야할 필요가 있을 때 DispatcherServlet 이전에 단계가 필요하며 이것이 Filter입니다.

SpringSecurity도 인증과 인가를 처리하기 위해 Filter를 사용하는데 FilterChainProxy를 통해 상세로직인 구현되고 있습니다. 요청에 대해서 인증이 필요하면 AuthenticationFilter가 요청을 가로채고, 가로챈 정보를 통해 UsernamePasswordAuthenticationToken의 인증용 객체를 생성합니다. 그 후 AuthenticationManager에게 넘겨 인증을 시도하고 실패하면 SecurityContextHolder를 비우고 성공하면 SecuriyContextHolder에 Authentication를 세팅합니다. SecurityContext는 인증이 완료된 사용자의 상세정보(Authentication)를 저장합니다.

서버는 사용자 인증이 정상적으로 완료되면 JWT를 생성하게 되고 JWT를 사용자에게 반환합니다.

 

 

Reference


https://velog.io/@kimdy0915/%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-JWT%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90

https://velog.io/@hope0206/Spring-Security-%EA%B5%AC%EC%A1%B0-%ED%9D%90%EB%A6%84-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%97%AD%ED%95%A0-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0