I have two service connected to a registry, one of them need to query some data from the other, the token need to be passed to the endpoint.
I Have tried the following but it didn't work, the endpoint act as if no token is provided.
#GetMapping("/api/users/find")
#Headers("Authorization: Bearer {token}")
Optional<UserDTO> findUserByEmail(
#Param("token") String token, #RequestParam("email") String email);
#GetMapping("/api/users/find")
Optional<UserDTO> findUserByEmail(
#RequestHeaders("Authorization") String token, #RequestParam("email") String email);
#GetMapping("/api/users/find")
Optional<UserDTO> findUserByEmail(
#HeaderMap Map<String, Object> headers , #RequestParam("email") String email);
Should work lke this #RequestHeader(value = "Authorization") String authorization, but make sure you pass the right value, must be something like Bearer token.
Your this code is absolutely correct.
#GetMapping("/api/users/find")
Optional<UserDTO> findUserByEmail(
#RequestHeaders("Authorization") String token, #RequestParam("email") String email);
Just when you are calling this particular method add "Bearer " in front of the value of the token
token = "Bearer " + token;
findUserByEmail(token,email);
Create Header like this and pass to your feign client
private HttpHeaders getHeaders(final HttpServletRequest httpServletRequest) {
final HttpHeaders headers = new HttpHeaders();
headers.add("authorization", httpServletRequest.getHeader("authorization"));
return headers;
Example 1
Or very simple add intercepter
#Component
public class AuthFeignInterceptor implements RequestInterceptor {
#Override
public void apply(RequestTemplate template) {
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
final HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
template.header(HttpHeaders.AUTHORIZATION, httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION));
}
}
}
Example 2
Related
I want to get the oath2 access token and using this I want to call an another service.
Below code does the same it gets the access token and call an another API using that. Using the below code I am able to do what ever I want with the below code.
But I am new to Spring Security I just want to know if there is a better way to do this. Like rather than making a separate call to get the token and then call the service can i do it in a single call? Or Using any other class provided by Spring can I write this in a better way ?
public class TestAPIToken{
#RequestMapping(value = "/showEmployees", method = RequestMethod.GET)
public ModelAndView showEmployees(#RequestParam("code") String code) throws JsonProcessingException, IOException {
String accessToken = getAccessToken();
System.out.println("API Token ---------" + accessToken);
HttpEntity<String> response = getResponseByCallingWithToken(accessToken);
System.out.println("API Response ---------" + response.getBody());
return null;
}
private HttpEntity<String> getResponseByCallingWithToken(String accessToken) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Bearer " + accessToken);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("msisdn", msisdn)
.queryParam("email", email);
HttpEntity<?> entity = new HttpEntity<>(headers);
HttpEntity<String> response = restTemplate.exchange(
builder.toUriString(),
HttpMethod.GET,
entity,
String.class);
reponse.getBody();
return response;
}
private String getAccessToken() {
ResponseEntity<String> response = null;
System.out.println("Authorization Code------" + code);
RestTemplate restTemplate = new RestTemplate();
// According OAuth documentation we need to send the client id and secret key in the header for authentication
String encodedCredentials = new String(Base64.encodeBase64(credentials.getBytes()));
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Basic " + encodedCredentials);
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("scope","scope,value");
body.add("grant_type","scope,value");
HttpEntity<String> request = new HttpEntity<String>(body, headers);
String access_token_url = "http://localhost:8080/oauth2/token";
ResponseEntity<TokenModel> response = restTemplate.exchange(access_token_url, HttpMethod.POST, request, TokenModel.class);
String accessToken = response.getBody().access_token;
return accessToken;
}
}
class TokenModel{
String access_token;
String scope;
String token_type;
String expires_in;
}
I am new to Spring security. Please help even if this seems simple to you
NB: This question does not have an exact duplicate
I am working on an assignment, where I have to implement OAuth Client, which will make request to auth server to get access token.
I have implemented it through RestTemplate. Since, I have got some information about the authserver like uri, grant-type, client secret. on the basis of these information I have created a request which returns me proper access token.
But now, requirement is to add scopes in request to auth server. can someone help me where this scopes can be added in request as per below code. ? I have tried to add this in header & map. but i get the bad request error.
class AuthTokenManager {
private ResponseEntity<String> requestToAuthServer(AuthServerInfo authServerInfo) {
//prepare request
final HttpEntity<MultiValueMap<String, String>> request =
new HttpEntity<>(prepareOAuthRequestBody(authServerInfo), prepareOAuthRequestHeader(authServerInfo));
// requesting auth-server for token
final ResponseEntity<String> response =
restTemplate.postForEntity(
authServerInfo.getOAuthAccessTokenUri(),
request,
String.class
);
return response;
}
private MultiValueMap<String, String> prepareOAuthRequestBody(AuthServerInfo authServerInfo) {
//prepare request body
final MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add(GRANT_TYPE, authServerInfo.getOAuthGrantType());
map.add(CLIENT_ID, authServerInfo.getOAuthClientId());
// map.add(SCOPE, authServerInfo.getOAuthScopes());
return map;
}
private String prepareEncodedCredentials(AuthServerInfo authServerInfo) {
//prepare authorization attribute for header.
final String credentials = authServerInfo.getOAuthClientId() + ":" +
authServerInfo.getOAuthClientSecret();
return StringUtils.str(Base64.encodeBase64(StringUtils.bytes(credentials)));
}
private HttpHeaders prepareOAuthRequestHeader(AuthServerInfo authServerInfo) {
// prepare request header
final HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.add(AUTHORIZATION, BASIC + prepareEncodedCredentials(authServerInfo));
headers.add(HttpHeaders.CONTENT_TYPE, URLENCODED);
headers.setCacheControl(CACHE_CONTROL);
return headers;
}
}
In my spring boot Application i have a scheduler which calls an API to generate token which expires in 15 min. Time of scheduler is also 15 min. please find below sample:
public class TokenGeneration {
private static String token = null;
#Scheduled(15 minutes)
public String fetchToken() {
// api call which return token
HttpEntity<model> response = restTemplate.exchange(uri, POST, entity, model.class);
token = response.getBody().getAccessToken();
}
}
I stored token value in static variable from a non static method so that i can use this token variable wherever i want to use token value. is this right approach ? if not plz let me know how i can achieve this.
Do i need to make TokenGeneration class singleton so that only one instance of this class is made throught application?
Also i want to create an interceptor or filter in which i can set Authorization headers and token value so that each request will populate authorization header automatically, i don't want to set authorization header in each request like this :
HttpHeaders headers = new HttpHeaders();
headers.set(CpsConstant.AUTHORIZATION, CpsConstant.BEARER + token);
So i tried with this custom interceptor :
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor{
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String token = TokenGeneration.token;
request.getHeaders().add("Authorization", "Bearer " + token);
return execution.execute(request, body);
}
will add this interceptor in restTemplate in config file.
So is this right approach for both token generation as well as setting headers for each request or any improvements need to be done in this approach ?
Me thinking of calling token generation method in interceptor in case of token is null like :
if(token == null){
//call token generation fetchToken method
}
It is the right approach
Spring default scope is always singleton if not specified
It is ok to use interceptor, but what if you want to call a API without a token?
Best approach to use two separate methods to send request with token and without token using a separate class
#Component
public class RestClient {
#Autowired
RestTemplate restTemplate;
public HttpHeaders getRequestHeaderBearer() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER +
TokenGeneration.token);
return headers;
}
public HttpHeaders getRequestHeader() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
public <T> ResponseEntity<T> restExchangeBearer(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeaderBearer()), classObj);
}
public <T> ResponseEntity<T> restExchange(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeader()), classObj);
}
}
I am completely new in RestTemplate and basically in the REST APIs also. I want to retrieve some data in my application via Jira REST API, but getting back 401 Unauthorised. Found and article on jira rest api documentation but don't really know how to rewrite this into java as the example uses the command line way with curl. I would appreciate any suggestion or advice how to rewrite:
curl -D- -X GET -H "Authorization: Basic ZnJlZDpmcmVk" -H "Content-Type: application/json" "http://kelpie9:8081/rest/api/2/issue/QA-31"
into java using spring rest template. Where the ZnJlZDpmcmVk is a base64 encoded string of username:password. Thank you very much.
Taken from the example on this site, I think this would be the most natural way of doing it, by filling in the header value and passing the header to the template.
This is to fill in the header Authorization:
String plainCreds = "willie:p#ssword";
byte[] plainCredsBytes = plainCreds.getBytes();
byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
String base64Creds = new String(base64CredsBytes);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Creds);
And this is to pass the header to the REST template:
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<Account> response = restTemplate.exchange(url, HttpMethod.GET, request, Account.class);
Account account = response.getBody();
You may use spring-boot RestTemplateBuilder
#Bean
RestOperations rest(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.basicAuthentication("user", "password").build();
}
See documentation
(before SB 2.1.0 it was #basicAuthorization)
There are multiple ways to add the basic HTTP authentication to the RestTemplate.
1. For a single request
try {
// request url
String url = "https://jsonplaceholder.typicode.com/posts";
// create auth credentials
String authStr = "username:password";
String base64Creds = Base64.getEncoder().encodeToString(authStr.getBytes());
// create headers
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Creds);
// create request
HttpEntity request = new HttpEntity(headers);
// make a request
ResponseEntity<String> response = new RestTemplate().exchange(url, HttpMethod.GET, request, String.class);
// get JSON response
String json = response.getBody();
} catch (Exception ex) {
ex.printStackTrace();
}
If you are using Spring 5.1 or higher, it is no longer required to manually set the authorization header. Use headers.setBasicAuth() method instead:
// create headers
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("username", "password");
2. For a group of requests
#Service
public class RestService {
private final RestTemplate restTemplate;
public RestService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder
.basicAuthentication("username", "password")
.build();
}
// use `restTemplate` instance here
}
3. For each and every request
#Bean
RestOperations restTemplateBuilder(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.basicAuthentication("username", "password").build();
}
I hope it helps!
As of Spring 5.1 you can use HttpHeaders.setBasicAuth
Create Basic Authorization header:
String username = "willie";
String password = ":p#ssword";
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(username, password);
...other headers goes here...
Pass the headers to the RestTemplate:
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<Account> response = restTemplate.exchange(url, HttpMethod.GET, request, Account.class);
Account account = response.getBody();
Documentation:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpHeaders.html#setBasicAuth-java.lang.String-java.lang.String-
(maybe) the easiest way without importing spring-boot.
restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor("user", "password"));
Reference Spring Boot's TestRestTemplate implementation as follows:
https://github.com/spring-projects/spring-boot/blob/v1.2.2.RELEASE/spring-boot/src/main/java/org/springframework/boot/test/TestRestTemplate.java
Especially, see the addAuthentication() method as follows:
private void addAuthentication(String username, String password) {
if (username == null) {
return;
}
List<ClientHttpRequestInterceptor> interceptors = Collections
.<ClientHttpRequestInterceptor> singletonList(new BasicAuthorizationInterceptor(
username, password));
setRequestFactory(new InterceptingClientHttpRequestFactory(getRequestFactory(),
interceptors));
}
Similarly, you can make your own RestTemplate easily
by inheritance like TestRestTemplate as follows:
https://github.com/izeye/samples-spring-boot-branches/blob/rest-and-actuator-with-security/src/main/java/samples/springboot/util/BasicAuthRestTemplate.java
Instead of instantiating as follows:
TestRestTemplate restTemplate = new TestRestTemplate();
Just do it like this:
TestRestTemplate restTemplate = new TestRestTemplate(user, password);
It works for me, I hope it helps!
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(username, password);
then continue with the same procedure mentioned by the others here:
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET,
request, String.class);
Use setBasicAuth to define credentials
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("myUsername", myPassword);
Then create the request like you prefer.
Example:
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET,
request, String.class);
String body = response.getBody();
I'm using spring version 5.3.15 for my unit test environment. I used withBasicAuth for my tests :
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyTestClass {
...
#Autowired
private TestRestTemplate restTemplate;
...
#Test
#SneakyThrows
public void TestGetSettings(){
DtoClass dtoClass = this.restTemplate
.withBasicAuth(UserServices.DEFAULT_USER, UserServices.DEFAULT_PASSWORD)
.getForObject(String.format("http://localhost:%d/setting",
port), DtoClass.class);
assertThat(dtoClass.getClientAddress()).isNotEmpty();
}
...
}
As you see this method only work for basic authentication. If you look at the details of the withBasicAuth method, you will find that the method source will be like this:
// TestRestTemplate.java file:
...
public class TestRestTemplate {
...
private final RestTemplateBuilder builder;
...
public TestRestTemplate withBasicAuth(String username, String password) {
TestRestTemplate template = new TestRestTemplate(this.builder, username, password, this.httpClientOptions);
...
}
}
As a result, for other types of authentication you can use the RestTemplateBuilder as a builder which is mentioned in other answers.
Follow Step By Step
I added Client Credentials In application.Properties file like below...
http.basicauth.username = yourUserName
http.basicauth.password = yourPassword
And , Then I created one class With two fields Because I'm loading those two fields from the Application.Properties file : username and password . Make sure your class is annotated with #Component..
#Value("${http.basicauth.username}")
private String username;
#Value("${http.basicauth.password}")
private String password;
And Then , You need to autowired above class From Wherever you want..
// I'm getting a username and password from application.properties file
String userCredentials = referenceClassName.getUsername()+":"+referenceClassName.getPassword();
// Encoded User Credentials and Convert it into a String
String encodedUserCredentials= Base64.getMimeEncoder().encodeToString(userCredentialsBytes.getBytes());
headers.set("Authorization", "Basic " +base64UserCredentials);
HttpEntity request = new HttpEntity(headers);
String url = "externalUrl";
// Getting a Json String body
String body = restTemplate.exchange(url,HttpMethod.GET,request,String.class).getBody();
Note :: For getting an Access Token from String Json body , That's why I converted it into a Json Object
JsonObject tokenJsonObject = new JsonParser().parse(body).getAsJsonObject();
// Getting access token as string from tokenJsonObject
String accessToken = tokenJsonObject.has("access_token") && !tokenJsonObject.get("access_token").isJsonNull() ? tokenJsonObject.get("access_token").getAsString() : "";
If you have any concerns, please let me know in comments..Hope It will helpful to you..
I have been using Spring above 5.1.x
// create headers for basic auth
var headers = new HttpHeaders();
headers.setBasicAuth("username", "password");
I have build a web application using spring mvc framework to publish REST services.
For example:
#Controller
#RequestMapping("/movie")
public class MovieController {
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public #ResponseBody Movie getMovie(#PathVariable String id, #RequestBody user) {
return dataProvider.getMovieById(user,id);
}
Now I need to deploy my application but I have the following problem:
The clients do not have direct access to the computer on which the application resides (There is a firewall). Therefore I need a redirection layer on a proxy machine (accessible by the clients) which calls the actual rest service.
I tried making a new call using RestTemplate:
For Example:
#Controller
#RequestMapping("/movieProxy")
public class MovieProxyController {
private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public #ResponseBody Movie getMovie(#PathVariable String id,#RequestBody user,final HttpServletResponse response,final HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = new RestTemplate();
return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), new HttpEntity<T>(user, headers), Movie.class);
}
This is ok but I need to rewrite each method in the controller to use the resttemplate. Also, this causes redundant serialization/deserialization on the proxy machine.
I tried writing a generic function using restemplate, but it did not work out:
#Controller
#RequestMapping("/movieProxy")
public class MovieProxyController {
private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";
#RequestMapping(value = "/**")
public ? redirect(final HttpServletResponse response,final HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = new RestTemplate();
return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), ? , ?);
}
I could not find a method of resttemplate which works with request and response objects.
I also tried spring redirect and forward. But redirect does not change the request's client ip address so i think it is useless in this case. I could not forward to another URL either.
Is there a more appropriate way to achieve this?
You can mirror/proxy all requests with this:
private String server = "localhost";
private int port = 8080;
#RequestMapping("/**")
#ResponseBody
public String mirrorRest(#RequestBody String body, HttpMethod method, HttpServletRequest request) throws URISyntaxException
{
URI uri = new URI("http", null, server, port, request.getRequestURI(), request.getQueryString(), null);
ResponseEntity<String> responseEntity =
restTemplate.exchange(uri, method, new HttpEntity<String>(body), String.class);
return responseEntity.getBody();
}
This will not mirror any headers.
Here's my modified version of the original answer, which differs in four points:
It does not make the request body mandatory, and as such does not let GET requests fail.
It copies all headers present in the original request. If you are using another proxy/web server, this can cause issues due to content length/gzip compression. Limit the headers to the ones you really need.
It does not reencode the query params or the path. We expect them to be encoded anyway. Note that other parts of your URL might also be encoded. If that is the case for you, leverage the full potential of UriComponentsBuilder.
It does return error codes from the server properly.
#RequestMapping("/**")
public ResponseEntity mirrorRest(#RequestBody(required = false) String body,
HttpMethod method, HttpServletRequest request, HttpServletResponse response)
throws URISyntaxException {
String requestUrl = request.getRequestURI();
URI uri = new URI("http", null, server, port, null, null, null);
uri = UriComponentsBuilder.fromUri(uri)
.path(requestUrl)
.query(request.getQueryString())
.build(true).toUri();
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.set(headerName, request.getHeader(headerName));
}
HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
try {
return restTemplate.exchange(uri, method, httpEntity, String.class);
} catch(HttpStatusCodeException e) {
return ResponseEntity.status(e.getRawStatusCode())
.headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
}
You can use Netflix Zuul to route requests coming to a spring application to another spring application.
Let's say you have two application: 1.songs-app, 2.api-gateway
In the api-gateway application, first add the zuul dependecy, then you can simply define your routing rule in application.yml as follows:
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>LATEST</version>
</dependency>
application.yml
server:
port: 8080
zuul:
routes:
foos:
path: /api/songs/**
url: http://localhost:8081/songs/
and lastly run the api-gateway application like:
#EnableZuulProxy
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Now, the gateway will route all the /api/songs/ requests to http://localhost:8081/songs/.
A working example is here: https://github.com/muatik/spring-playground/tree/master/spring-api-gateway
Another resource: http://www.baeldung.com/spring-rest-with-zuul-proxy
#derkoe has posted a great answer that helped me a lot!
Trying this in 2021, I was able to improve on it a little:
You don't need #ResponseBody if your class is a #RestController
#RequestBody(required = false) allows for requests without a body (e.g. GET)
https and port 443 for those ssl encrypted endpoints (if your server serves https on port 443)
If you return the entire responseEntity instead of only the body, you also get the headers and response code.
Example of added (optional) headers, e.g. headers.put("Authorization", Arrays.asList(String[] { "Bearer 234asdf234"})
Exception handling (catches and forwards HttpStatuses like 404 instead of throwing a 500 Server Error)
private String server = "localhost";
private int port = 443;
#Autowired
MultiValueMap<String, String> headers;
#Autowired
RestTemplate restTemplate;
#RequestMapping("/**")
public ResponseEntity<String> mirrorRest(#RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request) throws URISyntaxException
{
URI uri = new URI("https", null, server, port, request.getRequestURI(), request.getQueryString(), null);
HttpEntity<String> entity = new HttpEntity<>(body, headers);
try {
ResponseEntity<String> responseEntity =
restTemplate.exchange(uri, method, entity, String.class);
return responseEntity;
} catch (HttpClientErrorException ex) {
return ResponseEntity
.status(ex.getStatusCode())
.headers(ex.getResponseHeaders())
.body(ex.getResponseBodyAsString());
}
return responseEntity;
}
proxy controller with oauth2
#RequestMapping("v9")
#RestController
#EnableConfigurationProperties
public class ProxyRestController {
Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;
#Autowired
private ClientCredentialsResourceDetails clientCredentialsResourceDetails;
#Autowired
OAuth2RestTemplate oAuth2RestTemplate;
#Value("${gateway.url:http://gateway/}")
String gatewayUrl;
#RequestMapping(value = "/proxy/**")
public String proxy(#RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request, HttpServletResponse response,
#RequestHeader HttpHeaders headers) throws ServletException, IOException, URISyntaxException {
body = body == null ? "" : body;
String path = request.getRequestURI();
String query = request.getQueryString();
path = path.replaceAll(".*/v9/proxy", "");
StringBuffer urlBuilder = new StringBuffer(gatewayUrl);
if (path != null) {
urlBuilder.append(path);
}
if (query != null) {
urlBuilder.append('?');
urlBuilder.append(query);
}
URI url = new URI(urlBuilder.toString());
if (logger.isInfoEnabled()) {
logger.info("url: {} ", url);
logger.info("method: {} ", method);
logger.info("body: {} ", body);
logger.info("headers: {} ", headers);
}
ResponseEntity<String> responseEntity
= oAuth2RestTemplate.exchange(url, method, new HttpEntity<String>(body, headers), String.class);
return responseEntity.getBody();
}
#Bean
#ConfigurationProperties("security.oauth2.client")
#ConditionalOnMissingBean(ClientCredentialsResourceDetails.class)
public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
return new ClientCredentialsResourceDetails();
}
#Bean
#ConditionalOnMissingBean
public OAuth2RestTemplate oAuth2RestTemplate() {
return new OAuth2RestTemplate(clientCredentialsResourceDetails);
}
If you can get away with using a lower-level solution like mod_proxy that would be the simpler way to go, but if you need more control (e.g. security, translation, business logic) you may want to take a look at Apache Camel: http://camel.apache.org/how-to-use-camel-as-a-http-proxy-between-a-client-and-server.html
I got inspired by Veluria's solution, but I had issues with gzip compression sent from the target resource.
The goal was to omit Accept-Encoding header:
#RequestMapping("/**")
public ResponseEntity mirrorRest(#RequestBody(required = false) String body,
HttpMethod method, HttpServletRequest request, HttpServletResponse response)
throws URISyntaxException {
String requestUrl = request.getRequestURI();
URI uri = new URI("http", null, server, port, null, null, null);
uri = UriComponentsBuilder.fromUri(uri)
.path(requestUrl)
.query(request.getQueryString())
.build(true).toUri();
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (!headerName.equals("Accept-Encoding")) {
headers.set(headerName, request.getHeader(headerName));
}
}
HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
try {
return restTemplate.exchange(uri, method, httpEntity, String.class);
} catch(HttpStatusCodeException e) {
return ResponseEntity.status(e.getRawStatusCode())
.headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
}
You need something like jetty transparent proxy, which actually will redirect your call, and you get a chance to overwrite the request if you needed. You may get its detail at http://reanimatter.com/2016/01/25/embedded-jetty-as-http-proxy/