Routing - Spring Boot Web App & AWS Application Load Balancer - spring

I have a Spring Boot web app with the usual routings:
#GetMapping("/account")
public String search(#RequestParam(value="search"...
I've now deployed this behind an AWS Application Load Balancer, where I have used path based routing to target my app.
I've configured /admin/* on the Load Balancer to forward to my app, which works fine.
The issue though is that my app is now seeing /admin/account, rather than the /account it is expecting.
AWS says:
Note that the path pattern is used to route requests but does not
alter them. For example, if a rule has a path pattern of /img/*, the
rule would forward a request for /img/picture.jpg to the specified
target group as a request for /img/picture.jpg.
Is there any tidy way around this? Or do I have to resort to something like this:
#GetMapping("*/account")

I don't think that functionality exists in AWS Applicxation loadbalancer -- check this out stackoverflow.com/questions/39317685
Quoting the relevant part here
you can use a reverse proxy on your server and do something like this
(this is Nginx configuration):
server {
listen 80 default_server;
location /secured/ {
proxy_pass http://localhost:{service_port}/;
}
}
This will strip the /admin part and proxy everything else to your
service. Just be sure to have the trailing / after the service port.

The only thing I can think of doing (besides web server url rewrites or a reverse proxy like Zuul) is to do the url rewrites within your app.
An easy way to do that is using UrlRewriteFilter
#Bean
public FilterRegistrationBean urlRewriteFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new UrlRewriteFilter());
// You probably want to set the order very low so it fires before
// other filters, especially if you're using Spring Security
filterRegistration.setOrder(Integer.MIN_VALUE);
filterRegistration.setUrlPatterns(Collections.singletonList("/*"));
Map<String, String> initParams = new HashMap<>();
initParams.put("confPath", "url_rewrite.xml");
filterRegistration.setInitParameters(initParams);
return filterRegistration;
}
The maven coordinates for the filter:
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.3</version>
</dependency>
A configuration file example can be found here: http://cdn.rawgit.com/paultuckey/urlrewritefilter/master/src/doc/manual/4.0/urlrewrite.xml

Related

Spring cloud gateway proxy to controller in same application

I want to achieve the following with a spring boot webflux application:
I have an endpoint api/test. I would like the same controller to be available on dynamically configured sub paths. E.g. if configured a sub-route "app" then a request to app/api/test should end up in the same controller.
To facilitate this I did the following using a RouteLocatorBuilder:
route(id = "proxy_api_test") {
host(location.host)
path("/${location.route}/api/test/**" )
filters{
filter(setPathGatewayFilter.apply(createSetPathConfig("/api/test")))
}
uri(location.uri)
}
In case of testing on localhost for example location.host would be "localhost:8080" and location.route could be "app" and location.uri would be "http://localhost:8080".
And createSetPathConfig is given as:
fun createSetPathConfig(template: String): SetPathGatewayFilterFactory.Config{
val config = SetPathGatewayFilterFactory.Config()
config.template = template
return config
}
When running the application that would work like a charm because requests to http://localhost:8080/app/api/test would be redirected to http://localhost:8080/api/test with the help of spring cloud gateway. I have chose this approach also because it could be various sub paths at the same time, so the same controller must be available from different entry paths.
Now what I see is that this does not work in unit-tests using an #Autowired val client: WebTestClient because when executing the unit-test in fact no web-server is running. Is there a way to indicate with the uri in with RouteLocatorBuilder that the request should be executed on the same host such that unit-test would also work with the same logic? Because in fact in this case I would like spring cloud gateway not to forward to another host but to just change the routes dynamically.

Created RestHighLevelClient for Elasticsearch won't use SSL

I am trying to connect to an existing Elasticsearch instance. The instance is set up and working fine, which I can confirm by accessing it through the browser under https://localhost:9200. I am using Spring Data Elasticsearch version 4.1.2 and I am following the official instructions from https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.rest.
Unfortunately, when starting the application, the initial health checks of the elasticsearch instance fail, because the code is trying to use http instead of https:
o.s.w.r.f.client.ExchangeFunctions : [377e90b0] HTTP HEAD http://localhost:9200/
Therefore I added the ".usingSsl()" to the ClientConfiguration, but it does not seem to have any effect and I just don't understand why. The "funny" thing is, if I implement the reactive client as described here (https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.reactive), https suddenly works and the applications starts perfectly fine. However, I want to use the regular high level client.
Can you help me change the configuration in a way so that it finally makes use of https instead of http?
#Configuration
class ElasticsearchClientConfig: AbstractElasticsearchConfiguration() {
#Bean
override fun elasticsearchClient(): RestHighLevelClient {
val clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200")
.usingSsl()
.build();
return RestClients.create(clientConfiguration).rest();
}
}

Handling of requests locally by Zuul Gateway server

How do I make Spring Boot Zuul Proxy Server handle a specific request locally, instead of proxying it to some other Server?
Let's say I want to do a custom health check of Zuul Proxy Server itself through an API which should return a local response, instead of returning a proxied response from some other remote server.
What kind of route configuration is required to forward the request?
Following is what I figured out and it worked for me:
1) Create a local request handler:
Add a regular Controller/RestController with a RequestMapping in a file in Zuul proxy Server code
#RestController
public class LocalRequestHandler {
#GetMapping(path = "/status", produces = MediaType.TEXT_PLAIN_VALUE);
private static ResponseEntity<String> getServiceStatus() {
String status = "I am alive";
return ResponseEntity.ok().body(status);
}
}
2) Create a local forwarding route in the configuration file (application.properties or as the case may be) where other routes are defined. In my case, Spring Boot Zuul Proxy server is running on Jetty container with GatewaySvc as the application context.
zuul.routes.gatewaysvc.path=/GatewaySvc/status
zuul.routes.gatewaysvc.url=forward:/GatewaySvc/status
For a standalone Spring Boot, which I have not tested, you may have to remove the context path from the above configuration.
zuul.routes.gatewaysvc.path=/status
zuul.routes.gatewaysvc.url=forward:/status
Reference:
Strangulation Patterns and Local Forwards

Spring Boot - Multiple context paths - have one redirect to the other?

We have a spring boot 2 app that is typically accessed behind a proxy server for all requests at, say, /blah/**.
We set up this app to use a context path called /blah to simplify this (so we no longer had to add /blah to all of our controller mappings, resource imports, etc)
server:
port: 9876
servlet:
context-path: /blah
This works fine, but there are some cases where people might access the app at root (/) that we'd like to handle.
It looks like I need to create a new servlet that listens to "/" and just redirects to "/blah"? What is the easiest way to set that up? I don't want any of my existing beans in this other context, just a simple controller or html file that performs a redirect.
This is what I think would be easiest way.
#RequestMapping("/*")
public void redirect(HttpServletResponse response) throws IOException{
response.sendRedirect("/blah");
}

Dynamic provider for a Marshalling web service outbound gateway

Is it possible to set a dynamic provider for a Marshalling web service outbound gateway?
I mean, if I try for example: http://100.0.0.1 and it not works, I would like to try http://100.0.0.2 instead
My current configuration:
MarshallingWebServiceOutboundGateway gw = new MarshallingWebServiceOutboundGateway(provider, jaxb2Marshaller(), jaxb2Marshaller());
Yes, that's true. Since MarshallingWebServiceOutboundGateway allows to inject DestinationProvider, you feel free to provide any custom implementation.
For your fault-tolerant use-case you should do: new URLConnection(url).connect() to test connection to the target server in that your DestinationProvider implementation.
UPDATE
But If I how can I test new URLConnection(url).connect() if I have https credentials, certificate or any kind of security
Well, another good solution from the Spring Integration is load-balancing and several subscribers to the same DirectChannel:
#Bean
public MessageChannel wsChannel() {
return new DirectChannel(null);
}
to switch of the default RoundRobinLoadBalancingStrategy.
And after that you can have several #ServiceActivator(inputChannel="wsChannel"). When the first one is fail, the message is sent to the second and so on, until the good result or the fall for each URL.

Resources