Joseph Hwang
Joseph Hwang

Reputation: 1421

Where to put username and password of WebClient of Spring Webflux?

I try to make Spring WebFlux security application with router and handler classes. First, below codes are the configuration codes of WebFlux security.

@Configuration
@EnableWebFluxSecurity
public class BlogWebFluxSecurityConfig {
    
    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        
        UserDetails userWebFlux = User.withUsername("joseph").password("password").roles("USER").build();
        return new MapReactiveUserDetailsService(userWebFlux);
    }
    
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
        .authorizeExchange()
        .pathMatchers("/route/user/all", "/route/post/all").permitAll()
        .pathMatchers(HttpMethod.GET, "/route/user/**", "/route/post/**").hasRole("USER")
        .anyExchange().authenticated()
        .and()
        .httpBasic();
        
        return http.build();
    } 
}

And the next codes are about the router class:

@Configuration
@EnableWebFlux
public class BlogWebFluxEndpointRouter {

    @Bean
    public RouterFunction<ServerResponse> routesUser(UserHandler handler) {
        
        return RouterFunctions.route(RequestPredicates.GET("/route/user/all"), handler::findAll)
                    .andRoute(RequestPredicates.GET("/route/user/id/{id}"), handler::findById)
                    .andRoute(RequestPredicates.GET("/route/user/username/{username}"), handler::findByUsername)
                    .andRoute(RequestPredicates.GET("/route/user/email/{email}"), handler::findByEmail)
                    .andRoute(RequestPredicates.POST("/route/user/create"), handler::register)
                    .andRoute(RequestPredicates.GET("/route/user/login/{username}/{password}"), handler::authenticate);
    }
    
    @Bean
    public RouterFunction<ServerResponse> routesPost(PostHandler handler) {
        
        return RouterFunctions.route(RequestPredicates.GET("/route/post/all"), handler::findAll)
                    .andRoute(RequestPredicates.GET("/route/post/id/{id}"), handler::findById)
                    .andRoute(RequestPredicates.GET("/route/post/delete/{id}"), handler::deleteById)
                    .andRoute(RequestPredicates.POST("/route/post/create"), handler::create)
                    .andRoute(RequestPredicates.PUT("/route/post/{id}/{content}"), handler::edit);
    }
}

Even the network is REST web service, But I use WebClient class of WebFlux.

public void functionOnUserDocument() { 
    client.get().uri("/route/user/all").accept(MediaType.APPLICATION_JSON).exchange()
            .flatMapMany(response -> response.bodyToFlux(User.class))
            .subscribe(u -> System.out.println("All Users : " + u.getUsername() + ":" + u.getEmail() + ":" + u.getFullname()));
    
    client.get().uri("/route/user/id/{id}", "0002").accept(MediaType.APPLICATION_JSON).exchange()
            .flatMap(response -> response.bodyToMono(User.class))
            .subscribe(u -> System.out.println("GET by Id : " + u.getUsername() + ":" + u.getEmail() + ":" + u.getFullname()));
    
    client.get().uri("/route/user/username/{username}", "jina").accept(MediaType.APPLICATION_JSON).exchange()
            .flatMap(response -> response.bodyToMono(User.class))
            .subscribe(u -> System.out.println("Get by username : " + u.getUsername() + ":" + u.getEmail() + ":" + u.getFullname()));

    client.get().uri("/route/user/email/{email}", "[email protected]").accept(MediaType.APPLICATION_JSON).exchange()
            .flatMap(response -> response.bodyToMono(User.class))
            .subscribe(u -> System.out.println("Get By Email : " + u.getUsername() + ":" + u.getEmail() + ":" + u.getFullname()));
    
    client.get().uri("/route/user/login/{username}/{password}", "julian", "password").exchange()
            .map(ClientResponse::statusCode).subscribe(response -> System.out.println("Login : " + response.getReasonPhrase()));
    
    User user = new User("0005", 4L, "jane", "password", "[email protected]", "누나", "USER");

    client.post().uri("/route/user/create").body(Mono.just(user), User.class).exchange() 
            .map(ClientResponse::statusCode).subscribe(response -> System.out.println("User Creation: " + response.getReasonPhrase()));
}

Because I make the WebFlux security configuration, definitely some WebClient can not be executed and forbidden like below:

Login : Unauthorized
User Creation: Forbidden

I do not use cURL. So what I want to know is what my WebClient methods are to be, where username and password have to be located and transferred to WebClient class.

Upvotes: 11

Views: 27098

Answers (3)

James
James

Reputation: 12182

Since spring 5.1 you should set basic authentication with HttpHeaders#setBasicAuth, like this:

webClient
    .get()
    .uri("https://example.com")
    .headers(headers -> headers.setBasicAuth("username", "password"))
    .exchange()
    ....

the previous approach, of using .filter(basicAuthentication("user", "password"), is now deprecated.

Upvotes: 32

Anton Bogdan
Anton Bogdan

Reputation: 71

Spring provides API for supplying basic authentication parameters to your WebClient via ClientFilters.

You can achieve the same result of having Authorization header set up with less custom coding.

Please see code snippet below from spring docs:

import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;

    WebClient client = WebClient.builder()
            .filter(basicAuthentication("user", "password"))
            .build();

Upvotes: 7

MuratOzkan
MuratOzkan

Reputation: 2730

HTTP Basic authentication expects the username and password encoded in Base64 format in Authorization header. Also, you don't need to have a login endpoint since this information should be sent with every request.

Add the Basic Auth header to each call in your client like the following:

String basicAuthHeader = "basic " + Base64Utils.encodeToString((username + ":" + password).getBytes())

client.get().uri("/route/user/all")
      .accept(MediaType.APPLICATION_JSON)
      .header(HttpHeaders.AUTHORIZATION, basicAuthHeader)
      .exchange()
      .flatMapMany(response -> response.bodyToFlux(User.class))
      .subscribe(u -> System.out.println("All Users : " + u.getUsername() + ":" + u.getEmail() + ":" + u.getFullname()));

Upvotes: 9

Related Questions