디지털 시대의 발전과 함께, 웹 애플리케이션과 API의 사용이 급증하면서 보안 문제는 그 어느 때보다 중요해졌습니다.
정보와 자원의 보호를 보장하기 위해, 인증(Authentication)과 인가(Authorization)는 필수적인 보안 절차로 자리 잡았습니다. 이러한 보안 절차는 애플리케이션의 신뢰성을 높이고, 데이터의 무결성과 사용자 프라이버시를 보호하는 데 핵심적인 역할을 합니다.
인증(Authentication)과 인가(Authorization)란❓
인증(Authentication)
인증은 해당 유저가 실제 유저인지 인증하는 개념으로, 사용자 접근의 첫 번째 단계입니다. 이를 통해 시스템은 사용자가 누구인지 확인하고, 올바른 자원에 대한 접근을 허용합니다.
인가(Authorization)
인가는 인증된 사용자가 특정 자원이나 기능에 대한 권한이 있는지를 결정하는 과정입니다.
인증이 성공적으로 이루어졌다고 하더라도, 사용자가 자원에 접근할 수 있는 권한이 있는지는 별도로 결정되어야 합니다.
웹 애플리케이션 인증
위와 같이 일반적으로 서버-클라이언트 구조로 되어있고, 실제로 이 두 가지 요소는 아주 멀리 떨어져 있습니다.
서버-클라이언트 구조의 특성
- 서버
- 요청을 처리하고, 데이터를 저장하며, 클라이언트의 요청에 응답합니다.
- 서버는 인증 및 인가를 관리하며, 클라이언트가 보낸 요청을 검증하고 적절한 응답을 제공합니다.
- 클라이언트
- 서버에 요청을 보내고, 서버로부터 응답을 받습니다.
- 클라이언트는 브라우저, 모바일 애플리케이션 또는 다른 사용자 인터페이스일 수 있습니다. 클라이언트는 서버와의 상호작용을 통해 인증 정보를 전송하고, 서버의 응답을 처리합니다.
HTTP 프로토콜의 특성
Http라는 프로토콜을 이용하여 통신을합니다. HTTP 프로토콜은 비연결성(Connectionless)과 무상태(Stateless)의 특성을 가지고 있습니다.
- 비연결성 (Connectionless)
- HTTP는 각 요청이 독립적으로 처리되는 프로토콜입니다.
- 클라이언트가 서버에 요청을 보내면, 서버는 그 요청을 처리하고 응답을 반환한 후 연결을 종료합니다. 클라이언트와 서버 간에
지속적인 연결이 유지되지 않습니다. - 이러한 특성은 각 요청이 독립적으로 처리되며, 서버가 클라이언트의 이전 요청을 기억하지 않음을 의미합니다.
- 무상태 (Stateless)
- HTTP는
상태를 유지하지 않습니다. 즉, 서버는 클라이언트의 요청 간상태 정보를 저장하지 않습니다. - 각 요청은 독립적으로 처리되며, 서버는 이전 요청에 대한 정보가 없습니다.
- 이로 인해 인증 정보를 매 요청마다 전달해야 하며, 서버는 각 요청을 개별적으로 처리합니다.
- HTTP는
그러면 여기서 의문점이 생기게 됩니다.
우리들은 인터넷 사용할때는 이전의 정보들이 잘 있는것처럼 연속성 있게 사용해 왔는데, 어떻게 "유저의 인증"이 처리되고 있는 걸 까요❓
웹 애플리케이션에서는 쿠키-세션 기반 인증과 JWT 토큰 기반 인증 두 가지 주요 방법을 사용합니다.
쿠키와 세션란❓
쿠키-세션 기반 인증과 JWT 토큰 기반 인증을 알아보기에 앞서 먼저 쿠키와 세션은 무엇을 의미할까요❓
쿠키(Cookie): 클라이언트에 저장될 목적으로 생성한 작은 정보를 담은 파일입니다.
우리들은 브라우저를 통해 쿠키를 확인해 볼 수 있습니다. (여기서는 크롬 브라우저)
개발자 도구를 열어보고, Application - Storage - Cookies에 도메인 별로 저장되어 있는 것을 확인할 수 있습니다.
쿠키의 구성 요소
- Name (이름)
- 쿠키를 식별하기 위해 사용되는 키입니다. 웹 서버는 이 이름을 통해 쿠키를 구별하고 접근할 수 있습니다.
- 특징: 이름은
중복될 수 없으며, 각 쿠키는 고유한 이름을 가져야 합니다.
- Value (값)
- 쿠키에 저장되는 실제 데이터입니다. 이 값은 서버가 클라이언트에게 전달하는 정보이며, 클라이언트는 이 값을 사용할 수 있습니다.
- 특징: 값은 문자열로 저장되며, 필요에 따라 암호화하거나 인코딩하여 사용할 수 있습니다.
- Domain (도메인)
- 쿠키가 유효한 도메인을 정의합니다. 쿠키는 이 도메인과 관련된 요청에만 포함됩니다.
- 특징: 도메인은 일반적으로 쿠키를 설정한 서버의 도메인과 같거나 서브도메인까지 포함할 수 있습니다.
- Path (경로)
- 쿠키가 유효한 URL 경로를 정의합니다. 이 경로 이하의 요청에만 쿠키가 포함됩니다.
- 특징: 특정 경로를 지정하면, 그 경로 아래의 URL에서만 쿠키를 사용할 수 있습니다.
- 경로를 /로 설정하면 도메인 내 모든 경로에서 쿠키를 사용할 수 있습니다.
- Expires (만료기한)
- 쿠키의 유효 기간을 설정합니다. 만료 기한이 지나면 쿠키는 자동으로 삭제됩니다.
- 특징: 만료 기한은 날짜와 시간으로 설정됩니다.
만료 기한이 설정되지 않으면, 쿠키는 세션 쿠키로 간주되어 브라우저 세션이 종료될 때 삭제됩니다.
세션의 구성 요소
- 서버에서 일정시간 동안 클라이언트 상태를 유지하기 위해 사용됩니다.
- 서버에서 클라이언트 별로 유일무이한 '세션 ID'를 부여한 후 클라이언트 별 필요한 정보를 서버에 저장합니다.
- 서버에서 생성한 '세션 ID'는 클라이언트의 쿠키값('세션 쿠키'라고 부름)으로 저장되어 클라이언트 식별에 사용됩니다.
쿠키-세션 기반 인증란❓
쿠키-세션 방식은 웹 애플리케이션에서 사용자의 인증 상태를 유지하고 관리하는 일반적인 방법입니다. 이 방식은 쿠키와 세션을 활용하여 사용자의 인증 정보를 저장하고, 사용자와 서버 간의 상태를 유지합니다.
즉, 서버가 ‘특정 유저가 로그인되었다’는 상태를 저장하는 방식입니다.
인증과 관련된 아주 약간의 정보만 서버가 가지고 있게 되고 유저의 이전 상태의 전부는 아니더라도 인증과 관련된 최소한의 정보는 저장해서 로그인을 유지시킨다는 개념입니다.
쿠키-세션 방식의 동작 방식
- 사용자 로그인 요청
- 사용자가 로그인 폼에 사용자 이름과 비밀번호를 입력하고 제출합니다.
- 서버의 인증 확인
- 서버는 데이터베이스의 사용자 테이블에서 입력된 사용자 이름과 비밀번호를 확인합니다.
- 세션 정보 저장
- 인증이 성공하면, 서버는 세션 저장소에 해당 사용자가 로그인된 상태임을 기록합니다.
- 세션 ID 발급
- 세션 저장소는 사용자와는 관련 없는 난수 형태의 세션 ID를 생성합니다.
- 사용자와 관련이 있는 정보라면 보안상 문제가 있기 때문입니다.
- 세션 저장소는 사용자와는 관련 없는 난수 형태의 세션 ID를 생성합니다.
- 세션 ID 응답
- 서버는 클라이언트에게 로그인 성공 응답으로 세션 ID를 쿠키에 담아 전달합니다.
- 쿠키 저장 및 전송
- 클라이언트는 세션 ID를 쿠키에 저장하고, 이후의 요청마다 이 세션 ID를 HTTP 헤더에 담아 서버에 보냅니다.
- 쿠키 검증
- 서버는 클라이언트의 요청에서 쿠키에 포함된 세션 ID를 확인하고, 이를 기반으로 세션 저장소에서 검증합니다.
- 사용자 인증 확인
- 세션 ID가 유효하면, 서버는 이 사용자가 로그인된 상태로 인식합니다.
- 인증된 응답 처리
- 서버는 로그인된 사용자에 맞는 응답을 제공합니다.
쿠키-세션과 JWT 기반 인증의 차이점❗️
- 세션 저장소 vs. 토큰 검증
- 쿠키-세션 방식
- 세션 ID를 통해 클라이언트를 식별하고, 서버가 세션 정보를 저장하고 관리합니다. 클라이언트는 매 요청 시 세션 ID를 함께 전송하며, 서버는 이 ID를 통해 클라이언트를 검증합니다.
- JWT 기반 인증
- 서버가 토큰 자체를 클라이언트에 보내고, 클라이언트는 이 토큰을 저장하여 요청 시 전송합니다. 서버는 토큰을 검증하여 클라이언트의 상태를 확인하며, 별도의
세션 저장소가 필요하지 않습니다.
- 서버가 토큰 자체를 클라이언트에 보내고, 클라이언트는 이 토큰을 저장하여 요청 시 전송합니다. 서버는 토큰을 검증하여 클라이언트의 상태를 확인하며, 별도의
- 쿠키-세션 방식
두 개 다 비슷하지만 가장 큰 차이점을 정리하자면
쿠키-세션 방식은 서버에서 세션 ID를 저장하고 관리해야 합니다. 클라이언트는 세션 ID를 쿠키에 저장하고, 요청 시마다 이 쿠키를 서버에 전송하여 서버가 세션 저장소에서 클라이언트를 인증합니다.
쿠키-세션 방식에서는 서버가 세션 ID를 발급하고, 클라이언트는 이 세션 ID를 쿠키에 저장하여 이후의 HTTP 요청 시마다 함께 전송합니다. 서버는 세션 ID를 통해 클라이언트를 인증하며, 실제로는 상태를 유지하지 않지만 클라이언트와의 세션이 계속 이어지는 것처럼 보이게 합니다.
Spring에서의 쿠키-세션 생성 방법
1. 쿠키 값 인코딩
String cookieValue = "yourCookieValue";
cookieValue = URLEncoder.encode(cookieValue, "utf-8").replaceAll("\\+", "%20"); // 공백을 %20으로 변환
쿠키 값에는 공백이나 특수 문자가 포함될 수 있으므로, URL Encoder.encode() 메서드를 사용하여 값을 인코딩합니다. 이는 쿠키 값이 안전하게 전송되도록 보장합니다.
2. 쿠키 객체 생성
Cookie cookie = new Cookie("AUTHORIZATION_HEADER", cookieValue); // Name-Value 형식
Spring에서 Cookie 클래스 제공하고 있습니다. Cookie 클래스를 사용하여 쿠키를 생성합니다. 쿠키는 이름과 값(Name, Value)의 쌍으로 구성됩니다.
3. 쿠키 속성 설정
cookie.setMaxAge(60 * 60); // 쿠키의 유효 기간을 1시간으로 설정 (초 단위)
cookie.setPath("/"); // 쿠키가 유효한 경로
cookie.setDomain("example.com"); // 쿠키가 유효한 도메인
cookie.setHttpOnly(true); // JavaScript에서 쿠키 접근 불가
cookie.setSecure(true); // HTTPS에서만 전송
쿠키에 추가 속성을 설정할 수 있습니다. 예를 들어, 쿠키의 유효 기간을 설정하거나, 특정 도메인 및 경로에 대해서만 유효하도록 설정할 수 있습니다.
4. 쿠키를 응답에 추가
response.addCookie(cookie); // HttpServletResponse 객체를 통해 쿠키를 응답에 추가
생성한 쿠키를 HTTP 응답에 추가하여 클라이언트에 전송합니다.
1. 쿠키 생성
addCookie 메서드
- cookieValue를 UTF-8로 인코딩한 후, 공백을 %20으로 대체합니다.
- Cookie 객체를 생성하고 AUTHORIZATION_HEADER라는 이름으로 cookieValue를 설정합니다.
- 쿠키의 유효 기간을 30분으로 설정하고, 경로를 /로 지정하여 애플리케이션의 모든 경로에서 쿠키가 유효하도록 합니다.
- HttpServletResponse 객체를 사용하여 쿠키를 응답에 추가합니다.
정리하자면 다음과 같습니다.
쿠키를 생성하는 요청을 서버에 보내면, 서버는 addCookie() 메서드를 통해 쿠키를 생성하고 응답의 "Response Headers"에 Set-Cookie 정보를 포함시킵니다.
위 코드에서는, Set-Cookie 헤더에는 쿠키의 이름("Authorization"), 값("ZINU Auth"), 만료 시간(30x60초, 즉 1800초), 경로('/') 등의 정보가 포함됩니다.
브라우저는 이 Set-Cookie 정보를 읽어 쿠키 저장소에 저장합니다.
2. 쿠키 조회
- /get-cookie 엔드포인트는 클라이언트로부터 받은 쿠키 값을 추출하여 반환합니다.
- @CookieValue 어노테이션을 사용하여 AUTHORIZATION_HEADER라는 이름의 쿠키 값을 자동으로 주입받아 사용합니다.
@CookieValue를 통해 쿠키값을 가져왔습니다. 그러면 요청되는 HttpServletRequest에 쿠키가 들어있다는 의미입니다.
개발자 도구에서 확인을 해보면 다음과 같습니다.
따라서 위 그림처럼 HttpServletResponse에 들어있는 쿠키를 @CookieValue를 통해 간편하게 가져올 수 있습니다.
Spring에서의 쿠키-세션 생성 방법
1. 세션 생성 : createSession 메서드
- 세션 생성 또는 조회
- req.getSession(true) 호출은 현재 HTTP 요청에 대해 HttpSession 객체를 반환합니다.
- true를 인자로 전달하면, 만약 현재 요청에
세션이 존재하지 않으면 새로운 세션을 생성합니다. - 이미 세션이 존재하는 경우, 기존 세션을 반환합니다.
- 세션에 데이터 저장
- session.setAttribute(AUTHORIZATION_HEADER, "ZINU Auth") 호출을 통해 세션에 데이터를 저장합니다.
- 여기서 AUTHORIZATION_HEADER는 "Authorization" 문자열을 키로 사용하여, "ZINU Auth" 값을 세션에 저장합니다.
- 응답:
- 세션 생성이 완료된 후, 문자열 "createSession"을 반환합니다.
2. 세션 조회 : getSession 메서드
- 세션 조회
- req.getSession(false) 호출은 현재 HTTP 요청에 대해 HttpSession 객체를 반환합니다.
- false를 인자로 전달하면, 만약 현재 요청에 세션이 존재하지 않으면 null을 반환합니다.
- 세션이 존재할 경우에만 세션 객체를 반환합니다.
- 세션에서 데이터 조회
- session.getAttribute(AUTHORIZATION_HEADER) 호출을 통해 세션에 저장된 데이터를 조회합니다.
- AUTHORIZATION_HEADER는 "Authorization" 문자열을 키로 사용하여, 저장된 값을 가져옵니다.
- 응답
- 세션에서 가져온 값을 출력하고, "getSession : " 뒤에 해당 값을 붙여 문자열로 반환합니다.
create-session
Response Header의 Set-Cookie를 보면 서버에서 만든 유일무이한 "세션 ID"가 저장된 것을 확인할 수 있습니다.
"46 C259 A731908 B51 B05594 B4 EBFC2331" 우리 눈에는 이렇게 보이지만 실제로는 "ZINU Auth"의 정보들이 들어있는 것입니다.
get-session
Session을 잘 가져오는데 확인을 해보겠습니다.
출력도 "ZINU Auth"로 잘 나오고, get-session > Request Headers > Cookie에 쿠기 2개(Authorization=ZINU%20 Auth, JSESSIONID=6 C31 C54705900449 EAC246 E212 CCC276)가 정상적으로 get 한 것을 확인할 수 있습니다.
가져온 세션을 저장된 value를 Name을 파라미터로 전달해서 들어가 있던 "JSESSIONID=6 C31 C54705900449 EAC246 E212 CCC276 == ZINU Auth"을 잘 가져온 것도 확인할 수 있습니다.
HttpServletRequest & HttpServletResponse란❓
HttpServletRequest는 Java Servlet API에서 제공하는 인터페이스로, HTTP 요청에 대한요청 파라미터, 헤더, 세션, 쿠키, 본문 등의 다양한 정보를 서버 측에서 처리할 때 사용됩니다.
주로 웹 애플리케이션에서 클라이언트가 보낸 요청을 읽고, 요청에 대한 정보를 추출하는 데 활용됩니다.
HttpServletResponse는 Java Servlet API의 인터페이스로, 상태 코드, 헤더, 본문 작성, 쿠키 설정, 리다이렉트 등 다양한 기능을 통해 서버가 클라이언트에게 응답을 생성하고 전송하는 데 사용됩니다.
웹 애플리케이션에서 클라이언트의 요청에 대한 응답을 조작하고 설정하는 데 필수적인 역할을 합니다.
@CookieValue란❓
@CookieValue는 Spring Framework에서 제공하는 어노테이션으로, HTTP 요청의 쿠키 값을 쉽게 추출하여 메서드 파라미터로 전달받을 수 있게 해 줍니다.
주로 Spring MVC의 컨트롤러 메서드에서 클라이언트의 쿠키 값을 직접 읽어와 처리할 수 있도록 도와줍니다. 이를 통해 HTTP 요청에서 쿠키 정보를 손쉽게 접근하고 처리할 수 있습니다.
'Framework > Spring\Spring boot' 카테고리의 다른 글
[Spring Cloud] Spring Cloud란 무엇일까 ❓ (마이크로서비스 아키텍처(MSA)를 위한 프레임워크 ) (0) | 2024.07.31 |
---|---|
[Spring Boot] Spring Boot에서 JWT 다루기 (JWT, @Value, @PostConstruct) (0) | 2024.07.29 |
[Spring Boot] 같은 타입의 Bean이 두 개 이상일 때 처리 방법 (0) | 2024.07.28 |
[Spring] Spring에서 빈 이름 결정 방식 (0) | 2024.07.28 |
[Spring] Spring IoC 컨테이너에 Bean 수동 등록하기 (@Configuration, @Bean) (0) | 2024.07.28 |