(Spring Security 기반) OAuth2 vs 일반 로그인 / 세션 vs JWT 정리

728x90

 

세션과 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(개발자 유미) - 유튜브

728x90