Spring Security ‐ OAuth 2.0 Client Fundamentals - thought-corner/backend-roadmap GitHub Wiki

클라이언트 권한 부여 요청 시작

# application.yml 코드 발췌(keycloak)
spring:
  security:
    oauth2:
      client:
        registration:                                                      # 클라이언트 설정
          keycloak:
            client-id: oauth2-client-app                                   # 서비스 공급자에 등록된 클라이언트 아이디
            client-secret: tynI8eYUw4H1fJYxwLQ36XhFC1Ge1w1x                # 서비스 공급자에 등록된 클라이언트 비밀번호
            client-name: oauth2-client-app                                 # 클라이언트 이름
            authorization-grant-type: authorization_code                   # OAuth 2.0 권한 부여 타입
            redirect-uri: http://localhost:8081/login/oauth2/code/keycloak # 인가 서버가 권한 코드 부여 후 리다이렉트하는 위치
            client-authentication-method: client_secret_post               # 클라이언트 자격증명 전송 방식
            scope: openid,email                                            # 리소스에 접근 제한 범위
        provider:                                                          # 공급자 설정
          keycloak:
            issuer-uri: http://localhost:8080/realms/oauth2                                     # 서비스 공급자 위치
            authorization-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/auth # OAuth 2.0 권한 코드 부여 엔드포인트
            token-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/token        # OAuth 2.0 토큰 엔드포인트
            jwk-set-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/certs      # OAuth 2.0 JwkSetUri 엔드포인트
            user-info-uri: http://localhost:8080/realms/oauth2/protocol/openid-connect/userinfo # OAuth 2.0 UserInfo 엔드포인트
            user-name-attribute: preferred_username                                             # 사용자명을 추출하는 클레임명
  • 클라이언트가 인가 서버로 권한 부여 요청을 하거나 토큰 요청을 할 경우 클라이언트 정보 및 엔드포인트 정보를 참조해서 전달한다.
  • application.yml 환경설정 파일에 클라이언트 설정과 인가서버 엔드포인트 설정을 한다.
  • 초기화가 진행되면 application.yml에 있는 클라이언트 및 엔드포인트 정보가 OAuth2ClientProperties의 각 속성에 바인딩이 된다.
  • OAuth2ClientProperties에 바인딩 되어 있는 속성의 값은 인가서버로 권한 부여 요청을 하기 위한 ClientRegistration 클래스의 필드에 저장된다.
  • OAuth2AuthorizationRequestRedirectFilterClientRegistrationRepository에서 ClientRegistration을 조회해 OAuth2AuthorizationRequest를 구성하고, 사용자 브라우저를 인가서버로 리다이렉트시킨다.
  • 이후 토큰 요청은 RestClientAuthorizationCodeTokenResponseClient가 인가서버로 직접 POST한다.
  • Registration : 인가 서버에 등록된 클라이언트 및 요청 파라미터 정보를 나타낸다.
  • Provider : 공급자에서 제공하는 엔드포인트 등의 정보를 나타낸다.
  • OAuth2ClientProperties에서 클라이언트 및 공급자 정보를 맵에 저장하고 인가서버와의 통신 시 각 항목을 참조하여 사용하는 구조가 된다.

ClientRegistration

  • OAuth 2.0 또는 OpenID Connect 1.0 Provider에서 클라이언트 등록 정보를 나타낸다.
  • ClientRegistration은 OpenID Connect Provider의 설정 엔드포인트나 인가 서버의 메타 데이터 엔드포인트를 찾아 초기화할 수 있다.
  • ClientRegistration의 메서드를 사용하면 ClientRegistration을 쉽게 설정할 수 있다.
  • OAuth 2.0 공급자를 제공하는 클래스로서 글로벌 서비스 제공자 일부는 기본으로 제공되어진다.
  • client_idclient_secret는 별도로 작성을 해야 한다. 국내 공급자 정보는 위의 모든 항목들을 수동으로 작성해야 한다.
  • 클라이언트 기준인 Registration 항목과 서비스 제공자 기준인 Provider 항목으로 구분해 설정한다.
  • yml이나 properties 항목이 아닌 Java Config 방식으로 ClientRegistration을 작성할 수도 있다.
  • ClientRegistration 객체를 생성할 수 있는 빌더 클래스를 반환한다.
  • 내부 정적 클래스 중 하나인 ClientRegistrationRepositoryConfiguration에 선언된 어노테이션을 보게 되면 @ConditionalOnMissingBean(ClientRegistrationRepository.class)을 볼 수 있는데 이 어노테이션이 의미하는 바는 ClientRegistrationRepository 빈이 존재하지 않을 경우 실행된다는 것을 의미한다.
  • getBuilderFromIssuerIfPossible 메서드에서는 provider 설정에 issuer-uri가 있는지만 확인하고, 있으면 ClientRegistrations.fromIssuerLocation으로 디스커버리를 시도하며 없으면 null을 반환한다.
  • fromIssuerLocation 이 단계에서 OIDC 문서를 읽을지 아니면 RFC 8414 문서를 읽을지 결정한다.

ClientRegistrationRepository

  • ClientRegistrationRepository는 OAuth 2.0과 OpenID Connect 1.0의 ClientRegistration 저장소 역할을 한다.
  • 클라이언트 등록 정보는 궁극적으로 인가 서버가 저장하고 관리하는데 이 Repository는 인가 서버에 일차적으로 저장된 클라이언트 등록 정보의 일부를 검색하는 기능을 제공한다.
  • ClientRegistrationRepository의 구현체는 위와 같이 InMemoryClientRegistrationRepository이다.
  • 자동 설정을 사용하면 ClientRegistrationRepositoryApplicationContext 내의 @Bean으로 등록하므로 필요하다면 원하는 곳에 디펜던시를 주입할 수 있다.

자동설정에 의한 초기화 플로우

  • OAuth2ClientWebSecurityAutoConfiguration은 두 가지를 등록한다. oauth2SecurityFilterChain(HttpSecurity)@ConditionalOnDefaultWebSecurity가 붙어 있어 사용자가 SecurityFilterChain 빈을 직접 정의하지 않았을 때만 들어오는 기본 필터체인이고, authorizedClientRepository(OAuth2AuthorizedClientService)는 발급받은 토큰을 담아둘 저장소다.
  • authorizedClientRepository()가 실제로 반환하는 구현체다. 이름이 동작을 그대로 말해준다. 인증된 principal이냐를 기준으로 갈린다.
  • saveAuthorizedClient가 호출되면 isPrincipalAuthenticated(Authentication)로 판정해서, 인증된 사용자면 authorizedClientService로 넘기고 익명 사용자면 anonymousAuthorizedClientRepository(기본값 HttpSessionOAuth2AuthorizedClientRepository)로 보내 세션에 담는다.