Accessing HttpSession in session scoped Bean - Spring - spring

I have a session scoped Bean and want to get access to the session.
I tried this:
#Bean
#SessionScope
public WebClient oauthClient(HttpSession session) {
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(1024 * 1000)).build();
return WebClient.builder()
.exchangeStrategies(exchangeStrategies)
.filter(authHeader((String) session.getAttribute("access_token")))
.build();
}
but
session.getAttribute("access_token")
returns null.
Thanks for your help

There was an Problem in my Controller. This Code works fine.

Related

Webclient change encoding mode conditionally

I have created WebClient bean as follow
#Bean
public WebClient webClient(final HttpClient httpClient) {
return WebClient
.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(codecInmemoryMaxSize))
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}
I am reusing the same bean to make HTTP calls like
public Mono<ResponseModel> get(final String url) {
return webClient.get().uri(url)
.exchangeToMono(clientResponse -> clientResponse.toEntity(String.class)
.map(responseEntity -> new ResponseModel(responseEntity.getBody(), responseEntity.getStatusCode().value(),
CollectionUtils.toMultiValueMap(responseEntity.getHeaders().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))))));
}
I want to set encoding mode conditionally while calling API. is there any way to do that?
I know there as way like to set factory like
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory();
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
return WebClient
.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(codecInmemoryMaxSize))
.clientConnector(new ReactorClientHttpConnector(httpClient))
.uriBuilderFactory(factory)
.build();
but these will apply as bean level so API follows the same encoding mode. I want to make it conditional while calling API.

Spring WebClient and shared client credential token for all requests

I tried to use Spring WebClient and Spring Security OAuth2 Client for communication between two applications. I need to use OAuth2 Client Credentials grant type and use the same credentials and the same token for all requests between these applications. In the OAuth2 terminology resource owner is an application A itself and resource server is an application B (it is Keycloak Admin API). I use Spring Boot auto configuration from application.yaml and servlet environment. So I implemented it like this:
WebClient buildWebClient(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository, String clientName) {
final ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrationRepository, authorizedClientRepository);
oauth2Client.setDefaultClientRegistrationId(clientName);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
I found that I have an issue and invalid tokens (I use Keycloak and if my Keycloak is restarted then all old tokens are invalid) are never removed (you can see it in issue #9477) so I started debuging whole process of authorization in ServletOAuth2AuthorizedClientExchangeFilterFunction. I found that tokens are saved per user of the application A. It means that every user of the application A has its own token for authorization to the application B. But this is not my intended behavior I want to use only one token in the application A for consuming a resource from the application B.
I think that I found two solutions but both of them are not optimal.
Solution 1.:
Add another default request after ServletOAuth2AuthorizedClientExchangeFilterFunction which replaces user Authentication in attributes by an application Authentication. Problem is that it dependes on an internal implemention of ServletOAuth2AuthorizedClientExchangeFilterFunction which use a private attribute AUTHENTICATION_ATTR_NAME = Authentication.class.getName();.
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.defaultRequest(spec -> spec.attributes(attr ->
attr.put(AUTHENTICATION_ATTR_NAME, new CustomApplicaitonAuthentication())
)
.build();
Solution 2.:
Use a custom implementation of OAuth2AuthorizedClientRepository instead of default AuthenticatedPrincipalOAuth2AuthorizedClientRepository (which I have to do anyway because of the issue with removing invalid tokens) where I ignore user's Authentication and I use a custom application Authentication.
#Override
public <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRegistrationId, Authentication principal,
HttpServletRequest request) {
return this.authorizedClientService.loadAuthorizedClient(clientRegistrationId, new CustomApplicaitonAuthentication());
}
#Override
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,
HttpServletRequest request, HttpServletResponse response) {
this.authorizedClientService.saveAuthorizedClient(authorizedClient, new CustomApplicaitonAuthentication());
}
But I think that both my solutions are not optimal. Is there another way how to do it more straightforward?
I found solution because I needed to run it without a servlet request (messaging, scheduler etc.). Therefore I needed to update it and use the AuthorizedClientServiceOAuth2AuthorizedClientManager instead of the default DefaultOAuth2AuthorizedClientManager. Then I reallized that it does the same thing like I need.
#Bean
WebClient buildWebClient(
OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager,
OAuth2AuthorizationFailureHandler oAuth2AuthorizationFailureHandler,
String clientName) {
final ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
oAuth2AuthorizedClientManager);
oauth2Client.setDefaultClientRegistrationId(clientName);
oauth2Client.setAuthorizationFailureHandler(oAuth2AuthorizationFailureHandler);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager
(ClientRegistrationRepository clients, OAuth2AuthorizedClientService service, OAuth2AuthorizationFailureHandler authorizationFailureHandler) {
AuthorizedClientServiceOAuth2AuthorizedClientManager manager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(clients, service);
manager.setAuthorizationFailureHandler(authorizationFailureHandler);
return manager;
}
#Bean
public OAuth2AuthorizationFailureHandler resourceServerAuthorizationFailureHandler(
OAuth2AuthorizedClientService authorizedClientService) {
return new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(
(clientRegistrationId, principal, attributes) ->
authorizedClientService.removeAuthorizedClient(clientRegistrationId, principal.getName()));
}
We had to add a custom OAuth2AuthorizationFailureHandler because if you do not use this constructor ServletOAuth2AuthorizedClientExchangeFilterFunction(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) and you use a custom OAuth2AuthorizedClientManager then it is not configured and you have to do it manually.

Spring Boot Reactive WebClient "serverWebExchange must be null" when Spring Security OAuth is in use

I would like to use a WebClient from a Spring Boot WebFlux app that is set up with Spring Security OAuth 2 Client Credentials.
But, I get: java.lang.IllegalArgumentException: serverWebExchange must be null
The code is here: https://github.com/mparaz/spring-apigee-client
When I disable Spring Security by removing it from the pom.xml, it works properly.
When I keep using Spring Security, but instead of returning the webClient() chain result to the controller, and just prints it out, it also works.
It looks like the Reactive client and server don't work together when Spring Security is used. How could I get them running together?
For me the problem was following
https://docs.spring.io/spring-security/site/docs/5.5.x/reference/html5/#oauth2Client-authorized-manager-provider
The DefaultOAuth2AuthorizedClientManager is designed to be used within the context of a HttpServletRequest. When operating outside of a HttpServletRequest context, use AuthorizedClientServiceOAuth2AuthorizedClientManager instead.
Comment on the following link worked for me
https://www.gitmemory.com/issue/spring-projects/spring-security/8444/621567261
Another link
https://github.com/spring-projects/spring-security/issues/8230
Comment
DefaultReactiveOAuth2AuthorizedClientManager is intended to be used within a request context.
Given that you're seeing serverWebExchange cannot be null, you must be operating outside of a request context, which in case you should use AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager instead.
NOTE: Change the ServerOAuth2AuthorizedClientRepository parameter to ReactiveOAuth2AuthorizedClientService.
Actual code
#Bean
fun serverOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations: List<ClientRegistration>)
: ServerOAuth2AuthorizedClientExchangeFilterFunction {
val clientRegistrationRepository = InMemoryReactiveClientRegistrationRepository(clientRegistrations)
val authorizedClientService = InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository)
val oAuth2AuthorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository,
authorizedClientService
)
val filterFunction = ServerOAuth2AuthorizedClientExchangeFilterFunction(oAuth2AuthorizedClientManager)
filterFunction.setDefaultClientRegistrationId(clientId)
return filterFunction
}
It seems that if you use an UnAuthenticatedServerOAuth2AuthorizedClientRepository it will propagate the webExchange from the source request going into to your #RestController into the WebClient you are using to invoke the other service causing the java.lang.IllegalArgumentException: serverWebExchange must be null
To fix this, use the autowired implementation of the ServerOAuth2AuthorizedClientRepository (this happens to be AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository)
#Bean
#LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder(ReactiveClientRegistrationRepository clientRegistrations,
ObjectMapper objectMapper,
ServerOAuth2AuthorizedClientRepository clientRepository) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2ClientFilter = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations,
clientRepository);
oauth2ClientFilter.setDefaultClientRegistrationId("apigee");
WebClient.Builder builder = WebClient.builder();
builder.defaultHeader("Content-Type", MediaType.APPLICATION_JSON.toString());
builder.defaultHeader("Accept", MediaType.APPLICATION_JSON.toString());
builder.filter(oauth2ClientFilter);
return builder;
}

Shall we make a bean and Autowire OkHttpClient

OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "q=609&client=122&layer=explore&key=w3S4BEmDKd8Q3VCCO2OZTnI8sAQxIFwA&name=utkarsh%20sharma&password=utk&phone=1111111112");
Request request = new Request.Builder()
.url("http://explore-uat.mapmyindia.in/explore-api/v1.3/")
.post(body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("Cache-Control", "no-cache")
.addHeader("Postman-Token", "44666246-b697-488f-9410-df09f7faa53a")
.build();
Response response = client.newCall(request).execute();
I'am using this code to make a post request to API.
I use this many times in my class.
Is it possible to make a bean of OKhttpClient and autowire in my class
Please reply!!Thnx in advance
You could do it by declaring a bean somewhere in your configuration:
#Configuration
public class HttpClientConfiguration {
#Bean
public OkHttpClient httpClient() {
return new OkHttpClient();
}
}
Also, if not declared otherwise, every spring bean is by default a singleton: https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html
Regarding the initial question. I think you should declare it as a spring bean. It should ease testing.
I would declare it as Spring bean, since that makes it much easier for testing compared to a Singleton solution. However, since you are using Spring boot, you could also just use RestTemplate, as described here: https://spring.io/guides/gs/consuming-rest/
A singleton as defined below should be sufficient:
public class OkHttpFactory {
private static OkHttpClient client = new OkHttpClient();
public OkHttpClient getClient() {
return client;
}
}

Spring RestTemplate - Need to release connection?

This is my Configuration for Rest Template,
#Bean
#Qualifier("myRestService")
public RestTemplate createRestTemplate(#Value("${connection.timeout}") String maxConn) {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(maxTotalConn);
connectionManager.setDefaultMaxPerRoute(maxPerChannel);
RequestConfig config = RequestConfig.custom().setConnectTimeout(100000).build();
CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager)
.setDefaultRequestConfig(config).build();
ClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setErrorHandler(new RestResponseErrorHandler());
restTemplate.setMessageConverters(createMessageConverters());
return restTemplate;
}
Am using PoolingHttpClientConnectionManager for managing the connections.
Its being accessed by the following code,
ResponseEntity<String> response = restClient.exchange( url, HttpMethod.GET, entity , String.class );
Do i need to release the connection after the above call or is it taken care by RestTemplate. If we need to take care of releasing connection.
Please can some one explain/show how to release the connection.
You should declare the ClientHttpRequestFactory as a bean. By declaring it as a bean, it becomes managed by the Spring bean factory, which will call the factory's destroy method when the application is closed, or the bean goes out of scope. The destroy method of the ClientHttpRequestFactory will close the underlying ClientConnectionManager's connection pool. You can check the Spring API docs for this.
#Bean
public ClientHttpRequestFactory createRequestFactory(#Value("${connection.timeout}") String maxConn) {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(maxTotalConn);
connectionManager.setDefaultMaxPerRoute(maxPerChannel);
RequestConfig config = RequestConfig.custom().setConnectTimeout(100000).build();
CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager)
.setDefaultRequestConfig(config).build();
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
Then you can use this bean to create your RestTemplate:
#Bean
#Qualifier("myRestService")
public RestTemplate createRestTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.setErrorHandler(new RestResponseErrorHandler());
restTemplate.setMessageConverters(createMessageConverters());
return restTemplate;
}
The question which you have asked:
Do i need to release the connection after the above call or is it taken care by RestTemplate. If we need to take care of releasing connection.
No, you do not need to close the connection on the response, if you use resttemplate.
From the apache httpclient, you need to consume the complete response (EntityUtils.consume(HttpEntity) and close the response.
This can be verified in the ClientConnectionRelease.java
But RestTemplate does this for you, to verify the same have a look into
RestTemplate.java
Look for method
protected <T> T doExecute(URI url,...) {
try {
ClientHttpRequest request = this.createRequest(url, method);
...
response = request.execute();
...
if(responseExtractor != null) {
var7 = responseExtractor.extractData(response);
return var7;
}
...
...
} finally {
if(response != null) {
response.close();
}
}
}
Where response extractor does the work for you by consuming the response using
responseExtractor.extractData(response);
And after extracting the data completely it is closing response.close() as well.
I think the answer is here: org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#doExecuteRequest
#Override
protected RemoteInvocationResult doExecuteRequest(
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
throws IOException, ClassNotFoundException {
HttpPost postMethod = createHttpPost(config);
setRequestBody(config, postMethod, baos);
try {
HttpResponse response = executeHttpPost(config, getHttpClient(), postMethod);
validateResponse(config, response);
InputStream responseBody = getResponseBody(config, response);
return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
}
finally {
postMethod.releaseConnection();
}
}

Resources