Testing RestTemplate with Custom HttpClient - java

I am developing a Spring Boot Application which calls a REST-API which frequently performs a 303 See Other redirect to the proper location.
For a given resource I start with a random initial URL, intercept the redirect to store the proper location for the next request and at last perform the redirect.
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
#Configuration
class RestTemplateFactory {
private static final Logger LOG = LoggerFactory.getLogger(RestTemplateFactory.class);
#Autowired
KeyMap keyMap;
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
HttpClient httpClient = HttpClientBuilder
.create()
.setRedirectStrategy(new DefaultRedirectStrategy() {
#Override
public boolean isRedirected(HttpRequest request, HttpResponse response,
HttpContext context) throws ProtocolException {
if (super.isRedirected(request, response, context)) {
String redirectURL = response.getFirstHeader("Location").getValue();
LOG.debug("Intercepted redirect: original={}, redirect={}", request.getRequestLine(),
redirectURL);
keyMap.put(redirectURL);
return true;
}
return false;
}
})
.build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
return builder
.requestFactory(requestFactory)
.build();
}
}
(The class KeyMap is used to store the location for some domain key, which is stored in a ThreadLocal before the RestTemplate is invoked.)
Question: How can I test this special RestTemplate?

Related

"Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource."

I've made an angular application with typescript code that uses a java spring web service, I'm having trouble with CORS.
The first http request gets an access token from the web server to use in all following requests, but all following requests are refused with this error:
Access to XMLHttpRequest at X from origin Y has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
X is the URL resource of my http method
Y is the origin of the request
API GATEWAY MODULE
package it.sitgeo.apigateway.controller;
import it.sitgeo.apigateway.exception.LoginException;
import it.sitgeo.apigateway.model.AppUser;
import it.sitgeo.apigateway.model.LoginForm;
import it.sitgeo.apigateway.service.AppUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
#RestController
#RequestMapping("/sitgeo")
#RequiredArgsConstructor
#CrossOrigin(allowedHeaders = "*")
public class AppUserController {
private final AppUserService service;
#PostMapping(path = "/login", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<String> login (#Valid #ModelAttribute LoginForm loginForm) throws LoginException {
String access_token = service.login(loginForm.getUsername(), loginForm.getPassword());
if(access_token == null){
throw new LoginException("Le credenziali inserite non sono corrette");
}
return ResponseEntity.ok(access_token);
}
PED MODULE
package it.sitgeo.ped.Controllers;
import it.sitgeo.ped.Entities.*;
import it.sitgeo.ped.Repositories.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.mail.MessagingException;
import java.io.IOException;
#Slf4j
#RestController
#RequestMapping("/sitgeo/PED")
#AllArgsConstructor
#CrossOrigin(origins = {"http://localhost","Y"} ,allowedHeaders = "*")
public class PEDController {
final EmailConfiguration emailConfiguration;
private final PraticaRepository praticaRepository;
private final ComuneRepository comuneRepository;
private final TitolareRepository titolareRepository;
private final PdfRepository pdfRepository;
private final ProcuratoreRepository procuratoreRepository;
private final DomicilioElettronicoRepository domicilioElettronicoRepository;
private final LocalizzazioneRepository localizzazioneRepository;
private final CatastoRepository catastoRepository;
private final TecnicoRepository tecnicoRepository;
private final SoggettiTitolariRepository soggettiTitolariRepository;
private final StatoUnitaRepository statoUnitaRepository;
private final TipologiaRepository tipologiaRepository;
//#CrossOrigin(origins = "http://localhost:4200")
#GetMapping("/getCOMUNI/{provincia}")
public ResponseEntity<?> getComuni(#PathVariable String provincia) {
log.info("getCOMUNI {}", provincia);
ResponseEntity<?> response;
try {
response = new ResponseEntity<String[]>(comuneRepository.searchComuneByProvincia(provincia),
HttpStatus.OK);
} catch (Exception e) {
response = new ResponseEntity<String[]>(HttpStatus.BAD_REQUEST);
}
System.out.println(response.getBody());
return response;
}
PED MODULE
package it.sitgeo.ped.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class CORSConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("Y", "http://localhost")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD","OPTIONS")
.allowedHeaders("*");
}
}
I've tried this http method using postman and it works as intended returning a JSON.

How to create NTLM Authentication Using spring

I am using NTLM authentication for my service. How to create the NTLM authentication in my API service call can anyone help with that? I need the complete coding for NTLM authentication
We use the following code to work with NTLM in production. As you can see it checks whether configuration is correct by sending simple HTTP GET.
package xxx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.*;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.auth.NTLMSchemeFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.transport.WebServiceMessageSender;
import org.springframework.ws.transport.http.HttpComponentsMessageSender;
import java.util.Arrays;
#Configuration
public class Configuration {
#Bean
public WebServiceMessageSender messageSender(
#Autowired final Credentials credentials,
#Autowired final HttpUriRequest handshake,
#Value("${service.timeout}") final int timeout
) {
HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender();
CredentialsProvider credentialsProvider;
Registry<AuthSchemeProvider> registry;
RequestConfig requestConfig;
credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, credentials);
registry = RegistryBuilder.<AuthSchemeProvider> create()
.register(AuthSchemes.NTLM, new NTLMSchemeFactory())
.build();
HttpRequestInterceptor interceptor =
(request, context) -> request.removeHeaders(HttpHeaders.CONTENT_LENGTH);
requestConfig = RequestConfig.custom()
.setConnectTimeout(timeout)
.build();
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setDefaultAuthSchemeRegistry(registry)
.setDefaultCredentialsProvider(credentialsProvider)
.addInterceptorFirst(interceptor)
.build();
try {
CloseableHttpResponse r = httpClient.execute(handshake);
if (log.isInfoEnabled()) {
log.info("Handshake initiated, response headers: {}",
Arrays.toString(r.getAllHeaders())
);
}
} catch (Exception e) {
log.error("Could not execute HTTP handshake request (method = {})",
handshake.getMethod(), e
);
}
messageSender.setHttpClient(httpClient);
return messageSender;
}
#Bean
public Credentials credentials(
#Value("${service.auth.username}") String user,
#Value("${service.auth.password}") String pass,
#Value("${service.auth.workstation}") String workstation,
#Value("${service.auth.domain}") String domain
) {
return new org.apache.http.auth.NTCredentials(user, pass, workstation, domain);
}
#Bean
public HttpUriRequest handshake(#Value("${service.uri}") final String uri) {
return new HttpGet(uri);
}
}
application.properties looks like this:
service.uri=http://somehost/somepath/SomeService.svc
service.action=http://somehost1/somepath1
service.timeout=3000
service.auth.username=someuser
service.auth.password=somepassword
service.auth.domain=somedomain
service.auth.workstation=anything

Jackson: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token

I wrote a micro-service to make a HTTP call to an API. Code is as given below.
Connector Application
package com.ajay.dashboard.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
#SpringBootApplication
public class DellDashboardConnectorApplication {
public static void main(String[] args) {
SpringApplication.run(DellDashboardConnectorApplication.class, args);
}
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
Connector COntroller
package com.ajay.dashboard.service.controller;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.http.client.utils.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/*
* Created by Kulkaa
*/
#RestController
public class DellDashboardController {
private static final Logger logger = LoggerFactory.getLogger(DellDashboardController.class);
#CrossOrigin(origins = "http://localhost:8080")
#RequestMapping(method = RequestMethod.GET, value = "/incident", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> retrieveAllCircles(HttpServletRequest request) throws UnsupportedEncodingException {
logger.info("DellDashboardController -> retrieveAllIncidents : invoked.");
RestTemplate restTemplate =new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);
String formUrl = "api";
final String sysparm_query = "incident_stateNOT%20IN6%2C7%5Eassignment_group%3D4122c7f8f09cc1002283ac3a043ae3e6";
final String sysparm_display_value = "true";
final String sysparm_exclude_reference_link = "true";
try {
URIBuilder builder = new URIBuilder(formUrl);
builder.addParameter("sysparm_query", sysparm_query);
builder.addParameter("sysparm_display_value", sysparm_display_value);
builder.addParameter("sysparm_exclude_reference_link", sysparm_exclude_reference_link);
String actualUrl = builder.toString();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic U2VydmljZV9Nb2JpbGVSZXBvcnRpbmc6U2VydmljZV9Nb2JpbGVSZXBvcnRpbmc=");
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
return restTemplate.exchange(actualUrl, HttpMethod.GET, entity, String.class);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return retrieveAllCircles(request);
}
}
When I build it by using mvn clean install, it runs perfectly. However, when I run it as SpringBoot app, I get below error:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.lang.String out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 1]
Do I need to deserialize it by using POJO class?
JSON being mapped is in the format:
{
"result": [{
data here
}]
}
Is it a json object?
Try creating a null entity. This might work
HttpEntity<String> entity = new HttpEntity<String>(null,headers);
The JSON content is invalid, so the parser breaks. So this would induce an empty content.

Spring-boot: Apply Servlet Filter to all routes except one

A question for spring-boot gurus!
Use Case:
I want my application to behave differently depending on following routes:
/ no authentication, no authorization
/render authorization via a json web token (jwt) sent as an URL parameter (I know, it's weird)
any other routes: authorization via a json web token (jwt) sent as an URL parameter (I know, it's weird)
The secret for the jwt is stored as an element of the application configuration (application.yaml) (I'm aware that this is not best practice, it's a demo app so I don't care)
I'm using SpringBoot 2.0.5 and io.jsonwebtoken as the jwt library.
I've implemented it using a Servlet Filter, and it is working, but it feels really ugly. I couldn't find a way to say 'Apply this Servlet Filter to all endpoints except this list'. I've resorted to including the logic within the doFilter method, but this seems really ugly.
Is there a 'best practise' for this?
My current code is as follows:
SecurityConfiguration
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/render").permitAll()
.anyRequest().authenticated();
httpSecurity.headers().frameOptions().disable();
}
}
WebConfigurer
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Configuration;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.util.EnumSet;
#Configuration
public class WebConfigurer implements ServletContextInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
initFilter(servletContext, disps);
}
private void initFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
FilterRegistration.Dynamic myFilter =
servletContext.addFilter("myFilter",
new JWTAuthenticationFilter());
myFilter.addMappingForUrlPatterns(disps, true, "/app/*");
myFilter.setAsyncSupported(true);
}
}
JWTAuthenticationFilter
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.impl.TextCodec;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
public class JWTAuthenticationFilter extends GenericFilterBean {
#Value("${security.jwt.token.secret-key}")
private String secret;
#Override
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
String path = request.getRequestURI();
if (!path.equals("/")) {
String jwsString = request.getParameter("jwt");
Jws<Claims> jws;
String base64_encoded_secret = TextCodec.BASE64.encode(secret);
jws = Jwts.parser()
.setSigningKey(base64_encoded_secret)
.parseClaimsJws(jwsString);
}
} catch (Exception e) {
System.out.println(e);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
SecurityContextHolder.clearContext();
}
filterChain.doFilter(request, response);
}
}
Found the solution! I used a FilterRegistrationBean. There is no way to exclude URLs. My solution is to put all the app under the app/ directory, so I didn't need to put a filter on the root /.
#Bean
public FilterRegistrationBean FilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(1);
registration.addUrlPatterns("/app/*");
return registration;
}

Spring #Autowired restTemplate is null [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 4 years ago.
I am new to Spring. I develop Service that Consuming RESTful service with certficate using Java
Here is my Config class:
package configuration;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.ResourceUtils;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import java.util.function.Supplier;
#Configuration
public class RestClientCertConfig {
private char[] allPassword = "allpassword".toCharArray();
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {
SSLContext sslContext = SSLContextBuilder
.create()
.loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.jks"), allPassword, allPassword)
.loadTrustMaterial(ResourceUtils.getFile("classpath:truststore.jks"), allPassword)
.build();
HttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.build();
return builder
.requestFactory((Supplier<ClientHttpRequestFactory>)new HttpComponentsClientHttpRequestFactory(client))
.build();
}
}
And here is the class where I consume Restful EndPoint:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.net.URISyntaxException;
import java.util.Collections;
public class ECSConfigGet {
private static final String ECS_API_URI = "<RestEndPointToConsume";
#Autowired
private static RestTemplate restTemplate;
public static void main(String[] args) {
try {
makeECSCall("myTestHeaderValue");
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
private static void makeECSCall(String entityCode) throws RestClientException, URISyntaxException {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("entityCode", entityCode);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
ResponseEntity responseEntity = restTemplate.exchange(ECS_API_URI, HttpMethod.GET, entity, String.class);
}
}
Did I completely misunderstood the concept? I would expect restTemplate would not be null with all the Annotations I use. Thank for any help!
NullPointerException is fixed. ECSConfigGet looks like:
package main;
import configuration.RestClientCertConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import services.modelsdto.ExpenseConfigDTO;
import java.util.Collections;
#SpringBootApplication
#Component
public class ECSConfigGet implements CommandLineRunner{
//API to call
private static final String ECS_API_URI = "<API_TO_CONSUME>";
#Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(RestClientCertConfig.class);
applicationContext.getBean(RestTemplate.class);
SpringApplication.run(ECSConfigGet.class, args);
}
private void makeECSCall(String entityCode) throws RestClientException {
ExpenseConfigDTO expenseConfigDTO = new ExpenseConfigDTO();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("entityCode", entityCode);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
ResponseEntity responseEntity = restTemplate.exchange(ECS_API_URI, HttpMethod.GET, entity, String.class);
}
#Override
public void run(String... args) {
for (int i = 0; i < args.length; ++i) {
makeECSCall("myTestHeaderValue");
}
}
}
You're missing a bit of Spring boilerplate that you need to make #Autowired work. If you're using Spring Boot, you're close, but #Patrick is right generally: ECSConfigGet needs to be a bean by annotating it correctly, but you also need to run your application within an application context in order for any of the Spring magic to happen. I suggest checking out this tutorial on how to use Spring Boot in a command line application.
The high level is ECSConfigGet needs to be annotated with #SpringBootApplication and then have it implement CommandLineRunner and then from the run method, you will have access to the #Autowired component. Spring will instantiate ECSConfigGet and populate the properties. Also as #Roddy pointed out, RestTemplate cannot be static, either.
The ECSConfigGet class is not a bean so it can not autowire a component.
Add #Component as class annotation to ECSConfigGet

Categories