본문 바로가기
Java/Spring Boot

OAuth 구현

by curious week 2025. 3. 28.

 

목표

프론트에서 "구글 로그인"을 누르면
Spring 서버가 로그인 처리 → 사용자 정보 저장 → JWT 발급 → 클라이언트 전달
이 전체 흐름을 이해하는 게 목표야!


1. 전체 OAuth 로그인 흐름

[프론트]
사용자가 구글 로그인 버튼 클릭
   ↓
GET /oauth2/authorization/google
(Spring이 자동 제공하는 URL)

[Spring 서버]
→ Google 로그인 페이지로 리다이렉트
   ↓
→ 사용자가 로그인 후 동의
   ↓
Google → Spring으로 "code" 전송 (리디렉션 URL로)

[Spring 서버]
→ code를 access_token으로 교환
→ access_token으로 사용자 정보(email, name 등) 조회
→ OAuth2User 객체로 변환됨

2. 이후 흐름 (커스텀 처리)

[OAuth2User] (구글에서 받은 사용자 정보)
   ↓
OAuth2LoginSuccessHandler 실행
   ↓
 - DB에 유저 저장 or 조회
 - JWT 생성
 - 프론트로 리다이렉트 + 토큰 전달

Spring이 자동으로 해주는 것들

Spring boot 6.x.x 버전 이상이면 디펜던시에 아래 코드를 추가하고, 기능을 직접 구현할 필요 없음. 

    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
구글 로그인 페이지로 리다이렉트
code → access_token 교환
사용자 정보 가져오기
로그인 세션 생성 ❌ (JWT 쓴다면 우리가 커스터마이징)

커스터마이징하는 것들

유저 DB 저장/조회 OAuth2UserService or SuccessHandler
JWT 발급 JwtTokenProvider
프론트로 리디렉트 OAuth2LoginSuccessHandler

 

프론트

window.location.href = 'http://localhost:8080/oauth2/authorization/google';

Spring Security 설정

.oauth2Login(oauth -> oauth
  .successHandler(oAuth2LoginSuccessHandler)
)

성공 핸들러

OAuth2User user = (OAuth2User) authentication.getPrincipal();
String email = user.getAttribute("email");
// DB 저장 or 조회
// JWT 생성
// 프론트로 전달

흐름 단계

1단계 /oauth2/authorization/google 접속 (자동 생성됨)
2단계 구글 로그인 후 콜백 → OAuth2User 생성
3단계 OAuth2LoginSuccessHandler 에서 DB 저장 & JWT 발급
4단계 클라이언트로 JWT 전달 (리다이렉트 or 쿠키)

전체 흐름 요약

[1] 프론트: 구글 로그인 버튼 클릭 → Spring 서버 리디렉트
[2] 백엔드: 로그인 성공 → JWT 발급 + 리다이렉트
[3] 프론트: 리디렉트된 URL에서 accessToken 추출 → 저장

클라이언트 측

1. 로그인 버튼 (리디렉트)

// src/pages/auth/GoogleLoginButton.tsx

export const GoogleLoginButton = () => {
  const handleLogin = () => {
    window.location.href = 'http://localhost:8080/oauth2/authorization/google';
  };

  return (
    <button onClick={handleLogin}>
      구글 로그인
    </button>
  );
};

/oauth2/authorization/google ← 이건 Spring Security가 자동으로 만들어주는 URL!


2. 로그인 성공 후 리디렉트 받을 페이지

Spring 서버에서 아래처럼 리디렉션하면

response.sendRedirect("http://localhost:5173/oauth/success?token=" + accessToken);

→ 프론트에서는 이 페이지를 만들어서 token 추출 → 저장 → 리다이렉트 하면 됨!

// src/pages/oauth/GoogleSuccessPage.tsx

import { useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuthStore } from '@/stores/authStore';
import axios from 'axios';

export const GoogleSuccessPage = () => {
  const navigate = useNavigate();
  const location = useLocation();
  const { setAccessToken } = useAuthStore();

  useEffect(() => {
    const params = new URLSearchParams(location.search);
    const token = params.get('token');

    if (token) {
      setAccessToken(token);
      axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
      navigate('/home');
    } else {
      navigate('/auth');
    }
  }, []);

  return <div>로그인 중입니다...</div>;
};

3. 라우터 등록

import { GoogleSuccessPage } from '@/pages/oauth/GoogleSuccessPage';

<Route path="/oauth/success" element={<GoogleSuccessPage />} />

리프레시 토큰을 쿠키로 보내고 있다면?

  • 프론트에서 따로 다룰 필요 없이 withCredentials: true 설정만 잘해주면 됨
  • 지금은 accessToken만 처리하고 있으니까 localStorage or zustand에 저장하면 OK

결과 흐름

로그인 시작 /oauth2/authorization/google Spring Security가 구글로 리다이렉트
로그인 완료 /login/oauth2/code/google Spring이 자동으로 처리
JWT 발급 OAuth2LoginSuccessHandler JWT → /oauth/success?token=... 리다이렉트
프론트 수신 /oauth/success 토큰 저장 + 페이지 이동

 


Spring Security의 OAuth2Login은 기본적으로:

  • 사용자의 인증 상태(Session 또는 SecurityContext)만 유지하지
  • JWT 기반의 access + refresh 토큰 발급은 우리가 직접 구성해야

즉,  직접 로그인 처리할 때처럼:

ResponseEntity.ok()
  .header(HttpHeaders.SET_COOKIE, refreshCookie.toString())
  .body(new TokenResponse(accessToken));

같은 로직을 OAuth 로그인 성공 시에도 동일하게 넣어줘야 함.


해야 할 작업

OAuth2LoginSuccessHandler에서 다음처럼 처리:

@Component
@RequiredArgsConstructor
public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler {

    private final JwtTokenProvider jwtTokenProvider;
    private final RefreshTokenService refreshTokenService;

    @Override
	public void onAuthenticationSuccess(...) {
        // 1. OAuth2User에서 사용자 정보 가져오기
        OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
        String email = oAuth2User.getAttribute("email");

        // 2. 사용자 DB 저장 or 조회
        User user = userService.saveOrGetUser(email);

        // 3. access + refresh 토큰 발급
        String accessToken = jwtTokenProvider.createAccessToken(user.getId(), user.getEmail());
        String refreshToken = jwtTokenProvider.createRefreshToken(user.getId());

        // 4. refresh 토큰 저장 (ex: Redis)
        refreshTokenService.save(user.getId(), refreshToken, 7 * 24 * 60 * 60);

        // 5. refresh 토큰을 쿠키로 응답에 포함
        ResponseCookie cookie = jwtTokenProvider.createRefreshTokenCookie(refreshToken);
        response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());

        // 6. accessToken은 프론트로 리다이렉트 (query string 전달)
        response.sendRedirect("http://localhost:5173/oauth/success?token=" + accessToken);
	}
}
@Service
@RequiredArgsConstructor
public class OAuth2UserService {

    private final UserRepository userRepository;

    public User saveOrGetUser(String email, String name, String provider) {
        return userRepository.findByEmail(email)
                .orElseGet(() -> userRepository.save(User.oauth(email, name, provider)));
    }
}

다시 요약하면

accessToken 발급 일반 로그인 / OAuth 공통 ✅ 직접 생성
refreshToken 발급 일반 로그인에서만 처리됨 ✅ OAuth에도 직접 추가해야 함
refreshToken 저장 DB or Redis ✅ 별도 저장 필요
쿠키에 담기 응답 헤더에서 설정 ✅ 직접 넣어야 함 (HttpOnly)

 

 

'Java > Spring Boot' 카테고리의 다른 글

JPQL과 QueryDSL  (0) 2025.03.31
JPA  (3) 2025.03.31
Redis, kafka  (0) 2025.03.27
HTTP 상태 코드 종류  (0) 2025.03.27
JWT & CRSF  (0) 2025.03.25