-
Notifications
You must be signed in to change notification settings - Fork 14
Description
공식문서 학습
Getting Started
https://docs.spring.io/spring-authorization-server/docs/current/reference/html/getting-started.html
Installing Spring Authorization Server
implementation "org.springframework.boot:spring-boot-starter-oauth2-authorization-server"Developing Your First Application
시작하려면 @bean으로 정의된 최소 필수 구성 요소가 필요합니다. spring-boot-starter-oauth2-authorization-server 종속성을 사용할 때 다음 속성을 정의하고 Spring Boot가 필요한 @bean 정의를 제공하도록 하세요:
server:
port: 9000
logging:
level:
org.springframework.security: trace
spring:
security:
oauth2:
authorizationserver:
client:
oidc-client:
registration:
client-id: "oidc-client"
client-secret: "{noop}secret"
client-authentication-methods:
- "client_secret_basic"
authorization-grant-types:
- "authorization_code"
- "refresh_token"
redirect-uris:
- "http://127.0.0.1:8080/login/oauth2/code/oidc-client"
post-logout-redirect-uris:
- "http://127.0.0.1:8080/"
scopes:
- "openid"
- "profile"
require-authorization-consent: trueDefining Required Components
만약 default configuration을 커스텀하려면 최소한의 빈을 설정해야 한다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// A Spring Security filter chain for the Protocol Endpoints.
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
http
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling((exceptions) -> exceptions
.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
)
// Accept access tokens for User Info and/or Client Registration
.oauth2ResourceServer((resourceServer) -> resourceServer
.jwt(Customizer.withDefaults()));
return http.build();
}
// A Spring Security filter chain for authentication.
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());
return http.build();
}
// An instance of UserDetailsService for retrieving users to authenticate.
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
// An instance of RegisteredClientRepository for managing clients.
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("oidc-client")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/oidc-client")
.postLogoutRedirectUri("http://127.0.0.1:8080/")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
return new InMemoryRegisteredClientRepository(oidcClient);
}
// An instance of com.nimbusds.jose.jwk.source.JWKSource for signing access tokens.
// JWK는 암호화 키를 저장하는 방식으로 인가서버에서 발행하는 JWT 토큰의 암호화 및 서명에 필요한 암호화 키의 다양한 정보를 담은 JSON 객체 표준이다.
// JWK 형태의 정보를 통해서 JWT 를 검증할 수 있다.
// 리소스 서버에서는 디코더가 없으면 여기로 요청해서 이걸 가지고 디코더를 만든다.
@Bean
public JWKSource<SecurityContext> jwkSource() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
// An instance of java.security.KeyPair with keys generated on startup used to create the JWKSource above.
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
// An instance of JwtDecoder for decoding signed access tokens.
// 앞서 만든 jwkSource로 jwtDecoder를 만든다.
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
// An instance of AuthorizationServerSettings to configure Spring Authorization Server.
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
}Configuration Model
Default configuration
OAuth2AuthorizationServerConfiguration은 OAuth2 인증 서버에 대한 최소 기본 구성을 제공하는 @configuration입니다.
OAuth2AuthorizationServerConfiguration은 OAuth2AuthorizationServerConfigurer를 사용하여 기본 구성을 적용하고 OAuth2 인증 서버를 지원하는 모든 인프라 구성 요소로 구성된 SecurityFilterChain @bean을 등록합니다.
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(HttpSecurity)는 HttpSecurity에 기본 OAuth2 보안 구성을 적용하는 편의성(정적) 유틸리티 메서드입니다.
OAuth2 인증 서버의 SecurityFilterChain @bean은 다음과 같은 기본 프로토콜 엔드포인트로 구성됩니다:
- OAuth2 Authorization endpoint
- OAuth2 Device Authorization Endpoint
- OAuth2 Device Verification Endpoint
- OAuth2 Token endpoint
- OAuth2 Token Introspection endpoint
- OAuth2 Token Revocation endpoint
- OAuth2 Authorization Server Metadata endpoint
- JWK Set endpoint
jwt set endpoint는 JwKSource가 빈으로 등록되어 있어야만 등록됩니다.
다음 예제는 OAuth2AuthorizationServerConfiguration을 사용하여 최소한의 기본 구성을 적용하는 방법을 보여줍니다:
@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {
@Bean
public RegisteredClientRepository registeredClientRepository() {
List<RegisteredClient> registrations = ...
return new InMemoryRegisteredClientRepository(registrations);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = ...
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
}authorization_code 그랜트는 리소스 소유자의 인증이 필요합니다. 따라서 기본 OAuth2 보안 구성에 추가로 사용자 인증 메커니즘을 구성해야 합니다.
기본 구성에서는 OpenID Connect 1.0이 비활성화되어 있습니다. 다음 예제는 OidcConfigurer를 초기화하여 OpenID Connect 1.0을 활성화하는 방법을 보여줍니다:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Initialize `OidcConfigurer`
return http.build();
}기본 프로토콜 엔드포인트 외에도, OAuth2 인증 서버의 SecurityFilterChain @bean은 다음과 같은 OpenID Connect 1.0 프로토콜 엔드포인트로 구성됩니다:
- OpenID Connect 1.0 Provider Configuration endpoint
- OpenID Connect 1.0 Logout endpoint
- OpenID Connect 1.0 UserInfo endpoint
기본적으로 OpenID Connect 1.0 클라이언트 등록 엔드포인트는 비활성화되어 있습니다. 이는 많은 배포에서 동적 클라이언트 등록이 필요하지 않기 때문입니다.
OAuth2AuthorizationServerConfiguration.jwtDecoder(JWKSource)는 JwtDecoder @bean을 등록하는 데 사용할 수 있는 편의성(정적) 유틸리티 메서드입니다. 이 메서드는 OpenID Connect 1.0 UserInfo 엔드포인트와 OpenID Connect 1.0 클라이언트 등록 엔드포인트에 필요합니다.
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}OAuth2AuthorizationServerConfiguration의 주요 목적은 OAuth2 인증 서버에 대한 최소 기본 구성을 적용하는 편리한 방법을 제공하는 것입니다. 그러나 대부분의 경우 구성을 커스텀해야 합니다.
Customizing the configuration
OAuth2AuthorizationServerConfigurer는 OAuth2 인증 서버의 보안 구성을 완전히 사용자 정의할 수 있는 기능을 제공합니다. 이를 통해 사용할 핵심 구성 요소를 지정할 수 있습니다. 예를 들어, RegisteredClientRepository, OAuth2AuthorizationService, OAuth2TokenGenerator 등입니다. 또한, 프로토콜 엔드포인트의 요청 처리 로직을 사용자 정의할 수 있습니다. 예를 들어, 인가 엔드포인트, 디바이스 인가 엔드포인트, 디바이스 검증 엔드포인트, 토큰 엔드포인트, 토큰 검사 엔드포인트 등입니다.
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
// 클라이언트 관리 리포지토리
.registeredClientRepository(registeredClientRepository) (1)
// 인가 관리 서비스
.authorizationService(authorizationService) (2)
// 동의 관리 서비스
.authorizationConsentService(authorizationConsentService) (3)
// OAuth2 인증 서버의 구성 설정을 사용자 정의
.authorizationServerSettings(authorizationServerSettings) (4)
// 토큰 생성기
.tokenGenerator(tokenGenerator) (5)
// Oauth2 client authentication
.clientAuthentication(clientAuthentication -> { }) (6)
// authorization endpoint 설정
.authorizationEndpoint(authorizationEndpoint -> { }) (7)
// device 인증 endpoint
.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> { }) (8)
.deviceVerificationEndpoint(deviceVerificationEndpoint -> { }) (9)
// token endpoint 설정
.tokenEndpoint(tokenEndpoint -> { }) (10)
.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint -> { }) (11)
.tokenRevocationEndpoint(tokenRevocationEndpoint -> { }) (12)
.authorizationServerMetadataEndpoint(authorizationServerMetadataEndpoint -> { }) (13)
// oidc endpoint 설정
.oidc(oidc -> oidc
.providerConfigurationEndpoint(providerConfigurationEndpoint -> { }) (14)
.logoutEndpoint(logoutEndpoint -> { }) (15)
.userInfoEndpoint(userInfoEndpoint -> { }) (16)
.clientRegistrationEndpoint(clientRegistrationEndpoint -> { }) (17)
);
return http.build();
}Configuring Authorization Server Settings
AuthorizationServerSettings는 OAuth2 인증 서버의 구성 설정을 포함합니다. 이는 프로토콜 엔드포인트의 URI와 발급자 식별자를 지정합니다. 프로토콜 엔드포인트의 기본 URI는 다음과 같습니다:
public final class AuthorizationServerSettings extends AbstractSettings {
...
public static Builder builder() {
return new Builder()
.authorizationEndpoint("/oauth2/authorize")
.deviceAuthorizationEndpoint("/oauth2/device_authorization")
.deviceVerificationEndpoint("/oauth2/device_verification")
.tokenEndpoint("/oauth2/token")
.tokenIntrospectionEndpoint("/oauth2/introspect")
.tokenRevocationEndpoint("/oauth2/revoke")
.jwkSetEndpoint("/oauth2/jwks")
.oidcLogoutEndpoint("/connect/logout")
.oidcUserInfoEndpoint("/userinfo")
.oidcClientRegistrationEndpoint("/connect/register");
}
...
}AuthorizationServerSettings는 필수 구성 요소입니다.
@import(OAuth2AuthorizationServerConfiguration.class)는 이미 제공되지 않았다면 자동으로 AuthorizationServerSettings @bean을 등록합니다.
다음 예제는 구성 설정을 사용자 정의하고 AuthorizationServerSettings @bean을 등록하는 방법을 보여줍니다:
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer("https://example.com")
.authorizationEndpoint("/oauth2/v1/authorize")
.deviceAuthorizationEndpoint("/oauth2/v1/device_authorization")
.deviceVerificationEndpoint("/oauth2/v1/device_verification")
.tokenEndpoint("/oauth2/v1/token")
.tokenIntrospectionEndpoint("/oauth2/v1/introspect")
.tokenRevocationEndpoint("/oauth2/v1/revoke")
.jwkSetEndpoint("/oauth2/v1/jwks")
.oidcLogoutEndpoint("/connect/v1/logout")
.oidcUserInfoEndpoint("/connect/v1/userinfo")
.oidcClientRegistrationEndpoint("/connect/v1/register")
.build();
}AuthorizationServerContext는 Authorization Server 실행 환경의 정보를 보유하는 컨텍스트 객체입니다. 이는 AuthorizationServerSettings와 "current" issuer identifier에 대한 접근을 제공합니다.
만약 AuthorizationServerSettings.builder().issuer(String)에서 발급자 식별자가 구성되지 않았다면, 현재 요청에서 해결됩니다.
AuthorizationServerContext는 AuthorizationServerContextHolder를 통해 액세스할 수 있으며, ThreadLocal을 사용하여 현재 요청 스레드에 연결됩니다.
Configuring Client Authentication
OAuth2ClientAuthenticationConfigurer는 OAuth2 클라이언트 인증을 사용자 정의할 수 있는 기능을 제공합니다. 클라이언트 인증 요청에 대한 전처리, 주요 처리, 후처리 로직을 사용자 정의할 수 있는 확장 지점을 정의합니다.
OAuth2ClientAuthenticationConfigurer는 다음과 같은 구성 옵션을 제공합니다:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.clientAuthentication(clientAuthentication ->
clientAuthentication
// Adds an AuthenticationConverter (pre-processor) used when attempting to extract client credentials from HttpServletRequest to an instance of OAuth2ClientAuthenticationToken.
.authenticationConverter(authenticationConverter) (1)
// Sets the Consumer providing access to the List of default and (optionally) added AuthenticationConverter's allowing the ability to add, remove, or customize a specific AuthenticationConverter.
.authenticationConverters(authenticationConvertersConsumer) (2)
// OAuth2ClientAuthenticationToken의 인증에 사용되는 AuthenticationProvider(주요 처리기)를 추가합니다.
.authenticationProvider(authenticationProvider) (3)
.authenticationProviders(authenticationProvidersConsumer) (4)
// 성공적인 클라이언트 인증을 처리하고 OAuth2ClientAuthenticationToken을 SecurityContext에 연결하는 데 사용되는 AuthenticationSuccessHandler(후처리기)입니다.
.authenticationSuccessHandler(authenticationSuccessHandler) (5)
// 실패한 클라이언트 인증을 처리하고 OAuth2Error 응답을 반환하는 데 사용되는 AuthenticationFailureHandler(후처리기)입니다.
.errorResponseHandler(errorResponseHandler) (6)
);
return http.build();
}
OAuth2ClientAuthenticationConfigurer는 OAuth2ClientAuthenticationFilter를 구성하고 OAuth2 인증 서버 SecurityFilterChain @bean에 등록합니다. OAuth2ClientAuthenticationFilter는 클라이언트 인증 요청을 처리하는 필터입니다.
기본적으로, 클라이언트 인증은 OAuth2 토큰 엔드포인트, OAuth2 토큰 검사 엔드포인트, OAuth2 토큰 폐기 엔드포인트에서 필요합니다. 지원되는 클라이언트 인증 방법은 client_secret_basic, client_secret_post, private_key_jwt, client_secret_jwt, none (public clients)입니다.
OAuth2ClientAuthenticationFilter는 다음과 같은 기본값으로 구성됩니다:
- AuthenticationConverter - JwtClientAssertionAuthenticationConverter, ClientSecretBasicAuthenticationConverter, ClientSecretPostAuthenticationConverter 및 PublicClientAuthenticationConverter로 구성된 DelegatingAuthenticationConverter입니다.
- AuthenticationManager - JwtClientAssertionAuthenticationProvider, ClientSecretAuthenticationProvider 및 PublicClientAuthenticationProvider로 구성된 AuthenticationManager입니다.
- AuthenticationSuccessHandler - "인증된" OAuth2ClientAuthenticationToken(현재 인증)을 SecurityContext에 연결하는 내부 구현입니다.
- AuthenticationFailureHandler - OAuth2AuthenticationException과 연관된 OAuth2Error를 사용하여 OAuth2 오류 응답을 반환하는 내부 구현입니다.
Customizing Jwt Client Assertion Validation
JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY는 지정된 RegisteredClient에 대해 Jwt의 iss, sub, aud, exp 및 nbf 클레임을 유효성 검사하는 OAuth2TokenValidator를 제공하는 기본 팩토리입니다.
JwtClientAssertionDecoderFactory는 setJwtValidatorFactory()를 통해 Function<RegisteredClient, OAuth2TokenValidator> 타입의 사용자 정의 팩토리를 제공하여 기본 Jwt 클라이언트 어설션 유효성 검사를 재정의할 수 있는 기능을 제공합니다.
JwtClientAssertionDecoderFactory는 JwtClientAssertionAuthenticationProvider에서 사용하는 기본 JwtDecoderFactory입니다. 이는 지정된 RegisteredClient에 대해 JwtDecoder를 제공하며, OAuth2 클라이언트 인증 중 Jwt Bearer 토큰의 인증에 사용됩니다.
JwtClientAssertionDecoderFactory를 사용자 정의하는 일반적인 사용 사례 중 하나는 Jwt 클라이언트 어설션에서 추가적인 클레임을 유효성 검사하는 것입니다.
다음 예제는 Jwt 클라이언트 어설션에서 추가적인 클레임을 유효성 검사하는 사용자 정의 JwtClientAssertionDecoderFactory로 JwtClientAssertionAuthenticationProvider를 구성하는 방법을 보여줍니다:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.clientAuthentication(clientAuthentication ->
clientAuthentication
.authenticationProviders(configureJwtClientAssertionValidator())
);
return http.build();
}
private Consumer<List<AuthenticationProvider>> configureJwtClientAssertionValidator() {
return (authenticationProviders) ->
authenticationProviders.forEach((authenticationProvider) -> {
if (authenticationProvider instanceof JwtClientAssertionAuthenticationProvider) {
// Customize JwtClientAssertionDecoderFactory
JwtClientAssertionDecoderFactory jwtDecoderFactory = new JwtClientAssertionDecoderFactory();
Function<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = (registeredClient) ->
new DelegatingOAuth2TokenValidator<>(
// Use default validators
JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY.apply(registeredClient),
// Add custom validator
new JwtClaimValidator<>("claim", "value"::equals));
jwtDecoderFactory.setJwtValidatorFactory(jwtValidatorFactory);
((JwtClientAssertionAuthenticationProvider) authenticationProvider)
.setJwtDecoderFactory(jwtDecoderFactory);
}
});
}Core Model / Components
RegisteredClient
RegisteredClient은 인가 서버에 등록된 클라이언트를 나타내는 것입니다. 클라이언트는 authorization_code 또는 client_credentials와 같은 인가 그랜트 플로우를 시작하기 전에 인가 서버에 등록되어야 합니다.
클라이언트 등록 중에 클라이언트는 고유한 클라이언트 식별자, (선택적으로) 클라이언트 시크릿(클라이언트 유형에 따라 달라짐) 및 고유한 클라이언트 식별자와 관련된 메타데이터를 할당받습니다. 클라이언트의 메타데이터는 인간이 읽을 수 있는 표시 문자열(예: 클라이언트 이름)부터 프로토콜 플로우에 특정한 항목(예: 유효한 리디렉션 URI 목록)까지 다양할 수 있습니다.
Spring Security의 OAuth2 클라이언트 지원에서 대응되는 클라이언트 등록 모델은 ClientRegistration입니다.
클라이언트의 주요 목적은 보호된 리소스에 대한 액세스를 요청하는 것입니다. 클라이언트는 먼저 인가 서버에 인증하고 인가 그랜트를 제시하여 액세스 토큰을 요청합니다. 인가 서버는 클라이언트와 인가 그랜트를 인증하고 유효한 경우 액세스 토큰을 발급합니다. 이제 클라이언트는 액세스 토큰을 제시하여 리소스 서버로부터 보호된 리소스를 요청할 수 있습니다.
다음 예제는 authorization_code 그랜트 플로우를 수행할 수 있는 RegisteredClient를 구성하여 액세스 토큰을 요청하는 방법을 보여줍니다:
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("client-a")
.clientSecret("{noop}secret") (1)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://127.0.0.1:8080/authorized")
.scope("scope-a")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();Spring Security의 OAuth2 클라이언트 지원에서 해당하는 구성은 다음과 같습니다:
spring:
security:
oauth2:
client:
registration:
client-a:
provider: spring
client-id: client-a
client-secret: secret
authorization-grant-type: authorization_code
redirect-uri: "http://127.0.0.1:8080/authorized"
scope: scope-a
provider:
spring:
issuer-uri: http://localhost:9000RegisteredClient는 고유한 클라이언트 식별자와 관련된 메타데이터(속성)를 가지며, 다음과 같이 정의됩니다:
public class RegisteredClient implements Serializable {
// RegisteredClient 식별자
private String id; (1)
// client 식별자
private String clientId; (2)
// 식별자 발급시간
private Instant clientIdIssuedAt; (3)
// 시크릿값
private String clientSecret; (4)
// 시크릿 만료일
private Instant clientSecretExpiresAt; (5)
// 클라이언트에 대한 설명 -> consent 페이지 같은 곳에서 쓰인다.
private String clientName; (6)
// 인증 방법 client_secret_basic, client_secret_post, private_key_jwt, client_secret_jwt, none
private Set<ClientAuthenticationMethod> clientAuthenticationMethods; (7)
// 인가 그랜트 타입 authorization_code, client_credentials, refresh_token, and urn:ietf:params:oauth:grant-type:device_code.
private Set<AuthorizationGrantType> authorizationGrantTypes; (8)
// : 클라이언트가 리디렉션 기반 플로우(예: authorization_code 그랜트)에서 사용할 수 있는 등록된 리디렉션 URI
private Set<String> redirectUris; (9)
// 클라이언트가 로그아웃에 사용할 수 있는 로그아웃 후 리디렉션 URI
private Set<String> postLogoutRedirectUris; (10)
// 클라이언트가 요청할 수 있는 스코프입니다
private Set<String> scopes; (11)
// 클라이언트에 대한 사용자 정의 설정
private ClientSettings clientSettings; (12)
// 라이언트에게 발급된 OAuth2 토큰에 대한 사용자 정의 설정입니다. 예를 들어, 액세스/리프레시 토큰의 유효 기간, 리프레시 토큰 재사용 여부 등이 포함
private TokenSettings tokenSettings; (13)
...
}RegisteredClientRepository
RegisteredClientRepository는 새 클라이언트를 등록하고 기존 클라이언트를 조회할 수 있는 중앙 컴포넌트입니다. 클라이언트 인증, 인가 그랜트 처리, 토큰 검사, 동적 클라이언트 등록 등과 같은 특정 프로토콜 플로우를 따를 때 다른 컴포넌트에서 사용됩니다.
RegisteredClientRepository의 제공되는 구현체는 InMemoryRegisteredClientRepository와 JdbcRegisteredClientRepository입니다. InMemoryRegisteredClientRepository 구현체는 RegisteredClient 인스턴스를 메모리에 저장하며, 개발 및 테스트 중에만 사용하는 것이 권장됩니다. JdbcRegisteredClientRepository는 JdbcOperations를 사용하여 RegisteredClient 인스턴스를 영속화하는 JDBC 구현체입니다.
RegisteredClientRepository는 필수 구성 요소입니다.
다음 예제는 RegisteredClientRepository @bean을 등록하는 방법을 보여줍니다:
@Bean
public RegisteredClientRepository registeredClientRepository() {
List<RegisteredClient> registrations = ...
return new InMemoryRegisteredClientRepository(registrations);
}또는 OAuth2AuthorizationServerConfigurer를 통해 RegisteredClientRepository를 구성할 수도 있습니다:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.registeredClientRepository(registeredClientRepository);
...
return http.build();
}OAuth2AuthorizationServerConfigurer는 동시에 여러 구성 옵션을 적용할 때 유용합니다.
OAuth2Authorization
OAuth2Authorization은 OAuth2 인증에 대한 표현이며, 클라이언트에게 인가된 상태와 관련된 정보를 보유합니다. 인가 유형이 client_credentials인 경우에는 리소스 소유자 또는 자체에 의해 클라이언트에게 인가된 상태를 나타냅니다.
Spring Security의 OAuth2 클라이언트 지원에서 해당하는 인가 모델은 OAuth2AuthorizedClient입니다.
인가 그랜트 플로우가 성공적으로 완료된 후에 OAuth2Authorization이 생성되며, OAuth2AccessToken, (선택적) OAuth2RefreshToken 및 실행된 인가 그랜트 유형에 특정한 추가 상태와 연관시킵니다.
OAuth2Authorization과 연관된 OAuth2Token 인스턴스는 인가 그랜트 유형에 따라 다릅니다.
OAuth2 authorization_code 그랜트의 경우, OAuth2AuthorizationCode, OAuth2AccessToken 및 (선택적) OAuth2RefreshToken이 연관됩니다.
OpenID Connect 1.0 authorization_code 그랜트의 경우, OAuth2AuthorizationCode, OidcIdToken, OAuth2AccessToken 및 (선택적) OAuth2RefreshToken이 연관됩니다.
OAuth2 client_credentials 그랜트의 경우, OAuth2AccessToken만 연관됩니다.
OAuth2Authorization 및 해당 속성은 다음과 같이 정의됩니다
public class OAuth2Authorization implements Serializable {
// 식별자
private String id;
// RegisteredClient를 고유하게 식별하는 ID
private String registeredClientId;
// 리소스 소유자(또는 클라이언트)의 주체 이름입니다
private String principalName;
// 사용된 AuthorizationGrantType
private AuthorizationGrantType authorizationGrantType;
// 인가된 스코프 집합
private Set<String> authorizedScopes;
// 실행된 인가 그랜트 유형에 특정한 OAuth2Token 인스턴스(및 관련 메타데이터)입니다.
private Map<Class<? extends OAuth2Token>, Token<?>> tokens;
// 실행된 인가 그랜트 유형에 특정한 추가 속성입니다. 예를 들어, 인증된 Principal, OAuth2AuthorizationRequest 등이 포함될 수 있습니다
private Map<String, Object> attributes;
...
OAuth2Authorization과 관련된 OAuth2Token 인스턴스는 수명을 가지고 있습니다. 새로 발급된 OAuth2Token은 활성 상태이며, 만료되거나 무효화(폐기)되면 비활성 상태가 됩니다. OAuth2Authorization은 모든 관련 OAuth2Token 인스턴스가 비활성 상태인 경우 (암묵적으로) 비활성 상태입니다. 각 OAuth2Token은 OAuth2Authorization.Token에 보관되며, isExpired(), isInvalidated(), isActive()에 대한 접근자를 제공합니다.
OAuth2Authorization.Token은 또한 getClaims()를 제공하며, OAuth2Token과 연관된 클레임(있는 경우)을 반환합니다.
OAuth2AuthorizationService
OAuth2AuthorizationService는 새 인가를 저장하고 기존 인가를 조회하는 중앙 컴포넌트입니다. 특정 프로토콜 플로우를 따를 때 다른 컴포넌트에서 사용됩니다. 예를 들어 클라이언트 인증, 인가 그랜트 처리, 토큰 검사, 토큰 폐기, 동적 클라이언트 등록 등이 있습니다.
OAuth2AuthorizationService의 제공되는 구현체는 InMemoryOAuth2AuthorizationService와 JdbcOAuth2AuthorizationService입니다. InMemoryOAuth2AuthorizationService 구현체는 OAuth2Authorization 인스턴스를 메모리에 저장하며, 개발 및 테스트 중에만 사용하는 것이 권장됩니다. JdbcOAuth2AuthorizationService는 JdbcOperations를 사용하여 OAuth2Authorization 인스턴스를 영속화하는 JDBC 구현체입니다.
OAuth2AuthorizationService는 선택적인 구성 요소이며 기본적으로 InMemoryOAuth2AuthorizationService를 사용합니다.
다음 예제는 OAuth2AuthorizationService @bean을 등록하는 방법을 보여줍니다:
@Bean
public OAuth2AuthorizationService authorizationService() {
return new InMemoryOAuth2AuthorizationService();
}또는 OAuth2AuthorizationServerConfigurer를 통해 OAuth2AuthorizationService를 구성할 수도 있습니다:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.authorizationService(authorizationService);
...
return http.build();
}
OAuth2AuthorizationConsent
OAuth2AuthorizationConsent은 OAuth2 인가 요청 플로우(예: authorization_code 그랜트)에서의 인가 "동의"(결정)를 나타냅니다. 이는 리소스 소유자에 의해 클라이언트에게 부여된 권한을 포함합니다.
리소스 소유자는 클라이언트에 의해 요청된 권한의 일부만 부여할 수 있습니다. 일반적인 사용 사례는 authorization_code 그랜트 플로우입니다. 이 경우 클라이언트가 스코프를 요청하고 리소스 소유자가 요청된 스코프에 대한 액세스를 허용하거나 거부합니다.
OAuth2 인가 요청 플로우의 완료 후에는 OAuth2AuthorizationConsent가 생성되거나 업데이트되고 부여된 권한을 클라이언트와 리소스 소유자와 연결합니다.
OAuth2AuthorizationConsent 및 해당 속성은 다음과 같이 정의됩니다:
public final class OAuth2AuthorizationConsent implements Serializable {
// registeredClient ID
private final String registeredClientId; (1)
// 리소스 소유자 주체 이름
private final String principalName; (2)
// 리소스 소유자가 클라이언트에게 부여한 권한입니다. 권한은 스코프, 클레임, 퍼미션, 역할 등을 나타낼 수 있습니다.
private final Set<GrantedAuthority> authorities; (3)
...
}OAuth2AuthorizationConsentService
OAuth2AuthorizationConsentService는 새로운 인가 동의를 저장하고 기존의 인가 동의를 조회하는 중앙 컴포넌트입니다. 이는 주로 OAuth2 인가 요청 플로우 (예: authorization_code 그랜트)를 구현하는 컴포넌트에서 사용됩니다.
OAuth2AuthorizationConsentService의 제공되는 구현체는 InMemoryOAuth2AuthorizationConsentService와 JdbcOAuth2AuthorizationConsentService가 있습니다. InMemoryOAuth2AuthorizationConsentService 구현체는 OAuth2AuthorizationConsent 인스턴스를 메모리에 저장하며, 개발 및 테스트에만 권장됩니다. JdbcOAuth2AuthorizationConsentService는 JdbcOperations를 사용하여 OAuth2AuthorizationConsent 인스턴스를 영속화하는 JDBC 구현체입니다.
OAuth2AuthorizationConsentService는 선택적인 구성 요소이며 기본적으로 InMemoryOAuth2AuthorizationConsentService를 사용합니다.
다음 예제는 OAuth2AuthorizationConsentService @bean을 등록하는 방법을 보여줍니다:
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService() {
return new InMemoryOAuth2AuthorizationConsentService();
}또는 OAuth2AuthorizationServerConfigurer를 통해 OAuth2AuthorizationConsentService를 구성할 수도 있습니다:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.authorizationConsentService(authorizationConsentService);
...
return http.build();
}OAuth2TokenContext
OAuth2TokenContext는 OAuth2Token과 관련된 정보를 보유하는 컨텍스트 객체로서, OAuth2TokenGenerator 및 OAuth2TokenCustomizer에서 사용됩니다.
OAuth2TokenContext는 다음과 같은 접근자를 제공합니다:
public interface OAuth2TokenContext extends Context {
default RegisteredClient getRegisteredClient() ... (1)
// 리소스 소유자(또는 클라이언트)의 Authencitation
default <T extends Authentication> T getPrincipal() ... (2)
// Authorization Server의 실행 환경 정보를 보유하는 AuthorizationServerContext 객체
default AuthorizationServerContext getAuthorizationServerContext() ... (3)
// 인가 그랜트와 관련된 OAuth2Authorization
@Nullable
default OAuth2Authorization getAuthorization() ... (4)
// 클라이언트에게 인가된 스코프
default Set<String> getAuthorizedScopes() ... (5)
// OAuth2TokenType을 가져옵니다. 지원되는 값은 code, access_token, refresh_token, id_toke
default OAuth2TokenType getTokenType() ... (6)
// 인가 그랜트와 관련된 AuthorizationGrantType
default AuthorizationGrantType getAuthorizationGrantType() ... (7)
// 인가 그랜트를 처리하는 AuthenticationProvider에서 사용하는 Authentication 인스턴스
default <T extends Authentication> T getAuthorizationGrant() ... (8)
...
}OAuth2TokenGenerator
OAuth2TokenGenerator는 제공된 OAuth2TokenContext에 포함된 정보를 기반으로 OAuth2Token을 생성하는 역할을 담당합니다.
생성되는 OAuth2Token은 주로 OAuth2TokenContext에서 지정된 OAuth2TokenType의 유형에 따라 달라집니다.
예를 들어, OAuth2TokenType 값이:
- code인 경우 OAuth2AuthorizationCode가 생성됩니다.
- access_token인 경우 OAuth2AccessToken이 생성됩니다.
- refresh_token인 경우 OAuth2RefreshToken이 생성됩니다.
- id_token인 경우 OidcIdToken이 생성됩니다.
또한, 생성된 OAuth2AccessToken의 형식은 RegisteredClient에 대해 구성된 TokenSettings.getAccessTokenFormat()에 따라 달라집니다. 형식이 OAuth2TokenFormat.SELF_CONTAINED(기본값)인 경우 Jwt가 생성되고, 형식이 OAuth2TokenFormat.REFERENCE인 경우 "opaque" 토큰이 생성됩니다.
생성된 OAuth2Token이 클레임 집합을 가지고 있고 ClaimAccessor를 구현한 경우, 해당 클레임은 OAuth2Authorization.Token.getClaims()를 통해 액세스할 수 있습니다.
OAuth2TokenGenerator는 주로 authorization_code, client_credentials, refresh_token 등의 인가 그랜트 처리를 구현하는 컴포넌트에서 사용됩니다.
제공되는 구현체는 OAuth2AccessTokenGenerator, OAuth2RefreshTokenGenerator, JwtGenerator가 있습니다. OAuth2AccessTokenGenerator는 "opaque" (OAuth2TokenFormat.REFERENCE) 액세스 토큰을 생성하고, JwtGenerator는 Jwt (OAuth2TokenFormat.SELF_CONTAINED)를 생성합니다.
OAuth2TokenGenerator는 선택적인 구성 요소이며 디폴트는 OAuth2AccessTokenGenerator와 OAuth2RefreshTokenGenerator로 구성된 DelegatingOAuth2TokenGenerator입니다.
JwtEncoder @bean 또는 JWKSource @bean이 등록된 경우, DelegatingOAuth2TokenGenerator에는 추가로 JwtGenerator가 구성됩니다.
Auth2TokenGenerator는 access_token 및 refresh_token에 대한 사용자 정의 토큰 형식을 지원하는 큰 유연성을 제공합니다.
다음 예제는 OAuth2TokenGenerator @bean을 등록하는 방법을 보여줍니다:
@Bean
public OAuth2TokenGenerator<?> tokenGenerator() {
JwtEncoder jwtEncoder = ...
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
return new DelegatingOAuth2TokenGenerator(
jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}또는 OAuth2AuthorizationServerConfigurer를 통해 OAuth2TokenGenerator를 구성할 수도 있습니다:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.tokenGenerator(tokenGenerator);
...
return http.build();OAuth2TokenCustomizer
OAuth2TokenCustomizer는 OAuth2Token의 속성을 사용자 정의하는 기능을 제공합니다. 이 속성은 제공된 OAuth2TokenContext에서 액세스할 수 있습니다. OAuth2TokenGenerator에서 OAuth2Token이 생성되기 전에 OAuth2Token의 속성을 사용자 정의할 수 있도록 합니다.
제네릭 타입이 OAuth2TokenClaimsContext(OAuth2TokenContext를 구현함)인 OAuth2TokenCustomizer를 선언하면 "opaque" OAuth2AccessToken의 클레임을 사용자 정의할 수 있는 기능을 제공합니다. OAuth2TokenClaimsContext.getClaims()를 통해 OAuth2TokenClaimsSet.Builder에 액세스하여 클레임을 추가, 대체, 제거할 수 있습니다.
다음 예제는 OAuth2TokenCustomizer를 구현하고 OAuth2AccessTokenGenerator와 함께 구성하는 방법을 보여줍니다:
@Bean
public OAuth2TokenGenerator<?> tokenGenerator() {
JwtEncoder jwtEncoder = ...
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
accessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer());
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
return new DelegatingOAuth2TokenGenerator(
jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}
@Bean
public OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer() {
return context -> {
OAuth2TokenClaimsSet.Builder claims = context.getClaims();
// Customize claims
};
}OAuth2TokenGenerator가 @bean으로 제공되지 않거나 OAuth2AuthorizationServerConfigurer를 통해 구성되지 않은 경우, OAuth2TokenCustomizer @bean은 자동으로 OAuth2AccessTokenGenerator와 함께 구성됩니다.
OAuth2TokenCustomizer는 Jwt의 헤더 및 클레임을 사용자 정의하는 기능을 제공합니다. 제네릭 타입이 JwtEncodingContext(OAuth2TokenContext를 구현함)인 OAuth2TokenCustomizer는 JwsHeader.Builder에 액세스하여 헤더를 추가, 대체, 제거할 수 있는 기능을 제공합니다. 또한 JwtClaimsSet.Builder에 액세스하여 클레임을 추가, 대체, 제거할 수 있는 기능을 제공합니다.
다음 예제는 OAuth2TokenCustomizer를 구현하고 JwtGenerator와 함께 구성하는 방법을 보여줍니다:
@Bean
public OAuth2TokenGenerator<?> tokenGenerator() {
JwtEncoder jwtEncoder = ...
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
jwtGenerator.setJwtCustomizer(jwtCustomizer());
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
return new DelegatingOAuth2TokenGenerator(
jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return context -> {
JwsHeader.Builder headers = context.getHeaders();
JwtClaimsSet.Builder claims = context.getClaims();
if (context.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) {
// Customize headers/claims for access_token
} else if (context.getTokenType().getValue().equals(OidcParameterNames.ID_TOKEN)) {
// Customize headers/claims for id_token
}
};
}OAuth2TokenGenerator가 @bean으로 제공되지 않거나 OAuth2AuthorizationServerConfigurer를 통해 구성되지 않은 경우, OAuth2TokenCustomizer @bean은 자동으로 JwtGenerator와 함께 구성됩니다.
SessionRegistry
만약 OpenID Connect 1.0이 활성화되어 있다면, 인증된 세션을 추적하기 위해 SessionRegistry 인스턴스가 사용됩니다. SessionRegistry는 OAuth2 인증 엔드포인트에 연결된 기본 SessionAuthenticationStrategy의 일부로 사용되어 새로운 인증된 세션을 등록합니다.
만약 SessionRegistry @bean이 등록되어 있지 않다면, 기본 구현체인 SessionRegistryImpl이 사용됩니다.
만약 SessionRegistry @bean이 등록되어 있고 SessionRegistryImpl의 인스턴스인 경우, HttpSessionEventPublisher @bean도 등록되어야 합니다. HttpSessionEventPublisher는 세션 생명주기 이벤트 (예: SessionDestroyedEvent)를 SessionRegistryImpl에 알릴 책임이 있으며, SessionInformation 인스턴스를 제거하는 기능을 제공합니다.
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}