Spring Boot OAuth2 인증

Hinolog·2025년 5월 14일

MSA

목록 보기
1/5

목차

  1. 프로젝트 개요
  2. 보안 설정 - SecurityConfig
  3. OAuth2 로그인 흐름 이해
  4. 성공 핸들링 - SuccessHandler
  5. MSA 구조 - 서비스 연동
  6. 트러블슈팅 기록
  7. 다음 구현 예정
  8. 마무리

1. 프로젝트 개요

서비스 목표

  • MSA 환경에서 인증/인가를 전담하는 독립형 서비스
  • Google OAuth2를 통해 소셜 로그인 구현
  • JWT를 발급하여 무상태(stateless) 인증 처리

🛠 사용 스택

항목내용
LanguageJava 17
FrameworkSpring Boot 3.4.3
인증 방식OAuth2 (Google) + JWT
세션 관리Stateless (세션 미사용)
향후 연동Redis (Refresh Token 저장 예정)
MSA 구성Spring Cloud Config, Eureka, Gateway

프로젝트 아키텍처

[클라이언트] 
    ↓ ↑
[API Gateway] ⟷ [Eureka Server]
    ↓ ↑           ↑
[Auth-Service] → [Config Server]
    ↓ ↑
[Redis] (예정)

아키텍처 설명:

  • Gateway를 통해 모든 요청이 라우팅됩니다.
  • Eureka가 서비스 디스커버리 역할을 담당합니다.
  • Config Server에서 중앙 집중식 설정 관리를 합니다.
  • Auth-Service는 인증 서비스로, 추후 다른 마이크로서비스와 연동됩니다.

2. 보안 설정 - SecurityConfig

🔧 기본 설정

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .csrf(csrf -> csrf.disable())
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/").permitAll()
            .anyRequest().authenticated())
        .oauth2Login(oauth -> oauth
            .userInfoEndpoint(userInfo -> userInfo
                .userService(customOAuth2UserService)))
        .successHandler(oAuth2SuccessHandler);
    return http.build();
}

설정 포인트

  • CSRF 비활성화: API 서버는 세션을 사용하지 않기 때문에 불필요
  • SessionCreationPolicy.STATELESS: JWT 기반 인증은 세션 정보를 저장하지 않음
  • OAuth2 로그인 설정: 사용자 정보 후처리를 위해 CustomOAuth2UserService 등록
  • SuccessHandler 적용: 로그인 성공 후 직접 응답 제어 가능

3. OAuth2 로그인 흐름 이해

📈 로그인 성공까지의 흐름

[사용자 로그인 요청]
  → [Google 인증 페이지로 이동]
    → [OAuth2 인증 성공]
      → [CustomOAuth2UserService 실행]
        → [SuccessHandler 호출 → 직접 응답]

CustomOAuth2UserService 구현

@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) {
        // 기본 유저 정보 받아오기
        OAuth2User oAuth2User = super.loadUser(userRequest);

        // CustomOAuth2User로 래핑해서 리턴
        return new CustomOAuth2User(oAuth2User);
    }
}

CustomOAuth2User 구현

@RequiredArgsConstructor
public class CustomOAuth2User implements OAuth2User {

    private final OAuth2User oAuth2User;

    @Override
    public <A> A getAttribute(String name) {
        return OAuth2User.super.getAttribute(name);
    }

    @Override
    public Map<String, Object> getAttributes() {
        return oAuth2User.getAttributes();
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of();
    }

    @Override
    public String getName() {
        return oAuth2User.getName();
    }

    public String getEmail() {
        return (String) oAuth2User.getAttributes().get("email");
    }
}

구현 포인트:

  • 기본 OAuth2User를 감싸서 필요한 기능을 확장.
  • email 정보를 쉽게 가져올 수 있는 메소드를 추가.
  • 추후 사용자 권한 관리나 추가 정보 처리를 위한 확장이 용이합니다.

4. 성공 핸들링 - SuccessHandler

문제 상황

  • JWT 발급 로직이 아직 없어 로그인 성공 여부를 확인하기 어려움
  • 프론트 없이 백엔드 단독으로 테스트할 방법이 필요

해결 방법: SuccessHandler로 JSON 응답 반환

OAuth2 인증이 성공하면 로그인한 사용자의 이메일 정보를 JSON으로 응답합니다.

@Component
public class OAuth2SuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException {
        try {
            CustomOAuth2User user = (CustomOAuth2User) authentication.getPrincipal();
            String email = user.getEmail();

            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().write("{\"email\": \"" + email + "\"}");
        } catch (Exception e) {
            response.setContentType("application/json");
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write("{\"error\": \"" + e.getMessage() + "\"}");
        }
    }
}

Tip:
프론트엔드 없이도 브라우저에서 바로 OAuth 로그인 성공 여부를 확인할 수 있습니다!
/oauth2/authorization/google → 로그인 완료 → JSON 응답 { "email": "user@gmail.com" }

개선 사항:
예외 처리 로직을 추가하여 에러 상황에서도 클라이언트에게 적절한 응답을 주도록 했습니다.


5. MSA 구조 - 서비스 연동

설정 파일 (application.yml)

server:
  port: 0
spring:
  application:
    name: auth-service
  # config-server
  config:
    import: configserver:http://localhost:8888
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: [YOUR_CLIENT_ID]
            client-secret: [YOUR_CLIENT_SECRET]
            redirect-uri: http://localhost:8080/login/oauth2/code/google
            scope: profile, email
        provider:
          google:
            authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
            token-uri: https://oauth2.googleapis.com/token
            user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
            user-name-attribute: sub
eureka:
  instance:
    instance-id: ${spring.cloud.client.ip-address}:${random.value}

설정 포인트

  • 포트 0 설정: 랜덤 포트 사용으로 여러 인스턴스 실행 가능
  • Config Server 연동: 설정 정보를 중앙에서 관리
  • OAuth2 클라이언트 설정: Google 로그인을 위한 기본 설정
  • Eureka 인스턴스 설정: 서비스 디스커버리를 위한 인스턴스 ID 구성

팁:

  • Config Server에 민감한 정보(client-id, secret 등)를 보관하면 더 안전합니다.
  • 랜덤 포트(port: 0)를 사용하면 같은 서비스의 여러 인스턴스를 쉽게 실행할 수 있습니다.
  • Gateway 서비스에서 Auth-Service로의 라우팅 규칙도 설정해야 합니다.

6. 트러블슈팅 기록

상황해결 방법
로그인 성공 여부 확인 불가SuccessHandler에서 JSON 직접 반환
사용자 정보 디버깅 필요Authentication 객체 로그로 찍기
프론트 없는 상태에서 테스트브라우저 직접 호출로 반복 테스트 가능
예외 상황 처리 미흡SuccessHandler에 try-catch 추가
랜덤 포트 사용시 접근 문제Gateway 라우팅 + Eureka 디스커버리로 해결

핵심 포인트:
JWT 발급 이전 단계에서도, OAuth2 인증 성공을 독립적으로 검증할 수 있게 됨.
MSA 환경에서는 서비스 디스커버리와 게이트웨이 설정이 매우 중요함.


7. 다음 구현 예정 🛠

앞으로 이어질 작업

  • JWT AccessToken / RefreshToken 발급 구현
    • 예상 구현: AccessToken은 짧은 유효기간(30분), RefreshToken은 긴 유효기간(7일)
    • 토큰에 담길 정보: 사용자 ID, 이메일, 권한 정보
  • JwtAuthenticationFilter / JwtExceptionFilter 구성
    • 모든 API 요청에 JWT 검증 로직 적용
    • 토큰 만료/위변조 등의 예외 상황 처리
  • RefreshToken 저장을 위한 Redis 연동
    • Redis TTL 기능을 활용한 RefreshToken 자동 만료 처리
    • Token Blacklist 관리 기능 추가
  • OAuth2 사용자 → 기존/신규 회원 분기 처리
    • 최초 로그인 시 회원 정보 자동 등록
    • 기존 회원은 로그인 시간 업데이트

테스트 방법

Postman으로 테스트:
1. OAuth2 로그인: GET /oauth2/authorization/google로 브라우저에서 요청


8. 마무리

여기까지, OAuth2 로그인 성공 → SuccessHandler 응답 처리까지 구현 완료했습니다! 🎉
MSA 환경에서 Config Server, Eureka, Gateway와의 연동 방법도 간략히 소개했습니다.


0개의 댓글