Spring/Spring Security

[Spring Security] 6-1. OAuth2

noahkim_ 2025. 7. 13. 06:05

1. OAuth2 Resource Server

  • 보호된 자원에 대한 요청에 대하여, 인증 과정을 거쳐 자원을 응답하는 서버

 

Protect Access with an OAuth2 Access Token

구분 설명 사용 구성
JWT 자체 서명 토큰 (일반적인 OAuth2/JWT 기반 API 보호) 자체 디코딩 가능
(검증용 public key만 있으면 됨)
Opaque Token 토큰 내용을 알 수 없는 토큰 introspection 엔드포인트로 검증
(외부 인증 서버에서 introspection 제공 시 사용 가능)

 

설정) JWT

더보기
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://my-auth-server.com
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt());
            
        return http.build();
    }

    @Bean
    public JwtDecoder jwtDecoder() {
        return JwtDecoders.fromIssuerLocation("https://my-auth-server.com");
    }
}

 

설정) Opaque Token

더보기
spring:
  security:
    oauth2:
      resourceserver:
        opaquetoken:
          introspection-uri: https://my-auth-server.com/oauth2/introspect
          client-id: my-client-id
          client-secret: my-client-secret
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2.opaqueToken());
        
        return http.build();
    }

    @Bean
    public OpaqueTokenIntrospector opaqueTokenIntrospector() {
        return new SpringOpaqueTokenIntrospector(
            "https://my-auth-server.com/oauth2/introspect",
            "my-client-id",
            "my-client-secret");
    }
}

 

Protect Access with a custom JWT

  • 수동으로 직접 발급된 Access Token을 검증하는 방법
  • BearerTokenAuthenticationFilter + JwtAuthenticationProvider (NimbusJwtDecoder)

 

설정) Custom Access Token 검증

더보기
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          public-key-location: classpath:my-public-key.pub
  • 공개키만 등록하면 됨 (NimbusJwtDecoder)

 

2. OAuth2 Client

  • 로그인 처리하거나 API 요청에 사용할 수 있게 해주는 애플리케이션
  • Spring 앱이 외부 인증/인가 서버와 통신

 

설정) client 등록

더보기

사용할 외부 인증 서버에 대한 정보 등록

spring:
  security:
    oauth2:
      client:
        registration:
          my-oidc-client:
            provider: my-oidc-provider
            client-id: my-client-id
            client-secret: my-client-secret
            authorization-grant-type: authorization_code
            scope: openid,profile
        provider:
          my-oidc-provider:
            issuer-uri: https://my-oidc-provider.com

 

항목 경로 설명
spring.security.oauth2.client.registration.<registration-id> OAuth2 로그인용 클라이언트 등록 설정
registration.provider 연결할 인증 서버 설정명 (아래 provider 항목 참조)
registration.client-id 외부 인증 서버에서 발급받은 클라이언트 ID
registration.client-secret 외부 인증 서버에서 발급받은 클라이언트 비밀번호
registration.authorization-grant-type 권한 부여 방식
registration.scope 인증 요청 시 필요한 정보 범위
(OIDC의 경우 반드시 openid 포함)
spring.security.oauth2.client.provider.<provider-id> 인증 서버 설정
provider.issuer-uri OIDC 메타데이터를 조회할 인증 서버 주소
  • ClientRegistrationRepository에 등록됨

 

Grant Type

  • access token을 얻는 방식
  • 상황에 따라 받는 방식이 달라지므로 여러 종류의 grant type이 정의됨
Grant Type 설명 보안 고려사항 사용 주체 사용 예시
authorization_code user-agent가 로그인 후, Authorization Code를 받아 access_token 발급 가장 안전한 방식
(리디렉션 + 서버 측 처리)
user 소셜 로그인
refresh_token access_token이 만료되었을 때, refresh_token으로 새 access_token 재발급 refresh_token은 장기 보관되므로 유출 시 위험 user 자동 로그인 유지
client_credentials 사용자 없이, client id/secret만으로 access_token 발급 클라이언트 자격이 노출되면 악용 가능 server 백엔드 API 호출
스케줄러
배치
jwt_bearer 서명된 JWT를 통해 access_token 발급 (assertion 기반) JWT 위변조/만료 검증 필요 server
3rd party 시스템 연동
token_exchange 기존 access_token을 다른 시스템의 token으로 교환 원본 토큰의 권한 관리가 중요 server
마이크로서비스 간 인증

 

Login

항목 OIDC (OpenID Connect)
OAuth 2.0
주요 목적 사용자 인증 (누구인지 확인)
권한 부여 (무엇을 할 수 있는지 허가)
사용 토큰 ID Token (JWT)
Access Token (리소스 접근 권한을 부여받은 토큰)
사용자 정보 포함 여부 ✔️ ID Token에 사용자 정보 포함됨 (sub, email 등)
❌  별도 API 호출 필요
사용 위치 내 애플리케이션의 로그인 인증 처리
외부 API 사용 (예: Kakao, Google API)
scope 'openid' 포함 ✅ 
표준 확장 규격 여부 ✅ OAuth2의 인증 기능 확장
🔶 인증 자체는 포함하지 않음
리턴 시 포함 토큰 ID Token, Access Token
Access Token, (선택적 Refresh Token)
provider 클래스 OidcAuthorizationCodeAuthenticationProvider OAuth2AuthorizationCodeAuthenticationProvider

 

흐름) 로그인

더보기
단계 주체 동작 내용 관련 컴포넌트
1 User-Agent 클라이언트에서 제공한 OAuth2 로그인 페이지 접속 DefaultLoginPageGeneratingFilter
(/oauth2/authorization/{registrationId})
2 Client Authorization Server의 로그인 페이지로 리다이렉션 OAuth2AuthorizationRequestRedirectFilter
3 User-Agent Authorization Server에 로그인 요청 (브라우저를 통한 직접 요청)
4 Authorization Server 로그인 처리 및 인증 결과 생성
→ redirect_uri로 리다이렉션 (code 포함)
(서버 내부 인증 처리)
(/login/oauth2/code/{registrationId})
5 Client 받은 authorization code로 토큰 및 사용자 정보 요청 OAuth2LoginAuthenticationFilter

 

구현체) AuthenticationProvider

더보기
클래스명 주요 역할 및 설명
OAuth2LoginAuthenticationProvider OAuth2 로그인 흐름
- Authorization Code → Access Token 교환
- 인증 토큰 생성
- OAuth2AuthorizationCodeAuthenticationProvider OAuth2 로그인 흐름
- Authorization Code → Access Token 교환
- OidcAuthorizationCodeAuthenticationProvider OIDC 로그인 흐름
- ID Token, Access Token 처리
OidcBackChannelLogoutAuthenticationProvider
OIDC 로그아웃 흐름
- OIDC Back-Channel Logout 요청을 처리하는 Provider
- 서버 간 로그아웃 연동 지원

 

구현체) OAuth2UserService

더보기

Access Token → Userinfo 를 가져오는 것을 담당하는 클래스

항목 OidcUserService DefaultOAuth2UserService
사용 조건 scope에 openid 포함됨 (OpenID Connect) scope에 openid 없음 (순수 OAuth2)
사용 목적 사용자 인증 + 정보 조회 (id_token + access token 기반) 사용자 정보 조회용 (access token 기반)
호출 대상 id_token + 선택적 user-info-uri user-info-uri
반환 타입 OidcUser (extends OAuth2User) OAuth2User
토큰 활용 방식 id_token을 먼저 파싱 → 필요 시 user info endpoint 호출 access_token으로 user info endpoint 호출
사용 위치 OidcAuthorizationCodeAuthenticationProvider OAuth2LoginAuthenticationProvider

 

설정) oauth2Login

더보기

OAuth2Login()

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        // ...
        .oauth2Login(Customizer.withDefaults());
        
    return http.build();
}
  • Spring Security에서 OAuth2/OIDC 로그인 흐름을 처리

 

server-to-server

항목 Client Credentials Flow (client_credentials)
Extension Grant Type (custom_grant)
주체 사용자 ❌ 없음
→ 클라이언트(client_id) 자체가 주체
정의에 따라 유동적
(예: JWT, SAML, 내부 토큰 등)
용도 서버-서버 API 호출
백엔드 시스템 인증
특수 목적
- token_exchange, jwt_bearer, 커스텀 인증 흐름
표준 여부 ✅ OAuth2.0 공식 표준 grant type
❌ OAuth2 확장 사양 (RFC 8693 등 또는 사용자 정의)
사용되는 토큰 access_token만 발급됨 (refresh_token ❌)
access_token (+ 옵션으로 refresh_token)
Spring에서 지원 여부 ✅ 기본 내장
❌ 직접 구현 필요
access token 관리 클라이언트별로 분리 가능
(principal 기반 OAuth2AuthorizationService 저장)
직접 처리 필요
(토큰 컨텍스트/스코프 등 커스터마이징 필요)

 

 

흐름) server-to-server

더보기
단계 구성 요소 역할 설명 비고 / 주요 메서드
1 OAuth2ClientHttpRequestInterceptor RestClient 요청 시 자동으로 access token을 Authorization 헤더에 실어주는 인터셉터 Bearer <access_token>
2 OAuth2AuthorizedClientManager 인증된 client에서 access token 획득
(필요시 재인증 또는 재발급 흐름을 관리함)
authorize(OAuth2AuthorizeRequest)
3 OAuth2AuthorizedClientProvider 다양한 grant type 처리 내부적으로 token 발급 처리
4 OAuth2AuthorizedClientService 발급된 access token 및 관련 정보를 저장하고 재사용할 수 있도록 관리 저장소 구현체 존재
(DB/메모리 등)

 

표) extension grant type

더보기
항목 jwt-bearer token-exchange
Grant Type urn:ietf:params:oauth:grant-type:jwt-bearer urn:ietf:params:oauth:grant-type:token-exchange
주요 목적 기존 JWT 토큰을 사용해 Access Token 발급받기 기존 토큰을 새로운 토큰으로 교환
권한 위임 여부 ❌ 없음 (자기 자신 인증) ✅ 있음 (토큰 위임 및 재발급)
사용자 정보 포함 ❌ 없음 (클라이언트 인증 중심) ✅ 있음 (sub, actor 등 교환 대상 지정)
Provider JwtBearerOAuth2AuthorizedClientProvider
TokenExchangeOAuth2AuthorizedClientProvider
사용 예시 사전에 발급받은 JWT를 제출해 자원 권한 요청 사용자가 받은 외부 토큰을 자사 API용으로 교환

 

설정) RestClient

더보기
@Bean
public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {
    OAuth2ClientHttpRequestInterceptor requestInterceptor =
            new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);

    // 사용자가 아닌, 요청 속성에 지정된 "가상의 주체" 이름을 principal로 사용
    requestInterceptor.setPrincipalResolver(new RequestAttributePrincipalResolver());

    return RestClient.builder()
            .requestInterceptor(requestInterceptor)
            .build();
}

 


설정) OAuth2AuthorizedClientProvider

더보기

 jwt-bearer

@Bean
public OAuth2AuthorizedClientProvider jwtBearer() {
    return new JwtBearerOAuth2AuthorizedClientProvider();
}
  • 사용하고 싶은 확장 인증 방식의 구현체를 빈으로 등록하기

 

Customize the RestOperations used by OAuth2 Client Components

  • OAuth2 Client가 토큰을 요청할 때 사용하는 HTTP 클라이언트를 직접 설정할 수 있음

 

설정) OAuth2AcccessTokenResposneClient

더보기
@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
    var client = new DefaultAuthorizationCodeTokenResponseClient();
    client.setRestOperations(restTemplate()); // 커스터마이징된 RestTemplate 주입
    return client;
}

 

설정) RestTemplate

더보기
@Bean
public RestTemplate restTemplate() {
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
    factory.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.mycorp.com", 8080)));
    factory.setConnectTimeout(3000);
    factory.setReadTimeout(3000);

    RestTemplate restTemplate = new RestTemplate(factory);
    restTemplate.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));
    return restTemplate;
}
목적 설명
🔧 프록시 설정
기업 내부망에서 HTTP/HTTPS 프록시 설정 필요할 때
🔐 커스텀 인증 헤더 추가
예: mTLS, Basic 인증, 커스텀 헤더
📦 메시지 변환기 교체
JSON → XML 또는 특수한 응답 파싱이 필요할 때
🧪 로깅 또는 트레이싱
요청/응답 로깅, Zipkin, Sleuth, etc. 연동

 

출처