-
Notifications
You must be signed in to change notification settings - Fork 0
[Sangwan] week09 미션 #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: Sangwan
Are you sure you want to change the base?
[Sangwan] week09 미션 #104
Changes from all commits
560926a
3ee75a3
eb3a34d
235b90d
80804d5
c171d9b
e5bf384
5ae6a2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,282 @@ | ||
| - 세션과 토큰의 차이는? | ||
|
|
||
| --- | ||
|
|
||
| > **세션(Session)은 서버가 클라이언트의 인증 상태를 서버 메모리에 저장하는 Stateful 방식이고, 토큰(Token)은 인증 정보를 클라이언트가 직접 보관하며 서버는 저장하지 않는 Stateless 방식이다. 두 방식의 핵심 차이는 "누가 상태를 기억하느냐"에 있다.** | ||
| > | ||
|
|
||
| --- | ||
|
|
||
| ### 세션 기반 인증 (Stateful) | ||
|
|
||
| 세션 기반 인증은 사용자가 로그인하면 서버가 세션 객체를 생성하고, 그 세션 ID를 쿠키(JSESSIONID)에 담아 클라이언트에 전달하는 방식이다. 이후 클라이언트는 매 요청마다 이 쿠키를 서버에 전송하고, 서버는 세션 저장소에서 해당 ID를 조회하여 사용자를 식별한다. | ||
|
|
||
| ``` | ||
| 1. 클라이언트 → 서버: 로그인 요청 (email + password) | ||
| 2. 서버: 인증 성공 → 세션 생성 (sessionId: abc123) | ||
| 3. 서버 → 클라이언트: Set-Cookie: JSESSIONID=abc123 | ||
| 4. 클라이언트 → 서버: 요청 + Cookie: JSESSIONID=abc123 | ||
| 5. 서버: 세션 저장소에서 abc123 조회 → 사용자 확인 | ||
| ``` | ||
|
|
||
| Spring Security 폼 로그인 설정 (세션 방식): | ||
|
|
||
| ```java | ||
| .formLogin(form -> form | ||
| .defaultSuccessUrl("/swagger-ui/index.html", true) | ||
| .permitAll() | ||
| ) | ||
| // 인증 정보는 서버 세션에 저장되고, 브라우저는 JSESSIONID 쿠키를 전송 | ||
| // 서버는 해당 ID로 세션을 찾아 사용자 인증 상태를 유지 (Stateful) | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ### 토큰 기반 인증 (Stateless) | ||
|
|
||
| 토큰 기반 인증은 로그인 성공 시 서버가 JWT 토큰을 발급하고, 클라이언트가 이를 저장(로컬 스토리지, 세션 스토리지 등)하는 방식이다. 이후 요청 시 Authorization: Bearer <token> 헤더에 토큰을 담아 전송하면, 서버는 DB 조회 없이 토큰의 서명만 검증하여 사용자를 식별한다. | ||
|
|
||
| ``` | ||
| 1. 클라이언트 → 서버: 로그인 요청 (email + password) | ||
| 2. 서버: 인증 성공 → JWT 토큰 생성 후 반환 | ||
| 3. 클라이언트: 토큰을 로컬 스토리지에 저장 | ||
| 4. 클라이언트 → 서버: 요청 + Authorization: Bearer eyJhbGci... | ||
| 5. 서버: 토큰 서명 검증 → 사용자 확인 (DB 조회 없음) | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ### 세션 vs 토큰 비교 | ||
|
|
||
| | 구분 | 세션 기반 (Stateful) | 토큰 기반 (Stateless) | | ||
| | --- | --- | --- | | ||
| | 상태 저장 위치 | 서버 (메모리/DB) | 클라이언트 (로컬 스토리지 등) | | ||
| | 서버 확장성 | 낮음 (세션 공유 문제) | 높음 (서버 간 공유 불필요) | | ||
| | 로그아웃 처리 | 즉시 가능 (서버에서 세션 삭제) | 어려움 (토큰 만료 전까지 유효) | | ||
| | 보안 | 세션 하이재킹 위험 | 토큰 탈취 시 만료 전까지 악용 가능 | | ||
| | 서버 부하 | 높음 (세션 저장소 유지) | 낮음 (서명 검증만 수행) | | ||
| | 모바일/앱 지원 | 쿠키 의존으로 불편 | 헤더 기반으로 편리 | | ||
| | 주요 사용 사례 | 전통적인 웹 서비스, 금융 서비스 | REST API, MSA, 모바일 앱 | | ||
|
|
||
| --- | ||
|
|
||
| ### 어떤 방식을 선택해야 할까? | ||
|
|
||
| 최근에는 모바일 앱, 프론트엔드와 백엔드가 분리된 구조, REST API 기반 서비스가 많아지면서 토큰 기반 인증(JWT)을 범용적으로 더 많이 사용하는 추세이다. 반면 금융 서비스처럼 보안이 최우선인 경우에는 세션 방식이 유리하다. | ||
|
|
||
| 서버가 여러 대로 확장되는 환경(Scale-out)에서는 세션 공유 문제가 발생하므로 토큰 방식이 훨씬 유리하다. 세션 방식을 유지하려면 Redis 같은 별도의 세션 공유 저장소가 필요하다. | ||
|
|
||
| - 엑세스 토큰과 리프레시 토큰이란? | ||
|
|
||
| --- | ||
|
|
||
| > **액세스 토큰(Access Token)은 API 요청 시 인증에 사용하는 단기 JWT 토큰이고, 리프레시 토큰(Refresh Token)은 액세스 토큰이 만료되었을 때 재발급을 위해 사용하는 장기 보조 토큰이다. 두 토큰을 함께 사용하면 보안성과 사용자 편의성을 동시에 높일 수 있다.** | ||
| > | ||
|
|
||
| --- | ||
|
|
||
| ### 왜 두 가지 토큰이 필요한가? | ||
|
|
||
| JWT 토큰은 한번 발급되면 서버에서 임의로 무효화할 수 없다는 단점이 있다. 만약 토큰이 해커에게 탈취된다면, 토큰이 만료될 때까지 해커가 해당 사용자인 척 API를 호출할 수 있다. | ||
|
|
||
| 이 문제를 해결하기 위한 전략이 바로 **액세스 토큰 + 리프레시 토큰** 조합이다. | ||
|
|
||
| - 액세스 토큰의 유효기간을 짧게 설정(30분~1시간)하여 탈취 시 피해를 최소화한다. | ||
| - 리프레시 토큰의 유효기간은 길게 설정(7일~30일)하여 사용자가 자주 재로그인하지 않아도 되게 한다. | ||
|
|
||
| --- | ||
|
|
||
| ### 액세스 토큰 (Access Token) | ||
|
|
||
| 액세스 토큰은 실제 API 요청 시 인증에 사용하는 메인 JWT 토큰이다. 모든 Private API 호출 시 `Authorization: Bearer <access_token>` 헤더에 담아 전송한다. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Authorization 헤더 예시의 백틱이 원문에서 닫히지 않아 Markdown 가독성이 낮아질 수 있습니다. 인증 헤더 형식은 실습에서 자주 복사되는 부분이므로 코드 표기와 설명을 정확히 맞추는 것을 권장합니다. |
||
|
|
||
| ```java | ||
| // JwtUtil에서 액세스 토큰 생성 | ||
| public String createAccessToken(AuthMember member) { | ||
| return Jwts.builder() | ||
| .subject(member.getUsername()) // 사용자 이메일 | ||
| .claim("role", authorities) // 권한 정보 | ||
| .issuedAt(Date.from(now)) // 발급 시간 | ||
| .expiration(Date.from(now.plus(accessExpiration))) // 만료 시간 (30분) | ||
| .signWith(secretKey) | ||
| .compact(); | ||
| } | ||
| ``` | ||
|
|
||
| **특징:** | ||
|
|
||
| - 유효기간: 짧게 설정 (30분 ~ 4시간 권장) | ||
| - 저장 위치: 클라이언트 메모리, 로컬 스토리지, 쿠키 | ||
| - 용도: 모든 인증이 필요한 API 요청에 사용 | ||
| - 만료 시: 리프레시 토큰으로 재발급 요청 | ||
|
|
||
| --- | ||
|
|
||
| ### 리프레시 토큰 (Refresh Token) | ||
|
|
||
| 리프레시 토큰은 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 재발급받기 위한 보조 토큰이다. 리프레시 토큰 자체는 API 인증에 직접 사용하지 않는다. | ||
|
|
||
| ``` | ||
| [액세스 토큰 만료 시 재발급 흐름] | ||
| 1. 클라이언트 → 서버: API 요청 + 만료된 액세스 토큰 | ||
| 2. 서버: 401 Unauthorized 응답 | ||
| 3. 클라이언트 → 서버: 토큰 재발급 요청 + 리프레시 토큰 | ||
| 4. 서버: 리프레시 토큰 검증 (DB 조회) | ||
| 5. 서버 → 클라이언트: 새로운 액세스 토큰 발급 | ||
| 6. 클라이언트: 새 액세스 토큰으로 원래 API 재요청 | ||
| ``` | ||
|
|
||
| **특징:** | ||
|
|
||
| - 유효기간: 길게 설정 (7일 ~ 30일) | ||
| - 저장 위치: 서버 DB 또는 Redis (보안을 위해 서버에 저장) | ||
| - 용도: 액세스 토큰 재발급 전용 | ||
| - 로그아웃 시: DB에서 리프레시 토큰 삭제 → 사실상 로그아웃 처리 가능 | ||
|
|
||
| --- | ||
|
|
||
| ### 액세스 토큰 vs 리프레시 토큰 비교 | ||
|
|
||
| | 구분 | 액세스 토큰 (Access Token) | 리프레시 토큰 (Refresh Token) | | ||
| | --- | --- | --- | | ||
| | 역할 | API 인증에 직접 사용 | 액세스 토큰 재발급용 | | ||
| | 유효기간 | 짧음 (30분 ~ 4시간) | 김 (7일 ~ 30일) | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 리프레시 토큰 유효기간 설명에 오탈자가 있습니다. 개념 자체는 적절하게 정리되어 있으므로 |
||
| | 저장 위치 | 클라이언트 (로컬 스토리지 등) | 서버 DB 또는 Redis | | ||
| | 탈취 시 피해 | 만료 전까지 악용 가능 (짧아서 피해 최소화) | 매우 위험 (즉시 모든 권한 탈취) | | ||
| | 만료 시 처리 | 리프레시 토큰으로 재발급 | 재로그인 필요 | | ||
|
|
||
| --- | ||
|
|
||
| ### application.yml 설정 예시 | ||
|
|
||
| ```yaml | ||
| jwt: | ||
| token: | ||
| secretKey: ${JWT_SECRET_KEY} | ||
| expiration: | ||
| access: 1800000 # 30분 (ms 단위) | ||
| refresh: 604800000 # 7일 (ms 단위) | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ### 정리 | ||
|
|
||
| 액세스 토큰과 리프레시 토큰의 조합은 JWT의 가장 큰 단점인 "토큰 무효화 불가" 문제를 보완하는 현실적인 해결책이다. 액세스 토큰은 짧은 유효기간으로 보안을 강화하고, 리프레시 토큰은 긴 유효기간으로 사용자 편의성을 보장한다. 리프레시 토큰을 서버 DB에 저장함으로써 로그아웃 시 즉각적인 무효화도 가능해진다. | ||
|
|
||
| - OAuth 1.0과 OAuth 2.0의 차이는? | ||
|
|
||
| --- | ||
|
|
||
| > **OAuth(Open Authorization)는 제3자 애플리케이션이 사용자의 자격 증명(아이디/비밀번호) 없이도 사용자를 대신해 특정 서비스의 리소스에 접근할 수 있도록 허가하는 개방형 표준 프로토콜이다. OAuth 1.0과 2.0은 같은 목적을 가지지만 구현 방식과 보안 메커니즘에서 큰 차이가 있다.** | ||
| > | ||
|
|
||
| --- | ||
|
|
||
| ### OAuth란? | ||
|
|
||
| 소셜 로그인(카카오로 로그인, 구글로 로그인 등)이 바로 OAuth를 활용한 대표적인 예이다. 사용자는 카카오 계정 정보를 우리 서비스에 직접 제공하지 않아도, 카카오에 로그인하고 권한을 허가함으로써 우리 서비스가 카카오 프로필 정보를 가져올 수 있게 된다. | ||
|
|
||
| **OAuth의 주요 등장인물:** | ||
|
|
||
| - **Resource Owner**: 사용자 (카카오 계정 소유자) | ||
| - **Client**: 우리가 만드는 서비스 (Spring Boot 앱) | ||
| - **Authorization Server**: 인증을 처리하는 서버 (카카오 인증 서버) | ||
| - **Resource Server**: 실제 데이터를 가진 서버 (카카오 API 서버) | ||
|
|
||
| --- | ||
|
|
||
| ### OAuth 1.0 | ||
|
|
||
| OAuth 1.0은 2007년에 등장한 초기 버전으로, 모든 요청에 서명(Signature)을 포함시켜 보안을 보장하는 방식이다. | ||
|
|
||
| ``` | ||
| [OAuth 1.0 흐름] | ||
| 1. Client → Authorization Server: Request Token 요청 | ||
| 2. Authorization Server → Client: Request Token 발급 | ||
| 3. Client → Resource Owner: 인증 페이지로 리다이렉트 | ||
| 4. Resource Owner: 권한 허가 | ||
| 5. Authorization Server → Client: Verifier 코드 전달 | ||
| 6. Client → Authorization Server: Access Token 요청 (Request Token + Verifier) | ||
| 7. Authorization Server → Client: Access Token 발급 | ||
| 8. Client → Resource Server: API 요청 (Access Token + 서명) | ||
| ``` | ||
|
|
||
| **OAuth 1.0의 특징:** | ||
|
|
||
| - 모든 API 요청에 HMAC-SHA1 서명 포함 (복잡한 암호화 로직 필요) | ||
| - HTTPS 없이도 보안 유지 가능 (서명 검증으로 위변조 방지) | ||
| - 구현이 매우 복잡하고 모바일 환경에 부적합 | ||
| - Access Token이 만료되지 않아 보안 취약점 존재 | ||
|
|
||
| --- | ||
|
|
||
| ### OAuth 2.0 | ||
|
|
||
| OAuth 2.0은 2012년에 등장한 개선 버전으로, HTTPS를 기본으로 전제하여 서명 로직을 제거하고 구현을 단순화했다. 현재 카카오, 구글, 네이버 등 대부분의 소셜 로그인이 OAuth 2.0을 사용한다. | ||
|
|
||
| ``` | ||
| [OAuth 2.0 Authorization Code 흐름 - 가장 많이 사용] | ||
| 1. Client → Resource Owner: 인증 페이지로 리다이렉트 | ||
| (예: https://kauth.kakao.com/oauth/authorize?client_id=...&redirect_uri=...) | ||
| 2. Resource Owner: 카카오 로그인 + 권한 허가 | ||
| 3. Authorization Server → Client: Authorization Code 전달 (redirect_uri로) | ||
| 4. Client → Authorization Server: Access Token 요청 (Authorization Code 사용) | ||
| 5. Authorization Server → Client: Access Token + Refresh Token 발급 | ||
| 6. Client → Resource Server: API 요청 (Access Token) | ||
| 7. Resource Server → Client: 사용자 정보 반환 | ||
| ``` | ||
|
|
||
| **Spring Boot에서 카카오 OAuth 2.0 설정 예시:** | ||
|
|
||
| ```yaml | ||
| spring: | ||
| security: | ||
| oauth2: | ||
| client: | ||
| registration: | ||
| kakao: | ||
| client-id: ${KAKAO_CLIENT_ID} | ||
| client-secret: ${KAKAO_CLIENT_SECRET} | ||
| redirect-uri: "{baseUrl}/login/oauth2/code/kakao" | ||
| authorization-grant-type: authorization_code | ||
| scope: profile_nickname, account_email | ||
| provider: | ||
| kakao: | ||
| authorization-uri: https://kauth.kakao.com/oauth/authorize | ||
| token-uri: https://kauth.kakao.com/oauth/token | ||
| user-info-uri: https://kapi.kakao.com/v2/user/me | ||
| user-name-attribute: id | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ### OAuth 1.0 vs OAuth 2.0 비교 | ||
|
|
||
| | 구분 | OAuth 1.0 | OAuth 2.0 | | ||
| | --- | --- | --- | | ||
| | 등장 시기 | 2007년 | 2012년 | | ||
| | 보안 방식 | HMAC-SHA1 서명 (모든 요청에 서명 포함) | HTTPS 기반 (서명 없음) | | ||
| | 구현 복잡도 | 높음 (서명 로직 직접 구현) | 낮음 (라이브러리 활용 가능) | | ||
| | 토큰 종류 | Request Token + Access Token | Access Token + Refresh Token | | ||
| | 토큰 만료 | 만료 없음 (보안 취약) | 만료 있음 + Refresh Token으로 재발급 | | ||
| | 모바일 지원 | 부적합 (복잡한 서명 로직) | 적합 (다양한 Grant Type 지원) | | ||
| | Grant Type | 단일 방식 | 4가지 방식 (Authorization Code, Implicit, Client Credentials, Resource Owner Password) | | ||
| | 현재 사용 | 거의 사용 안 함 | 표준으로 사용 (카카오, 구글, 네이버 등) | | ||
|
|
||
| --- | ||
|
|
||
| ### OAuth 2.0의 4가지 Grant Type | ||
|
|
||
| | Grant Type | 설명 | 주요 사용 사례 | | ||
| | --- | --- | --- | | ||
| | Authorization Code | 인증 코드를 통해 토큰 발급 (가장 안전) | 소셜 로그인 (카카오, 구글 등) | | ||
| | Implicit | 인증 코드 없이 바로 토큰 발급 (현재 권장 안 함) | SPA (현재는 PKCE로 대체) | | ||
| | Client Credentials | 사용자 없이 서버 간 인증 | MSA 서비스 간 통신 | | ||
| | Resource Owner Password | 사용자 ID/PW를 직접 전달 (권장 안 함) | 신뢰할 수 있는 1st-party 앱 | | ||
|
|
||
| --- | ||
|
|
||
| ### 정리 | ||
|
|
||
| OAuth 2.0은 OAuth 1.0의 복잡한 서명 메커니즘을 HTTPS로 대체하여 구현을 대폭 단순화했다. 현재 대부분의 소셜 로그인은 OAuth 2.0의 Authorization Code Grant Type을 사용하며, Spring Security는 `spring-security-oauth2-client` 라이브러리를 통해 이를 손쉽게 구현할 수 있도록 지원한다. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
토큰 기반 인증에서 서버가 DB를 전혀 조회하지 않는다고 단정하면 Refresh Token 저장소, 토큰 블랙리스트, 사용자 상태 확인 같은 예외 전략이 빠져 보일 수 있습니다. Access Token 검증만 놓고 보면 서명 검증으로 처리할 수 있지만, 재발급·로그아웃·강제 만료 정책에서는 서버 저장소 조회가 필요할 수 있음을 함께 구분하는 것을 권장합니다.