spring cloud gateway only forwards to base path of url - java

I'm trying to build a simple api-gateway using spring-cloud-gateway. So far I understood the basic principle but I'm running in a specific Problem:
The target url which I'm forwarding my request potentially contains zero, one or multiple path segments. Unfortunately, these path segments are ignored.
private final String routingTargetWithPath = "http://hapi.fhir.org/baseR4";
#Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("patient", r -> r
.path("/Patient", "/Patient/*")
.and()
.method(GET)
.uri(routingTargetWithPath)
)
.build();
}
Using curl sending the request to my api-gateway:
curl http://localhost:8080/Patient
and accordingly
curl http://localhost:8080/Patient/2069748
I would assume the requests would be routed to:
http://hapi.fhir.org/baseR4/Patient
and accordingly
http://hapi.fhir.org/baseR4/Patient/2069748
But instead they are being routed to:
http://hapi.fhir.org/Patient
and accordingly
http://hapi.fhir.org/Patient/2069748
So, the path of the configured routing url is ignored.
Unfortunately, I can't do a manual rewrite here, since in production the "routingTarget" will be configured and I don't know, if and how many path segments it will contain.
How can I achieve to route to the complete configured routing target?

Ok, I found the answer:
According to here it is intentional, that the path of the uri is ignored.
So in my case, the set path filter would fix the problem:
private final URI routingTargetWithPath = URI.create("http://hapi.fhir.org/baseR4");
#Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("patient", r -> r
.path("/Patient", "/Patient/*")
.and()
.method(GET)
.filters(f -> f.prefixPath(routingTargetWithPath.getPath()))
.uri(routingTargetWithPath)
)
.build();
}

Related

Spring cloud gateway routing with path

I'm trying to write a gateway routing with a path (route everything to www.example.com/foobar/), but the '/foobar/' part is ignored, and everything is routed to www.example.com/)
My RouteLocator configuration is:
#Bean
public RouteLocator myRouteLocator(final RouteLocatorBuilder builder) {
return builder.routes()
.route(route -> route.path("/**").uri("http://www.example.com/foobar"))
.build();
}
When I call the service with http://localhost:8080/myApiCall, cloud gateway forwards the call to http://www.example.com/myApiCall instead of http://www.example.com/foobar/myApiCall.
If I call my service as http://localhost:8080/foobar/myApiCall, the resulting call is http://www.example.com/foobar/myApiCall, so it works correctly in this case.
Based on some debugging, my final URL is created here:
https://github.com/spring-cloud/spring-cloud-gateway/blob/v3.1.3/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java#L88
Where only the host is used, the path is omitted from the configuration.
My used versions:
spring-cloud-gateway: v3.1.3
spring-core: v5.3.20
I have thought about just using a rewritepath filter to always append the /foobar/ part - but isn't there a better way?

why the spring gateway filter did not trigger

I define a filter in my spring gateway(2.2.8.RELEASE) project like this:
#Component
public class LogFilter2 extends AbstractGatewayFilterFactory {
#Override
public GatewayFilter apply(Object config) {
return (exchange,chain) -> {
System.out.println("LogFilter2 flitered!!!");
return chain.filter(exchange);
};
}
}
then config the filter in application.properties like this:
# dolphin music
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.routes[0].id=dolphin-music-service
# forward by ip:port way
spring.cloud.gateway.routes[0].uri=http://10.107.64.246:11014
# forward by service name way
# spring.cloud.gateway.routes[0].uri=lb://
spring.cloud.gateway.routes[0].predicates[0]=Path=/music/**
spring.cloud.gateway.routes[0].filters[0]=LogFilter2
but when I run the project and send a request to the url /music/xxxxxx, the request did not enter the filter.No log LogFilter2 flitered!!! output.what should I do to make the filter works as expect? I also tried many other way,this is the minimal demo:https://github.com/jiangxiaoqiang/java-learn. In this demo, I define different kind of gateway filter, no one work except the global gateway filter. I am struggle with this problem for days.

How to resolve CORS issue in Angular 12?

I am working on an Angular project and I have login page and during submitting login API, I am facing CORS error. I am attaching screenshot of it also. Any suggestion would be appreciated.
API Service.ts:
constructor( private http: HttpClient ) { }
httpOptionsPost = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Allow-Methods' : 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers' : 'Origin, Content-Type, Accept, X-Custom-Header, Upgrade-Insecure-Requests',
})
};
httpOptionsGet = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
})
};
_login(username, password) {
const url = this.domain + '/api/login?username='+username+'&password='+password;
return this.http.post(url, {}, this.httpOptionsPost);
}
Login Component:
this.apiService._login(data.username, data.password).subscribe((resp: any) => {
const resobj = JSON.parse(JSON.stringify(resp));
console.log(resobj);
})
You need to set up proxy configuration for running the app locally.
A short example is:
// create a file proxy.config.json
{
"/api": {
"target": "http://example.com/", // put your actual domain
"secure": false // true or false depending on your needs.
}
}
Then run your project passing the proxy conf to ng-serve or configure it in the package.json.
For instance:
ng serve --proxy-config proxy.conf.json
or
npm start -- --proxy-config proxy.conf.json
Or simply configure it in the angular.json.
Then, in your code, remove the usage of the domain, or assign the domain to localhost using environment.dev.ts if the domain is not the same as where you are going to deploy you app.
I'm referring to the domain variable in this block.
_login(username, password) {
const url = this.domain + '/api/login?username='+username+'&password='+password;
return this.http.post(url, {}, this.httpOptionsPost);
}
More details in https://www.positronx.io/setting-up-angular-proxy-configuration-via-angular-json/ and / or in the official webpack site for advanced proxy configurations
Cors preflight should be handled at server side code. In your case try adding below annotation onto your controller class.
#CrossOrigin("*")
#PostMapping("/user")
public User userController(){
}
And hope you have a configuration class for handling the CORS, if not try adding one. Sample code for reference.
#Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
List<String> allowedMethods=new ArrayList<>();
allowedMethods.add("GET");
allowedMethods.add("POST");
allowedMethods.add("PUT");
allowedMethods.add("DELETE");
CorsConfiguration cors=new CorsConfiguration();
cors.setAllowedMethods(allowedMethods);
http.cors().configurationSource(request -> cors.applyPermitDefaultValues());
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().requestMatchers(EndpointRequest.to("loggers")).hasRole("ADMIN").and().httpBasic();
}}
in your question , the pc_coder answer is right:
cors is related with backend. Be sure you allowed from api to reach requests
like the link:
How does Access-Control-Allow-Origin header work?
I'm not quite familiar with Angular. However, I had the same CORS issue with Vue.js and the solution is to change the add these lines :
module.exports = {
devServer: {
proxy: 'https://yourdomain'
}
}
and also replace the https://yourdomain/login to localhost when you do .get or .post, for example : 'http://localhost:8082/login'
I'm not sure if this would work for you since I am also very new at this, but it did work for me.
Before browser makes a call to your API URL from the angular client application, it will send a preflight request (An HTTP OPTIONS call). this will happen only when your client side application and server side applications are residing in different sub domains or domains. It is the responsibility of the server side application to control whether the cross origin request should be served or not. I see that you are setting the CORS headers in your request header. but it should be received from the API server. so remove that from the angular end and make the proper changes in the server side code.
By default, the Same-Origin-Policy (SOP) forbids request to a different resource. Everything should be loaded from the same source. This is to prevent websites to load data from different servers without the users knowledge. However, there are circumstances where this is required. To cover such case and still provide high safety measurements, CORS was created.
With CORS it is possible to send requests to a server different than the original one.
For this the webserver which is receiving the request must be configured accordingly. In your case this means, you have to adjust the CORS settings in the application which is providing the login API you are calling and allow localhost:4200 to be able to send CORS requests.

How to make endpoints accept only #crossorigin enabled uris in springboot while accessing using resttemplate? Why am i not getting cors error? [duplicate]

This question already has answers here:
Will CORS policy prevent resource access from non-browser requests?
(4 answers)
Why isn't my CORS configuration causing the server to filter incoming requests? How can I make the server only accept requests from a specific origin?
(1 answer)
Closed 3 years ago.
I have two projects A and B. Both run on different ports A(8080) B(8091). I had enabled cors origin(http://localhost:8090) in project A for endpoint "greeting". Then trying to call "greeting" endpoint from project B using resttemplate. Since cross origin in project A and the calling Project B are from different ports, I am expecting a cors related error.
But I am getting a proper response instead of an error.
I tried in chrome and postman but same result
//Project A
#RestController
public class GreetingController {
#GetMapping("/greeting")
#CrossOrigin(origins = "https://localhost:8090")
public String greeting() {
return "greetingsss";
}
}
//Project B
#RestController
public class GreetingController {
#GetMapping("/greeting1")
public String greeting() {
final String uri = "http://localhost:8080/greeting";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
return result;
}
}
Expected Result: failed to load, no access control allow origin
Actual Result: greetingsss
CORS is a web thing imposed by user agents(browsers). Hence, service to service communication won't be affected by that. If you want to achieve that for service to service communication, use spring security where you can intercept URLs by matching certain patterns and check a variety of stuff including origin IP/domain (hasIpAddress). Here, you may deny access or allow access using various rules.
Alternatively, you may write a security filter to disallow/allow certain IP/domains.

Spring Cloud Zuul: Apply filter only to specific route

I'm using Spring Cloud's Zuul to proxy some API requests to a few external servers. The proxying itself works well, but each service requires a (different) token provided in the request header.
I've successfully written a simple pre filter for each token that applies the appropriate header. However, I now have a problem. Even after pouring through the documentation, I can't figure out how to make each filter apply only to the proper route. I don't want to perform url-matching as the url changes across environments. Ideally, I'd have some way to get the name of the route in the filter.
My application.yml:
zuul:
routes:
foo:
path: /foo/**
url: https://fooserver.com
bar:
path: /bar/**
url: https://barserver.com
Ideally I'd like to do something like this in FooFilter.java (a prefilter):
public bool shouldFilter() {
return RequestContext.getCurrentContext().getRouteName().equals("foo");
}
but I can't seem to find any way to do this.
You can use proxy header in RequestContext to distinguish routed server like below. If you are using ribbon, you can also use serviceId header. But if you specify url direclty like above your example, you should use proxy header. One thing you have to know is that proxy header is set in PreDecorationFilter, so your pre-filter must have bigger value of filter order than the value that PreDecorationFilter has (it is 5 at this moment).
#Override
public int filterOrder() {
return 10;
}
#Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
if ((ctx.get("proxy") != null) && ctx.get("proxy").equals("foo")) {
return true;
}
return false;
}

Categories

Resources