In one of my previous blog posts, I gave an example of how to configure the Spring WebClient
for OAuth2 using Spring WebFlux. As most of the applications today are using Spring Web (Tomcat) and are not fully reactive, I also want to provide an example for this setup. In most cases, you just add Spring WebFlux to your existing application to make use of the Spring WebClient. Learn how to configure Spring WebClient for OAuth2 using a Servlet based application in this blog post.
OAuth2 Spring Web project setup
The Maven project for this example is quite similar to the WebFlux application. It includes Thymeleaf, Web, WebFlux, Security and the OAuth2 client:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>de.rieckpil.blog</groupId> <artifactId>spring-web-client-oauth2-servlet-stack</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-web-client-oauth2-servlet-stack</name> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
If you have Spring Web and Spring WebFlux on your classpath, Spring Boot assumes you want to run on Tomcat per default. With this setup, we can use our existing Servlet based applications and just use the WebClient
from Spring WebFlux, as the RestTemplate
won't receive further updates.
For the rest of the required configuration setup, follow the chapter in the WebFlux blog post.
Securing the Web application with OAuth2 login
The security configuration for a Spring Web application works a little bit different compared to Spring WebFlux. Here we have to extend the WebSecurityConfigurerAdapter
class and adjust the configuration by overriding methods.
I'm using the same approach as in the WebFlux example and require authentication for each request. In addition, the OAuth2 login and client support are enabled:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .oauth2Client() .and() .oauth2Login(); } } |
WebClient OAuth2 configuration for a Servlet application
Whereas we created a ServerOAuth2AuthorizedClientExchangeFilterFunction
for the WebFlux example, the Servlet approach works with a ServletOAuth2AuthorizedClientExchangeFilterFunction
.
The WebClient
setup therefor looks quite similar, except that the non-reactive classes are used:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | @Configuration public class WebClientConfig { @Bean public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) { ServletOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); return WebClient.builder() .filter(oauth) .build(); } @Bean public OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) { DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientRepository); authorizedClientManager.setAuthorizedClientProvider(new AuthorizationCodeOAuth2AuthorizedClientProvider()); return authorizedClientManager; } } |
With this setup, we can now inject an OAuth2 pre-configured WebClient
instance to our classes.
Accessing OAuth2 protected resources on GitHub
The Thymeleaf page doesn't any adjustments compared to the WebFlux example (you can find it here). That's why I did not include it in this post and just did a small change for the MVC controller.
Whereas we made use of the reactive types Flux
and Mono
in the WebFlux application, I'm now using .block()
on the WebClient
request to return a String
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | @Controller @RequestMapping("/") public class GitHubController { private static final String GITHUB_API_URL = "https://api.github.com"; private WebClient webClient; public GitHubController(WebClient webClient) { this.webClient = webClient; } @GetMapping public String index(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient, @AuthenticationPrincipal OAuth2User oauth2User, Model model) { model.addAttribute("repositories", fetchAllRepositories(authorizedClient)); model.addAttribute("username", oauth2User.getAttributes().get("login")); return "index"; } private List<String> fetchAllRepositories(OAuth2AuthorizedClient authorizedClient) { List<String> repositoryNames = new ArrayList<>(); this.webClient .get() .uri(GITHUB_API_URL, uriBuilder -> uriBuilder .path("/user/repos") .queryParam("per_page", 100) .build() ) .attributes(oauth2AuthorizedClient(authorizedClient)) .retrieve() .bodyToMono(ArrayNode.class) .block() .forEach(jsonNode -> repositoryNames.add(jsonNode.get("full_name").asText())); return repositoryNames; } } |
You can find the source code with further instructions on how to run this application on GitHub.
Have fun using the OAuth2 with the Spring WebClient for a Spring Web (Servlet) application,
Phil