Reputation: 1421
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
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
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
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