🌐 Spring Security + OAuth2 (Google / Kakao) 로그인 정리
JWT·쿠키 기반 로그인에 이어 OAuth2(소셜 로그인) 인증 방식을 함께 구성하는 방법을 정리.
SecurityConfig 클래스의 시큐리티 체인 설정과 application.properties 파일의 클라이언트 등록 정보를 함께 설명함.
1️⃣ OAuth2 로그인 개요
| 항목 | 설명 |
|---|
| 정의 | 외부 서비스(Google, Naver, Kakao 등)의 인증 서버를 통해 로그인 처리하는 프로토콜 |
| 목적 | 자체 회원가입 없이 외부 플랫폼 계정으로 인증 |
| 특징 | Access Token 기반 인증 / 서버가 사용자 비밀번호를 직접 처리하지 않음 |
| 활용 구조 | OAuth2 → Access Token 발급 → 사용자 정보 요청 → JWT 발급 / 쿠키 저장 등 내부 로직 연계 가능 |
2️⃣ Gradle 의존성
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
3️⃣ application.properties 설정
# [1] 공통 OAuth2 설정
spring.security.oauth2.client.registration.google.client-id=구글클라이언트ID
spring.security.oauth2.client.registration.google.client-secret=구글시크릿키
spring.security.oauth2.client.registration.google.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.google.scope=email,profile
# Naver
spring.security.oauth2.client.registration.naver.client-id=네이버클라이언트ID
spring.security.oauth2.client.registration.naver.client-secret=네이버시크릿키
spring.security.oauth2.client.registration.naver.client-name=Naver
spring.security.oauth2.client.registration.naver.redirect-uri={baseUrl}/login/oauth2/code/naver
spring.security.oauth2.client.registration.naver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.naver.scope=name,email
# Naver 인증 서버 정보
spring.security.oauth2.client.provider.naver.authorization-uri=https://nid.naver.com/oauth2.0/authorize
spring.security.oauth2.client.provider.naver.token-uri=https://nid.naver.com/oauth2.0/token
spring.security.oauth2.client.provider.naver.user-info-uri=https://openapi.naver.com/v1/nid/me
spring.security.oauth2.client.provider.naver.user-name-attribute=response
# Kakao
spring.security.oauth2.client.registration.kakao.client-id=카카오RESTAPI키
spring.security.oauth2.client.registration.kakao.client-secret=카카오로그인시크릿키
spring.security.oauth2.client.registration.kakao.redirect-uri={baseUrl}/login/oauth2/code/kakao
spring.security.oauth2.client.registration.kakao.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.kakao.client-authentication-method=POST
spring.security.oauth2.client.registration.kakao.scope=profile_nickname,account_email
spring.security.oauth2.client.registration.kakao.client-name=Kakao
# Kakao 인증 서버 정보
spring.security.oauth2.client.provider.kakao.authorization-uri=https://kauth.kakao.com/oauth/authorize
spring.security.oauth2.client.provider.kakao.token-uri=https://kauth.kakao.com/oauth/token
spring.security.oauth2.client.provider.kakao.user-info-uri=https://kapi.kakao.com/v2/user/me
spring.security.oauth2.client.provider.kakao.user-name-attribute=id
💡 redirect-uri 는 OAuth2 로그인 성공 후 돌아올 경로를 지정함.
{baseUrl} 은 기본적으로 http://localhost:8080 (Spring Boot 서버 주소)로 대체됨.
4️⃣ SecurityConfig 설정
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
private final OAuth2SuccessHandler oAuth2SuccessHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER","ADMIN")
.requestMatchers("/**").permitAll()
);
http.csrf(csrf -> csrf.disable());
// [2] 세션 정책 - JWT 사용 시 stateless
http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
// [3] OAuth2 로그인 설정
http.oauth2Login(oauth -> oauth
.loginPage("/login") // 로그인 페이지 지정 (없을 시 스프링 기본 폼)
.successHandler(oAuth2SuccessHandler) // 로그인 성공 후 처리
.failureUrl("/login?error=true") // 실패 시 이동
);
return http.build();
}
}
🔍 주요 메서드 설명
| 메서드 | 설명 |
|---|
.oauth2Login() | OAuth2 로그인 진입점 설정 |
.loginPage("/login") | 커스텀 로그인 페이지 지정 |
.successHandler() | 인증 성공 시 사용자 정보를 가공하여 후처리 (예: JWT 생성, 쿠키 저장 등) |
.failureUrl() | 로그인 실패 시 이동할 페이지 지정 |
.sessionCreationPolicy(STATELESS) | JWT 인증 시 세션을 유지하지 않음 |
5️⃣ OAuth2SuccessHandler — 로그인 성공 처리 로직
@Component
@RequiredArgsConstructor
public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final JwtService jwtService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
String email = oAuth2User.getAttribute("email");
String token = jwtService.createToken(email);
ResponseCookie cookie = ResponseCookie.from("AUTH", token)
.httpOnly(true)
.secure(true)
.sameSite("Lax")
.path("/")
.maxAge(Duration.ofDays(1))
.build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
response.sendRedirect("http://localhost:5173");
}
}
📘 주요 파라미터 설명
| 파라미터 | 설명 |
|---|
OAuth2User | 인증 서버(Google/Naver/Kakao)로부터 전달된 사용자 정보 객체 |
getAttribute("email") | 사용자 프로필 중 이메일 필드 추출 |
JwtService | JWT 토큰 발급 서비스 (이전 JWT 코드 재사용 가능) |
ResponseCookie | 쿠키에 JWT 저장. React 등 클라이언트에서 자동 전송됨 |
sendRedirect() | 로그인 성공 후 프런트엔드로 이동 |
6️⃣ OAuth2 인증 흐름 요약
사용자 → [React 로그인 버튼 클릭] → /oauth2/authorization/{provider}
→ 해당 소셜 로그인 페이지로 이동
→ 사용자 동의 → Spring Security OAuth2 Client → AccessToken 획득
→ UserInfo 요청 → OAuth2User 생성
→ OAuth2SuccessHandler 실행 → JWT 발급 → 쿠키 저장 → React 리다이렉트
7️⃣ JWT·쿠키 통합 구조
| 항목 | 설명 |
|---|
| JWT 발급 위치 | OAuth2SuccessHandler 내부 (jwtService.createToken()) |
| 쿠키 저장 위치 | ResponseCookie(HttpOnly, Secure, SameSite=Lax) |
| JWT 검증 위치 | JwtAuthFilter (기존과 동일) |
| 세션 | 사용하지 않음 (STATELESS) |
| CORS | React 포트 허용 필요 (CorsConfig에서 5173, 5174 등 지정) |
✅ 핵심 요약
| 항목 | 내용 |
|---|
| 인증 방식 | JWT + Cookie + OAuth2 통합 구조 |
| 핵심 클래스 | SecurityConfig, OAuth2SuccessHandler, JwtService |
| OAuth2 Provider | Google, Naver, Kakao |
| JWT 저장 위치 | HttpOnly 쿠키 |
| 보안 정책 | CSRF disable (개발용), SameSite=Lax, Secure, HttpOnly |
| 세션 정책 | Stateless (JWT 기반) |