SpringBoot security server2server Oauth

SpringBoot security server2server Oauth

Background

There is handful samples about SpringBoot Server2Server ClientCredential type oauth, but sometimes we need password grant type oauth. e.g. When connecting to salesforce for restful request.

Here I provide the sample code based on some references and experiments.

References:

https://davidagood.com/oauth-client-credentials-auto-refresh-spring/
https://github.com/helloworldless/spring-oauth2-client-credentials-webclient
https://www.javaer101.com/en/article/4829693.html
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2client

  • build.gradle
  •   implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.springframework.boot:spring-boot-starter-webflux'
        implementation 'org.springframework.boot:spring-boot-starter-actuator'
        implementation 'org.springframework.boot:spring-boot-starter-security'
        implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
    
  • Application.properties
  • spring.security.oauth2.client.registration.eric.client_id=
    spring.security.oauth2.client.registration.eric.client_secret=
    spring.security.oauth2.client.registration.eric.client-authentication-method=post
    spring.security.oauth2.client.registration.eric.authorization-grant-type=password
    spring.security.oauth2.client.provider.eric.token-uri=
    3. public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter
    

    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.function.Function;

    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;
    import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
    import org.springframework.security.oauth2.client.OAuth2AuthorizationFailureHandler;
    import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
    import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
    import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
    import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
    import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
    import org.springframework.security.oauth2.client.RemoveAuthorizedClientOAuth2AuthorizationFailureHandler;
    import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
    import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
    import org.springframework.util.StringUtils;
    import org.springframework.web.reactive.function.client.WebClient;

    @EnableWebSecurity
    public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
    @Value("${eric.username}")
    private String username;

    @Value("${eric.password}")
    private String password;
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/getab").permitAll().anyRequest().authenticated().and().httpBasic().and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().csrf().disable();
    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientService authorizedClientService) {
        OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
                .password().build();
        AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                clientRegistrationRepository, authorizedClientService);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
        authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());
        return authorizedClientManager;
    private Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper() {
        return authorizeRequest -> {
            Map<String, Object> contextAttributes = Collections.emptyMap();
            // HttpServletRequest servletRequest = authorizeRequest.getAttribute(HttpServletRequest.class.getName());
            if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
                contextAttributes = new HashMap<>();
                // `PasswordOAuth2AuthorizedClientProvider` requires both attributes
                contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
                contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
            return contextAttributes;
    @Bean("authenticatedWebClient")
    WebClient webClient(WebClient.Builder webClientBuilder, OAuth2AuthorizedClientManager authorizedClientManager,
            @Qualifier("resourceServerAuthorizationFailureHandler") OAuth2AuthorizationFailureHandler failureHandler) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
                authorizedClientManager);
        oauth2Client.setAuthorizationFailureHandler(failureHandler);
        // @formatter:off
        return webClientBuilder.apply(oauth2Client.oauth2Configuration()).build();
        // @formatter:on
    @Bean("resourceServerAuthorizationFailureHandler")
    OAuth2AuthorizationFailureHandler resourceServerAuthorizationFailureHandler(
            OAuth2AuthorizedClientService authorizedClientService) {
        return new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(
                (clientRegistrationId, principal, attributes) -> {
                    System.out.println("----resourceServerAuthorizationFailureHandler ----");
                    authorizedClientService.removeAuthorizedClient(clientRegistrationId, principal.getName());
    @Bean("authorizationServerAuthorizationFailureHandler")
    OAuth2AuthorizationFailureHandler authorizationFailureHandler(
            OAuth2AuthorizedClientService authorizedClientService) {
        return new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(
                (clientRegistrationId, principal, attributes) -> {
                    System.out.println("----resourceServerAuthorizationFailureHandler ----");
                    authorizedClientService.removeAuthorizedClient(clientRegistrationId, principal.getName());
    4. WebClient Usage:
    

    @Autowired
    private WebClient webClient;

    JsonNode res = webClient.method(HttpMethod.GET).
    uri("url")