Last active
June 26, 2020 01:51
-
-
Save loesak/ec97dfb891381932187e655153e741ae to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.loesak.feign.oauth2; | |
import feign.RequestInterceptor; | |
import feign.RequestTemplate; | |
import lombok.RequiredArgsConstructor; | |
import lombok.extern.slf4j.Slf4j; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.security.core.Authentication; | |
import org.springframework.security.core.GrantedAuthority; | |
import org.springframework.security.core.context.SecurityContextHolder; | |
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; | |
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; | |
import org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient; | |
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; | |
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest; | |
import org.springframework.security.oauth2.client.registration.ClientRegistration; | |
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; | |
import org.springframework.security.oauth2.core.AuthorizationGrantType; | |
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; | |
import java.time.Clock; | |
import java.time.Duration; | |
import java.time.Instant; | |
import java.util.Collection; | |
@Slf4j | |
@RequiredArgsConstructor | |
public class OAuth2FeignRequestInterceptor implements RequestInterceptor { | |
private final ClientRegistrationRepository clientRegistrationRepository; | |
private final OAuth2AuthorizedClientService authorizedClientService; | |
private final String clientRegistrationId; | |
private final Clock clock = Clock.systemUTC(); | |
private final Duration accessTokenExpiresSkew = Duration.ofMinutes(1); | |
private OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = new DefaultClientCredentialsTokenResponseClient(); | |
@Override | |
public void apply(RequestTemplate template) { | |
OAuth2AuthorizedClient oauthClient = this.authorizedClientService.loadAuthorizedClient( | |
this.clientRegistrationId, | |
this.clientRegistrationId); | |
if (oauthClient != null) { | |
ClientRegistration clientRegistration = oauthClient.getClientRegistration(); | |
if (!this.isClientCredentialsGrantType(clientRegistration)) { | |
throw new UnsupportedOperationException("Only client_credentials grant type is supported"); | |
} | |
if (this.hasTokenExpired(oauthClient)) { | |
oauthClient = this.getAuthorizedClient(clientRegistration); | |
} | |
} else { | |
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(this.clientRegistrationId); | |
if (clientRegistration == null) { | |
throw new IllegalArgumentException("Could not find ClientRegistration with id " + this.clientRegistrationId); | |
} | |
if (!this.isClientCredentialsGrantType(clientRegistration)) { | |
throw new UnsupportedOperationException("Only client_credentials grant type is supported"); | |
} | |
oauthClient = this.getAuthorizedClient(clientRegistration); | |
} | |
template.header(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", oauthClient.getAccessToken().getTokenValue())); | |
} | |
private boolean isClientCredentialsGrantType(ClientRegistration clientRegistration) { | |
return AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType()); | |
} | |
private OAuth2AuthorizedClient getAuthorizedClient(ClientRegistration clientRegistration) { | |
OAuth2ClientCredentialsGrantRequest clientCredentialsGrantRequest = new OAuth2ClientCredentialsGrantRequest(clientRegistration); | |
OAuth2AccessTokenResponse tokenResponse = this.clientCredentialsTokenResponseClient.getTokenResponse(clientCredentialsGrantRequest); | |
Authentication authentication = new Authentication() { | |
@Override | |
public Collection<? extends GrantedAuthority> getAuthorities() { | |
throw unsupported(); | |
} | |
@Override | |
public Object getCredentials() { | |
throw unsupported(); | |
} | |
@Override | |
public Object getDetails() { | |
throw unsupported(); | |
} | |
@Override | |
public Object getPrincipal() { | |
throw unsupported(); | |
} | |
@Override | |
public boolean isAuthenticated() { | |
throw unsupported(); | |
} | |
@Override | |
public void setAuthenticated(boolean isAuthenticated) | |
throws IllegalArgumentException { | |
throw unsupported(); | |
} | |
@Override | |
public String getName() { | |
return OAuth2FeignRequestInterceptor.this.clientRegistrationId; | |
} | |
private UnsupportedOperationException unsupported() { | |
return new UnsupportedOperationException("Not Supported"); | |
} | |
}; | |
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient( | |
clientRegistration, | |
authentication.getName(), | |
tokenResponse.getAccessToken()); | |
this.authorizedClientService.saveAuthorizedClient( | |
authorizedClient, | |
authentication); | |
return authorizedClient; | |
} | |
private boolean hasTokenExpired(OAuth2AuthorizedClient authorizedClient) { | |
Instant now = this.clock.instant(); | |
Instant expiresAt = authorizedClient.getAccessToken().getExpiresAt(); | |
if (now.isAfter(expiresAt.minus(this.accessTokenExpiresSkew))) { | |
return true; | |
} | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment