OAuth2RestTemplate용 Spring Security 5 교체
»spring-security-oauth2:2.4.0.RELEASE
「」의 OAuth2RestTemplate
,OAuth2ProtectedResourceDetails
★★★★★★★★★★★★★★★★★」ClientCredentialsAccessTokenProvider
모두 권장되지 않는 것으로 표시되었습니다.
이러한 클래스의 javadoc에서 봄철 보안 이행 가이드를 소개합니다.이 가이드에서는 스프링 보안의 핵심인 5프로젝트로 이행할 필요가 있음을 시사합니다.그러나 이 프로젝트에서 사용 사례를 어떻게 구현해야 할지 고민하고 있습니다.
애플리케이션으로의 착신 요구를 인증하고, 서드파티제의 OAuth 프로바이더를 사용해 ID를 검증하는 경우, 모든 문서와 예에서는, 제3 파트 OAuth 프로바이더와의 통합에 대해 설명하고 있습니다.
에서는, 「」을 「요청」으로 것입니다.RestTemplate
OAuth의 경우현재 작성 중입니다.OAuth2ProtectedResourceDetails
로 넘깁니다.OAuth2RestTemplate
요.ClientCredentialsAccessTokenProvider
OAuth2ResTemplate
사용 중인 OAuth 프로바이더에 필요한 토큰 요청에 헤더를 추가할 뿐입니다.
spring-security 5 문서에서는 토큰 요구의 커스터마이즈에 대해 언급하고 있습니다만, 서드파티제의 OAuth 프로바이더에 의한 착신 요구의 인증에 관한 섹션을 발견했습니다.이것을 어떻게 조합해 사용하는지는 불명확합니다.ClientHttpRequestInterceptor
외부 서비스에 대한 각 발신 요구가 처음에 토큰을 얻은 후 요청에 추가되도록 합니다.
위의 에는 「」에 되어 있습니다.OAuth2AuthorizedClientService
, 도 요격에 것 .ClientRegistrationRepository
서드파티 프로바이더의 등록을 보관 유지하고 있는 것 같습니다.이것에 의해, 착신 요구가 확실히 인증되고 있는 것을 확인할 수 있습니다.
Spring-security 5의 신기능을 이용하여 OAuth 프로바이더를 등록하고 어플리케이션에서 발신요구에 추가할 토큰을 얻을 수 있는 방법이 있습니까?
5의2.0 은 Spring Security 5.2.x 의 OAuth 2.0 을 지원하지 .RestTemplate
단, ,만,WebClient
스프링 보안 레퍼런스:
HTTP 클라이언트 지원
WebClient
서블릿 (서브릿)
외에 '있다'도 있어요.RestTemplate
는 이후 버전에서 더 이상 사용되지 않습니다.RestTemplate javadoc 참조:
메모: 5.0 이후로는 논블로킹, 리액티브
org.springframework.web.reactive.client.WebClient
대체품을RestTemplate
동기 및 비동기 및 스트리밍 시나리오를 효율적으로 지원합니다.RestTemplate
는 향후 버전에서 폐지될 예정이며 향후 주요 신기능은 추가되지 않을 예정입니다.「 」를 .WebClient
스프링 프레임워크
따라서, 최선의 해결방법은 포기해야 합니다.RestTemplate
에 WebClient
.
「」를 사용합니다.WebClient
Credentials Flow의 경우 credential의 경우
클라이언트 등록 및 프로바이더를 프로그래밍 방식으로 설정하거나 Spring Boot 자동 설정을 사용하여 설정합니다.
spring:
security:
oauth2:
client:
registration:
custom:
client-id: clientId
client-secret: clientSecret
authorization-grant-type: client_credentials
provider:
custom:
token-uri: http://localhost:8081/oauth/token
의OAuth2AuthorizedClientManager
@Bean
:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
설정합니다.WebClient
하여 ""ServerOAuth2AuthorizedClientExchangeFilterFunction
의 「」를 해 주세요.OAuth2AuthorizedClientManager
:
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("custom");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
.WebClient
내에 을 포함합니다.
안녕하세요. RestTemplate는 Spring Security 5에서 아직 지원되고 있습니다.RestTemplate는 비반응형 어플리케이션입니다.이행가이드에 기재된 바와 같이 스프링 보안을 적절하게 구성하고 대행 수신기를 작성하기만 하면 됩니다.
client_credentials 플로우를 사용하려면 다음 구성을 사용합니다.
application.yml
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: ${okta.oauth2.issuer}/v1/keys
client:
registration:
okta:
client-id: ${okta.oauth2.clientId}
client-secret: ${okta.oauth2.clientSecret}
scope: "custom-scope"
authorization-grant-type: client_credentials
provider: okta
provider:
okta:
authorization-uri: ${okta.oauth2.issuer}/v1/authorize
token-uri: ${okta.oauth2.issuer}/v1/token
OauthResTemplate 설정
@Configuration
@RequiredArgsConstructor
public class OAuthRestTemplateConfig {
public static final String OAUTH_WEBCLIENT = "OAUTH_WEBCLIENT";
private final RestTemplateBuilder restTemplateBuilder;
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
private final ClientRegistrationRepository clientRegistrationRepository;
@Bean(OAUTH_WEBCLIENT)
RestTemplate oAuthRestTemplate() {
var clientRegistration = clientRegistrationRepository.findByRegistrationId(Constants.OKTA_AUTH_SERVER_ID);
return restTemplateBuilder
.additionalInterceptors(new OAuthClientCredentialsRestTemplateInterceptorConfig(authorizedClientManager(), clientRegistration))
.setReadTimeout(Duration.ofSeconds(5))
.setConnectTimeout(Duration.ofSeconds(1))
.build();
}
@Bean
OAuth2AuthorizedClientManager authorizedClientManager() {
var authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
var authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, oAuth2AuthorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
}
가로채기
public class OAuthClientCredentialsRestTemplateInterceptor implements ClientHttpRequestInterceptor {
private final OAuth2AuthorizedClientManager manager;
private final Authentication principal;
private final ClientRegistration clientRegistration;
public OAuthClientCredentialsRestTemplateInterceptor(OAuth2AuthorizedClientManager manager, ClientRegistration clientRegistration) {
this.manager = manager;
this.clientRegistration = clientRegistration;
this.principal = createPrincipal();
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId(clientRegistration.getRegistrationId())
.principal(principal)
.build();
OAuth2AuthorizedClient client = manager.authorize(oAuth2AuthorizeRequest);
if (isNull(client)) {
throw new IllegalStateException("client credentials flow on " + clientRegistration.getRegistrationId() + " failed, client is null");
}
request.getHeaders().add(HttpHeaders.AUTHORIZATION, BEARER_PREFIX + client.getAccessToken().getTokenValue());
return execution.execute(request, body);
}
private Authentication createPrincipal() {
return new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptySet();
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getDetails() {
return null;
}
@Override
public Object getPrincipal() {
return this;
}
@Override
public boolean isAuthenticated() {
return false;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
return clientRegistration.getClientId();
}
};
}
}
그러면 첫 번째 콜 및 토큰 기한이 만료될 때마다 access_token이 생성됩니다.OAuth2 Authorized Client Manager가 이 모든 것을 관리합니다.
나는 @matt Williams의 대답이 꽤 도움이 된다는 것을 알았다.WebClient 설정을 위해 clientId와 secret을 프로그램적으로 전달하고 싶은 경우를 위해 추가하고 싶습니다.방법은 다음과 같습니다.
@Configuration
public class WebClientConfig {
public static final String TEST_REGISTRATION_ID = "test-client";
@Bean
public ReactiveClientRegistrationRepository clientRegistrationRepository() {
var clientRegistration = ClientRegistration.withRegistrationId(TEST_REGISTRATION_ID)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.clientId("<client_id>")
.clientSecret("<client_secret>")
.tokenUri("<token_uri>")
.build();
return new InMemoryReactiveClientRegistrationRepository(clientRegistration);
}
@Bean
public WebClient testWebClient(ReactiveClientRegistrationRepository clientRegistrationRepo) {
var oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepo, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId(TEST_REGISTRATION_ID);
return WebClient.builder()
.baseUrl("https://.test.com")
.filter(oauth)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
}
}
위의 답변은 @Anar Saltanov의 도움을 받아 이 지경에 이르렀지만 OAuth 토큰 요청에 헤더를 추가해야 했기 때문에 사용 사례의 문제를 어떻게 해결했는지에 대한 완전한 답변을 드리려고 생각했습니다.
공급자 세부 정보 구성
음음에 다음 합니다.application.properties
spring.security.oauth2.client.registration.uaa.client-id=${CLIENT_ID:}
spring.security.oauth2.client.registration.uaa.client-secret=${CLIENT_SECRET:}
spring.security.oauth2.client.registration.uaa.scope=${SCOPE:}
spring.security.oauth2.client.registration.uaa.authorization-grant-type=client_credentials
spring.security.oauth2.client.provider.uaa.token-uri=${UAA_URL:}
커스텀을 ReactiveOAuth2AccessTokenResponseClient
에, 「서버간 통신」을 .ServerOAuth2AuthorizedClientExchangeFilterFunction
은, 「」만을 .ReactiveOAuth2AuthorizedClientManager
non-certificate가 OAuth2AuthorizedClientManager
, 을 할 때ReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider()
하려면) (OAuth2는 OAuth2는 OAuth2는 OAuth2는 OAuth2는 OAuth2는 OAuth2는 OAuth2는 OAuth2는 프로바이더로 해야 .)ReactiveOAuth2AuthorizedClientProvider
instead instead instead instead instead instead the instead instead instead OAuth2AuthorizedClientProvider
non-reactive를 사용하는 경우 스프링 보안 참조 문서에 따라DefaultClientCredentialsTokenResponseClient
..setRequestEntityConverter()
요구를 . 단, 등가 OAuth2 입니다.WebClientReactiveClientCredentialsTokenResponseClient
은 이하지 않기 기능을 을 활용할 수 ).WebClientReactiveClientCredentialsTokenResponseClient
★★★★★★★★★★★★★★★★★★」
구현이 호출되었습니다.UaaWebClientReactiveClientCredentialsTokenResponseClient
때문에 합니다).headers()
★★★★★★★★★★★★★★★★★」body()
「」로부터의 WebClientReactiveClientCredentialsTokenResponseClient
헤더/바디필드를 추가할 경우 기본 인증 흐름은 변경되지 않습니다).
WebClient
ServerOAuth2AuthorizedClientExchangeFilterFunction.setClientCredentialsTokenResponseClient()
메서드가 폐지되었으므로 해당 메서드의 권장 해제 조언을 따르십시오.
권장되지 않습니다.대신 사용하세요. (또는 커스텀)으로 설정된 인스턴스를 만듭니다.인스턴스를 에 제공합니다.
그 결과, 다음과 같은 설정이 됩니다.
@Bean("oAuth2WebClient")
public WebClient oauthFilteredWebClient(final ReactiveClientRegistrationRepository
clientRegistrationRepository)
{
final ClientCredentialsReactiveOAuth2AuthorizedClientProvider
clientCredentialsReactiveOAuth2AuthorizedClientProvider =
new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();
clientCredentialsReactiveOAuth2AuthorizedClientProvider.setAccessTokenResponseClient(
new UaaWebClientReactiveClientCredentialsTokenResponseClient());
final DefaultReactiveOAuth2AuthorizedClientManager defaultReactiveOAuth2AuthorizedClientManager =
new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository,
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
defaultReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider(
clientCredentialsReactiveOAuth2AuthorizedClientProvider);
final ServerOAuth2AuthorizedClientExchangeFilterFunction oAuthFilter =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(defaultReactiveOAuth2AuthorizedClientManager);
oAuthFilter.setDefaultClientRegistrationId("uaa");
return WebClient.builder()
.filter(oAuthFilter)
.build();
}
WebClient
때처럼
oAuth2WebClient
위해 수 되었습니다.은, 「bean」의 「bean」의 「」의 「Bean」의 「Bean」의 「Bean」의 「Bean」의 「Bean」의 방법에 의한 것입니다.WebClient
.
은 간단한 입니다.OAuth2RestTemplate
Boot을 3.0.0-M4
없다application.yml
설정이 필요합니다.
SecurityConfig.java
@Bean
public ReactiveClientRegistrationRepository getRegistration() {
ClientRegistration registration = ClientRegistration
.withRegistrationId("custom")
.tokenUri("<token_URI>")
.clientId("<client_id>")
.clientSecret("<secret>")
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.build();
return new InMemoryReactiveClientRegistrationRepository(registration);
}
@Bean
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
InMemoryReactiveOAuth2AuthorizedClientService clientService = new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrations);
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrations, clientService);
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth.setDefaultClientRegistrationId("custom");
return WebClient.builder()
.filter(oauth)
.filter(errorHandler()) // This is an optional
.build();
}
public static ExchangeFilterFunction errorHandler() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
if (clientResponse.statusCode().is5xxServerError() || clientResponse.statusCode().is4xxClientError()) {
return clientResponse.bodyToMono(String.class)
.flatMap(errorBody -> Mono.error(new IllegalAccessException(errorBody)));
} else {
return Mono.just(clientResponse);
}
});
}
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0-M4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<dependencies>
언급URL : https://stackoverflow.com/questions/58982286/spring-security-5-replacement-for-oauth2resttemplate
'programing' 카테고리의 다른 글
React JS: on Click 청취자가 함수여야 하지만 형식 문자열이 필요합니다. (0) | 2023.04.02 |
---|---|
jQuery: 앵커 href를 온클릭하여 비동기적으로 송신합니다. (0) | 2023.04.02 |
중첩된 Grails 도메인 개체에 대한 JSON 바인딩 (0) | 2023.04.02 |
PHP - 기존 함수 재정의 (0) | 2023.04.02 |
Jackson은 ISO8601 형식의 날짜 시간을 Java8 Instant로 역직렬화 (0) | 2023.04.02 |