Improve oauth2 (#3014)
* feat: add custom header to oauth2 req * fix: current.getClass() raise NPE * fix: use access token instead of id token * fix: missing dependency * feat: add oauth2 login from swagger-ui * docs: update oauth2 configuration
This commit is contained in:
committed by
GitHub
parent
394048a583
commit
8d83218dc8
@@ -29,6 +29,7 @@ import org.eclipse.hawkbit.sdk.HawkbitClient;
|
||||
import org.eclipse.hawkbit.sdk.HawkbitServer;
|
||||
import org.eclipse.hawkbit.sdk.Tenant;
|
||||
import org.eclipse.hawkbit.ui.view.util.Utils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
@@ -41,7 +42,10 @@ import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
|
||||
@Slf4j
|
||||
@Theme("hawkbit")
|
||||
@@ -56,22 +60,32 @@ public class HawkbitUiApp implements AppShellConfigurator {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
private static final RequestInterceptor AUTHORIZATION = requestTemplate -> {
|
||||
final Authentication authentication = Objects.requireNonNull(
|
||||
SecurityContextHolder.getContext().getAuthentication(), "No authentication available in security context!");
|
||||
final Object principal = Objects.requireNonNull(authentication.getPrincipal(), "User is null!");
|
||||
if (principal instanceof OidcUser oidcUser) {
|
||||
requestTemplate.header(
|
||||
AUTHORIZATION_HEADER,
|
||||
"Bearer " + oidcUser.getIdToken().getTokenValue());
|
||||
} else {
|
||||
final String user = String.valueOf(principal);
|
||||
final Object pass = Objects.requireNonNull(authentication.getCredentials(), "Password is not available!");
|
||||
requestTemplate.header(
|
||||
AUTHORIZATION_HEADER,
|
||||
"Basic " + Base64.getEncoder().encodeToString((user + ":" + pass).getBytes(ISO_8859_1)));
|
||||
}
|
||||
};
|
||||
|
||||
private static RequestInterceptor authorizationInterceptor(final OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
|
||||
return requestTemplate -> {
|
||||
final Authentication authentication = Objects.requireNonNull(
|
||||
SecurityContextHolder.getContext().getAuthentication(), "No authentication available in security context!");
|
||||
if (authentication instanceof OAuth2AuthenticationToken oauth2Token) {
|
||||
// line from /org/springframework/security/oauth2/client/web/client/OAuth2ClientHttpRequestInterceptor.java#authorizeClient
|
||||
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(oauth2Token
|
||||
.getAuthorizedClientRegistrationId()).principal(authentication).build();
|
||||
OAuth2AuthorizedClient authorizedClient = oAuth2AuthorizedClientManager.authorize(authorizeRequest);
|
||||
if (authorizedClient != null) {
|
||||
requestTemplate.header(AUTHORIZATION_HEADER, "Bearer " + authorizedClient.getAccessToken().getTokenValue());
|
||||
} else {
|
||||
log.warn("No authorized client found for principal {} — request will be sent without Authorization header", oauth2Token
|
||||
.getName());
|
||||
}
|
||||
} else {
|
||||
final Object principal = Objects.requireNonNull(authentication.getPrincipal(), "User is null!");
|
||||
final String user = String.valueOf(principal);
|
||||
final Object pass = Objects.requireNonNull(authentication.getCredentials(), "Password is not available!");
|
||||
requestTemplate.header(
|
||||
AUTHORIZATION_HEADER,
|
||||
"Basic " + Base64.getEncoder().encodeToString((user + ":" + pass).getBytes(ISO_8859_1)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static final ErrorDecoder DEFAULT_ERROR_DECODER = new ErrorDecoder.Default();
|
||||
private static final ErrorDecoder ERROR_DECODER = (methodKey, response) -> {
|
||||
@@ -85,12 +99,14 @@ public class HawkbitUiApp implements AppShellConfigurator {
|
||||
}
|
||||
|
||||
@Bean
|
||||
HawkbitClient hawkbitClient(final HawkbitServer hawkBitServer) {
|
||||
HawkbitClient hawkbitClient(final HawkbitServer hawkBitServer,
|
||||
@Autowired(required = false) final OAuth2AuthorizedClientManager authorizedClientManager) {
|
||||
final RequestInterceptor authorization = authorizationInterceptor(authorizedClientManager);
|
||||
return new HawkbitClient(
|
||||
hawkBitServer, null, null, null,
|
||||
ERROR_DECODER,
|
||||
(tenant, controller) -> controller == null
|
||||
? AUTHORIZATION
|
||||
? authorization
|
||||
: HawkbitClient.DEFAULT_REQUEST_INTERCEPTOR_FN.apply(tenant, controller));
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.security;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -16,13 +18,32 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.oauth2.client", name = "enabled")
|
||||
public class Oauth2ClientConfig {
|
||||
|
||||
@Bean(name = "hawkbitOAuth2ClientCustomizer")
|
||||
@ConditionalOnProperty(prefix = "hawkbit.server.security.oauth2.client", name = "enabled")
|
||||
@ConditionalOnMissingBean(name = "hawkbitOAuth2ClientCustomizer")
|
||||
Customizer<OAuth2LoginConfigurer<HttpSecurity>> defaultOAuth2ClientCustomizer() {
|
||||
return Customizer.withDefaults();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public OAuth2AuthorizationRequestResolver authorizationRequestResolver(
|
||||
ClientRegistrationRepository repo,
|
||||
OidcClientProperties properties) {
|
||||
|
||||
final Map<String, String> additionalQueryStringParams = properties.getOauth2().getClient().getAdditionalQueryStringParams();
|
||||
final DefaultOAuth2AuthorizationRequestResolver resolver = new DefaultOAuth2AuthorizationRequestResolver(repo,
|
||||
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);
|
||||
resolver.setAuthorizationRequestCustomizer(
|
||||
customizer -> customizer.additionalParameters(params -> params.putAll(additionalQueryStringParams)));
|
||||
return resolver;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.ui.security;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@@ -29,6 +32,7 @@ public class OidcClientProperties {
|
||||
public static class Client {
|
||||
|
||||
private boolean enabled = false;
|
||||
private Map<String, String> additionalQueryStringParams = new HashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user