세션과 JWT 의 차이
세션 Stateful
- 로그인 성공 시, 인증 정보를 서버 세션(`HttpSession`)에 저장
- 클라이언트 요청 시, 세션 ID 를 쿠키로 전송
- 서버는 세션 ID 로 인증 정보를 조회해 로그인 상태 유지
장점
: 구현이 간단하고, Spring 에서 기본적으로 지원한다.
: 서버가 사용자 인증 상태를 관리하기 때문에 비교적 보안성이 높다.
단점
: 서버가 상태를 유지해야 하므로 확장성이 낮고, 분산 환경에 부적합하다.
: 서버 간 세션 동기화가 필요하다.
: 인증 정보가 많아질수록 서버 리소스 부담이 증가한다.
JWT Stateless
- 로그인 성공 시, JWT 토큰 생성하여 클라이언트에게 전달
- 클라이언트 요청 시, JWT 토큰을 헤더 or 쿠키에 담아 전송
- 서버는 JWT 의 서명을 검증하여 사용자 인증
장점
: 서버에 인증 상태를 저장하지 않아도 되므로 확장성과 유연성이 높다.
: `모바일`/`SPA`/`REST API` 에 최적화되어 있다.
: 토큰 자체에 사용자 정보를 담아 DB 조회 없이 인증이 가능하다.
단점
: JWT 탈취 시 위험이 있어 토큰 재발급/무효화가 어렵다.
: 리프레시 토큰과 만료 정책 설계가 필수적이다.
: 구현 난이도와 보안 설계 부담이 증가한다.
이때 `Spring Security` 는 기본적으로 `SecurityContext` 를 세션에 저장하는 것이기 때문에, 아래 설정을 명시해야 세션을 사용하지 않고, 요청마다 매번 재검증을 진행한다.
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
일반 로그인 흐름 비교
Spring Security + 세션 기반 로그인
1. `UsernamePasswordAuthenticationFilter` : 일반 로그인 요청 감지 (`/login`)
(이 때, `GetMappint(/login)` 은 매핑되지 않으므로 커스텀 하고싶으면 `formlogin` 을 비활성화 해야 한다.)
2. `AuthenticationManager` : 인증 로직 실행
3. `UserDetailsService` : 사용자 정보 조회
4. 인증 성공 시
: `SecurityContextHolder` 에 인증 정보 저장
: `HttpSession` 에 `SecurityContext` 인증 정보 저장하여 다음 요청에도 로그인 유지
Spring Security + JWT 기반 로그인
1. `UsernamePasswordAuthenticationFilter` : 일반 로그인 요청 감지 (`/login`)
(이 때, `GetMappint(/login)` 은 매핑되지 않으므로 커스텀 하고싶으면 `formlogin` 을 비활성화 해야 한다.)
2. 인증 성공 시: JWT 토큰 생성 후 클라이언트에 반환
: 이후 요청부터는 `JwtAuthenticationFilter` 가 JWT 토큰 유효성 검사
: 유효한 경우 `SecurityContextHolder` 에 인증 정보 저장
OAuth2 로그인 흐름 비교
Spring Security + Oauth2 + 세션 기반 로그인
1. `OAuth2LoginAuthenticationFilter` : 소셜 로그인 요청 감지 (`/oauth2/authorization/{provider}`)
2. `OAuth2UserService` : 소셜 사용자 정보 조회
3. 인증 성공 시
: `SecurityContextHolder` 에 인증 정보 저장
: `HttpSession` 에 `SecurityContext` 인증 정보 저장하여 다음 요청에도 로그인 유지
Spring Security + Oauth2 + JWT 기반 로그인
1. `OAuth2LoginAuthenticationFilter` : 소셜 로그인 요청 감지 (`/oauth2/authorization/{provider}`)
2. 인증 성공 시: JWT 토큰 생성 후 클라이언트에 반환
: 이후 요청부터는 `JwtAuthenticationFilter` 가 JWT 토큰 유효성 검사
: 유효한 경우 `SecurityContextHolder` 에 인증 정보 저장
프론트와 백엔드의 책임 분리 (OAuth2 + JWT)
모든 책임을 프론트에게
(프론트)
: 소셜 로그인 버튼
: 로그인 요청 로직
(외부 소셜 로그인 서비스의 `인증 서버`)
- 로그인 페이지에서 로그인
- 코드 발급
: 코드로 토큰 요청
(외부 소셜 로그인 서비스의 `인증 서버`)
- 토큰 발급
: 토큰으로 유저 요청
(외부 소셜 로그인 서비스의 `리소스 서버`)
- 유저 정보 발급
: 유저 정보 전송
(백엔드)
> 유저 정보 확인 후 JWT 발급
: JWT 획득
프론트 : 로그인 > 코드 발급 > Access 토큰 > 유저 정보 획득
백엔드 : 유저 정보 > JWT 발급
이때, 백엔드는 프론트에서 보낸 유저 정보의 진위 여부를 따지는 보안 로직이 필요하다.
모든 책임을 백엔드에게
(프론트)
: 소셜 로그인 버튼
(백엔드)
> 하이퍼링크로 API Get 요청
> 로그인 요청 로직
(외부 소셜 로그인 서비스의 `인증 서버`)
- 로그인 페이지에서 로그인
- 코드 발급
> 코드로 토큰 요청
(외부 소셜 로그인 서비스의 `인증 서버`)
- 토큰 발급
> 토큰으로 유저 요청
(외부 소셜 로그인 서비스의 `리소스 서버`)
- 유저 정보 발급
> 유저 정보 확인 후 JWT 발급
: JWT 획득
프론트 : 백엔드의 OAuth2 로그인 경로로 하이퍼링크
백엔드 : 로그인 > 코드 발급 > Access 토큰 > 유저 정보 획득 > JWT 발급
책임을 프론트와 백엔드가 나눠 가짐 (X)
잘못된 방식이다.
코드 또는 Access 토큰을 전송하는 방법은 지양해야 한다.
출처 | Spring OAuth2 클라이언트 JWT(개발자 유미) - 유튜브
'💠프로그래밍 언어 > Spring' 카테고리의 다른 글
[Spring] 트랜잭션 옵션과 전파에 대하여, 그리고 트랜잭션 주의사항까지 (0) | 2025.05.02 |
---|---|
[Spring] Querydsl 을 통해 동적 쿼리 해결하기 (1) | 2025.05.02 |
[Spring] 스프링 데이터 JPA 사용하기 (0) | 2025.05.02 |
[Spring] JPA 소개 및 사용 예시 (0) | 2025.05.02 |
[Spring] MyBatis 사용법 익히기 ! (1) | 2025.05.01 |