I am trying to replace existing Spring KerberosRestTemplate with WebClient APIs.
So is there any support provided for Kerberos in new WebClient APIs?
any help will be appreciated even pointing to some tutorial/doc will be helpful.
You need to create an ExchangeFilterFunction implementation which checks for the WWW-Authenticate header and then re-sends the request with an Authorization header.
#Override
public Mono<ClientResponse> filter(final ClientRequest request, final ExchangeFunction next) {
return next.exchange(request)
.flatMap(response -> {
final Set<String> headerValues = Sets.newLinkedHashSet(response.headers().header(HttpHeaders.WWW_AUTHENTICATE));
if (headerValues.contains("Negotiate")) {
final String authHeader = doAs(new CreateAuthorizationHeaderAction(userPrincipal, "HTTP/" + request.url().getHost()));
final ClientRequest authenticatedRequest = ClientRequest.from(request)
.header(HttpHeaders.AUTHORIZATION, "Negotiate " + authHeader)
.build();
return next.exchange(authenticatedRequest);
}
return Mono.just(response);
});
}
You can lift the implementation for CreateAuthorizationHeaderAction here.
Related
I have a controller that uses RestTemplate to get data from several rest endpoints. Since RestTemplate is blocking, my web page is taking long time to load. In order to increase the performance, I am planning to replace all my usages of RestTemplate with WebClient. One of the methods I currently have that uses RestTemplate is as below.
public List<MyObject> getMyObject(String input){
URI uri = UriComponentsBuilder.fromUriString("/someurl")
.path("123456")
.build()
.toUri();
RequestEntity<?> request = RequestEntity.get(uri).build();
ParameterizedTypeReference<List<MyObject>> responseType = new ParameterizedTypeReference<List<MyObject>>() {};
ResponseEntity<List<MyObject>> responseEntity = restTemplate.exchange(request, responseType);
MyObject obj = responseEntity.getBody();
}
Now I want to replace my above method to use WebClient but I am new to WebClient and not sure where to start. Any direction and help is appreciated.
To help you I am giving you example how we can replace restTemple with webClient. I hope you have already setup your pom.xml
Created a Configuration class.
#Slf4j
#Configuration
public class ApplicationConfig {
/**
* Web client web client.
*
* #return the web client
*/
#Bean
WebClient webClient() {
return WebClient.builder()
.filter(this.logRequest())
.filter(this.logResponse())
.build();
}
private ExchangeFilterFunction logRequest() {
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
log.info("WebClient request: {} {} {}", clientRequest.method(), clientRequest.url(), clientRequest.body());
clientRequest.headers().forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
return Mono.just(clientRequest);
});
}
private ExchangeFilterFunction logResponse() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
log.info("WebClient response status: {}", clientResponse.statusCode());
return Mono.just(clientResponse);
});
}
}
Plus a service class calling WebClient
#Component
#RequiredArgsConstructor
public class MyObjectService {
private final WebClient webClient;
public Mono<List<Object>> getMyObject(String input) {
URI uri = UriComponentsBuilder.fromUriString("/someurl")
.path("123456")
.build()
.toUri();
ParameterizedTypeReference<List<MyObject>> responseType = new ParameterizedTypeReference<List<MyObject>>() {
};
return this.webClient
.get()
.uri(uri)
.exchange()
.flatMap(response -> response.bodyToMono(responseType));
}
}
This will give you a non blocking Mono of List<MyObject>, you can also extract body to flux by using response.bodyToFlux(responseType)
I hope this will give you a base to explore more.
I am building a userinfo endpoint on my Webflux rest api, how do I access the access_token passed in through the Authorization header in the rest call. Also need a similar endpoint to update the user.
All the examples I have found with latest spring 5/boot 2 are about securing a webapp.
#GetMapping("/api/user-info")
public Map userInfo(OAuth2AuthenticationToken authentication) {
OAuth2AuthorizedClient authorizedClient = this.getAuthorizedClient(authentication);
Map userAttributes = Collections.emptyMap();
String userInfoEndpointUri = authorizedClient
.getClientRegistration()
.getProviderDetails()
.getUserInfoEndpoint()
.getUri();
if (!StringUtils.isEmpty(userInfoEndpointUri)) {
// userInfoEndpointUri is optional for OIDC Clients
userAttributes = WebClient.builder()
.filter(oauth2Credentials(authorizedClient))
.build()
.get()
.uri(userInfoEndpointUri)
.retrieve()
.bodyToMono(Map.class)
.block();
}
return userAttributes;
}
private OAuth2AuthorizedClient getAuthorizedClient(OAuth2AuthenticationToken authentication) {
return this.authorizedClientService.loadAuthorizedClient(
authentication.getAuthorizedClientRegistrationId(), authentication.getName());
}
private ExchangeFilterFunction oauth2Credentials(OAuth2AuthorizedClient authorizedClient) {
return ExchangeFilterFunction.ofRequestProcessor(
clientRequest -> {
ClientRequest authorizedRequest = ClientRequest.from(clientRequest)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + authorizedClient.getAccessToken().getTokenValue())
.build();
return Mono.just(authorizedRequest);
});
}
OAuth2AuthenticationToken object defined in the method is null which is understandable but not sure what else need configuring.
Thanks for your help.
I am using Retrofit.
I have an endpoint that redirects to another endpoint. The latter (the endpoint that I end up at) has a parameter in its URL that I need. What is the best way to get the value of this parameter?
I cannot even figure out how to get the URL that I am redirected to, using Retrofit.
OkHttp's Response will give you the wire-level request (https://square.github.io/okhttp/3.x/okhttp/okhttp3/Response.html#request--). This will be the Request that initiated the Response from the redirect. The Request will give you its HttpUrl, and HttpUrl can give you its parameters' keys and values, paths, etc.
With Retrofit 2, simply use retrofit2.Response.raw() to get the okhttp3.Response and follow the above.
I am using retrofit. And I can get the redirect url following this way :
private boolean handleRedirectUrl(RetrofitError cause) {
if (cause != null && cause.getResponse() != null) {
List<Header> headers = cause.getResponse().getHeaders();
for (Header header : headers) {
//KEY_HEADER_REDIRECT_LOCATION = "Location"
if (KEY_HEADER_REDIRECT_LOCATION.equals(header.getName())) {
String redirectUrl = header.getValue();
return true;
}
}
}
return false;
}
Hope it could help someone.
Solution for this would be to use an interceptor e.g.
private Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response response = chain.proceed(chain.request());
locationHistory.add(response.header("Location"));
return response;
}
};
Add the interceptor to your HttpClient and add that to Retrofit(using 2.0 for this example)
public void request(String url) {
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.followRedirects(true);
client.addNetworkInterceptor(interceptor);
OkHttpClient httpClient = client.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build();
}
Now you have full access the the entire redirect history.
I am using OAuth and I need to put the OAuth token in my header every time I make a request. I see the #Header annotation, but is there a way to make it parameterized so i can pass in at run time?
Here is the concept
#Header({Authorization:'OAuth {var}', api_version={var} })
Can you pass them in at Runtime?
#GET("/users")
void getUsers(
#Header("Authorization") String auth,
#Header("X-Api-Version") String version,
Callback<User> callback
)
Besides using #Header parameter, I'd rather use RequestInterceptor to update all your request without changing your interface. Using something like:
RestAdapter.Builder builder = new RestAdapter.Builder()
.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("Accept", "application/json;versions=1");
if (isUserLoggedIn()) {
request.addHeader("Authorization", getToken());
}
}
});
p/s : If you are using Retrofit2, you should use Interceptor instead of RequestInterceptor
Since RequestInterceptor is not longer available in Retrofit 2.0
Yes, you can pass them in runtime. As a matter of fact, pretty much exactly as you typed it out. This would be in your API interface class, named say SecretApiInterface.java
public interface SecretApiInterface {
#GET("/secret_things")
SecretThing.List getSecretThings(#Header("Authorization") String token)
}
Then you pass the parameters to this interface from your request, something along those lines: (this file would be for example SecretThingRequest.java)
public class SecretThingRequest extends RetrofitSpiceRequest<SecretThing.List, SecretApiInteface>{
private String token;
public SecretThingRequest(String token) {
super(SecretThing.List.class, SecretApiInterface.class);
this.token = token;
}
#Override
public SecretThing.List loadDataFromNetwork() {
SecretApiInterface service = getService();
return service.getSecretThings(Somehow.Magically.getToken());
}
}
Where Somehow.Magically.getToken() is a method call that returns a token, it is up to you where and how you define it.
You can of course have more than one #Header("Blah") String blah annotations in the interface implementation, as in your case!
I found it confusing too, the documentation clearly says it replaces the header, but it DOESN'T!
It is in fact added as with #Headers("hardcoded_string_of_liited_use") annotation
Hope this helps ;)
The accepted answer is for an older version of Retrofit. For future viewers the way to do this with Retrofit 2.0 is using a custom OkHttp client:
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Builder ongoing = chain.request().newBuilder();
ongoing.addHeader("Accept", "application/json;versions=1");
if (isUserLoggedIn()) {
ongoing.addHeader("Authorization", getToken());
}
return chain.proceed(ongoing.build());
}
})
.build();
Retrofit retrofit = new Retrofit.Builder()
// ... extra config
.client(httpClient)
.build();
Hope it helps someone. :)
Retrofit 2.3.0
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
okHttpClientBuilder
.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder newRequest = request.newBuilder().header("Authorization", accessToken);
return chain.proceed(newRequest.build());
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(GithubService.BASE_URL)
.client(okHttpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
I am using this to connect to GitHub.
I'm using Square's Retrofit library for short-lived network calls. There are a few pieces of data that I include as #Query params on every request. Like so:
#GET("/thingOne.php")
void thingOne(
#Query("app_version") String appVersion,
#Query("device_type") String deviceType,
Callback<Map<String,Object>> callback
);
#GET("/thingTwo.php")
void thingTwo(
#Query("app_version") String appVersion,
#Query("device_type") String deviceType,
Callback<Map<String,Object>> callback
);
It's cumbersome to have to define appVersion and deviceType for every single endpoint outlined in the Interface. Is there a way to set a base set of parameters that should be included with every request? Something similar to how we set a common Authorization Header?
RestAdapter restAdapter = new RestAdapter.Builder()
.setServer("...")
.setRequestHeaders(new RequestHeaders() {
#Override
public List<Header> get() {
List<Header> headers = new ArrayList<Header>();
Header authHeader = new Header(
"Authorization", "Bearer " + token);
headers.add(authHeader);
}
return headers;
}
})
.build();
this.service = restAdapter.create(ClientInterface.class);
You can ensure all requests have these query parameters by adding a custom RequestInterceptor to your RestAdapter
RequestInterceptor requestInterceptor = new RequestInterceptor()
{
#Override
public void intercept(RequestFacade request) {
request.addQueryParam("app_version", "Version 1.x");
request.addQueryParam("device_type", "Samsung S4");
}
};
restAdapter.setRequestInterceptor(requestInterceptor)