Spring Gateway builds incorrect URL with query string parameters - spring-boot

I have a Spring gateway microservices with this mapping:
- id: advertisementService4
uri: lb://advertisementService
predicates:
- Path=/public/breeds/**
filters:
- RewritePath=/public/breeds/(?<segment>/?.*), /v1/public/breeds/$\{segment}
If I make a call with Postman to this url: http://192.168.99.1:8050/public/breeds/500, the gateWay service solves the mapping and build the new url in the correct way (to /v1/public/breeds/500):
But if I call to this url http://192.168.99.1:8050/public/breeds?petType=Dog, the gateWay service chooses the correct mapping, but it builds the url in a incorrect way:
The gateWay service builds http://7a32a826ec7a:8070/public/breeds?petType=Dog instead of http://7a32a826ec7a:8070/v1/public/breeds?petType=Dog (with v1 in the URL)
I don't understand why. Could you help me please?

I fixed it changing the RewritePath:
from RewritePath=/public/breeds/(?/?.), /v1/public/breeds/${segment}
to RewritePath=/(?/?.), /v1/${segment}

Related

Expose public and private endpoint through Spring Cloud Gateway

I'm using Spring Cloud Gateway like entrypoint for my infrastructure. The gateway is configured with keycloak to validate Authentication header with following configuration
spring:
security:
oauth2:
resource-server:
jwt:
jwk-set-uri: https://httpd.keycloak.local:443/keycloak/realms/myRealm/protocol/openid-connect/certs
An example Route is the following
spring:
cloud:
gateway:
routes:
- id: my-route
uri: http://service.local:8020
predicates:
- Path=/myPath/api/myRoute/test
filters:
- name: StripPrefix
args:
parts: 2
How can I define, into yml file, this route public and another one authenticated through jwk-uri directed to keycloak?
I see nothing about security rules in spring-cloud-gateway configuration doc.
I believe you'll have to either:
let all traffic go through gateway and handle security on each service
write a web-security configuration class and define a SecurityWebFilterChain bean.
use spring-addons-webflux-jwt-resource-server to define permitAll() routes from properties (other routes requirering user to be authenticated)
Only last solution would work with yaml file configuration only:
in pom, replace spring-boot-starter-oauth2-resource-server with
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-jwt-resource-server</artifactId>
<version>${com.c4-soft.springaddons.version}</version>
</dependency>
in yaml, replace spring.security.oauth2.resource-server with
com.c4-soft.springaddons.security:
jwt-issuers:
- location: https://httpd.keycloak.local:443/keycloak/realms/myRealm
permit-all:
- /myPath/api/myRoute/test
- /public/**
Note that trying to access "non-public" routes without valid authorization would result in 401 (unauthorized) and not in 302 (redirect to login). In my opinion, client should unsure requests to protected routes are issued with Authorization header or handle unauthorized with a redirection to authorization-server and a retry.
Also note that spring-security-oauth2-webflux-addons will auto-configure more than just permit-all routes (CORS, CSRF and authorities mapping for instance).
Last, I haven't tried it yet with spring-cloud-gateway. please let me know how it goes ;-)

Applying regex in Spring Cloud Gateway Path predicate

I'm using 3.1.0 version for Spring Cloud Gateway and I need help in checking and fixing the Path predicate filter for my route configuration.
I'm trying to use regex in between a path, the route looks something like this in my config:
- id: Route1
uri: https://example.com
predicates:
- Path=/random/path/\d+/update
- Method=POST
Though this is a valid regex, I'm getting 404 error code for requests for path :
/random/path/12/update
Hence I need help to figure out the correct configs for this usecase
It requires a named path segment. See the syntax here. You can then use {mynum} in filters like SetPath
- id: Route1
uri: https://example.com
predicates:
- Path=/random/path/{mynum:\d+}/update
- Method=POST

How to prevent springboot gateway to forward to last element in the chain

I am using springboot gateway for load balancing requests between my microservice
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: baeldung
uri: question.com/questions/444/ask
- id: myOtherRouting
uri: answer.com/answers/333/answer
my problem is, if i send a wrong request like "question.com/questions/dasldkjasdas"
then the request is forwarded to answer.com/answers/333/answer
since this is the last chain in my config, how can i prevent this, and send notfound statuscode as response. I used
filters:
- SetStatus=NOT_FOUND
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.0.RELEASE/reference/html/#setstatus-gatewayfilter-factory
but this is not the best approuch i think.
You don't have any predicate specified for your route definitions - which means both of them match any incoming request. From Spring Cloud Gateway:
Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a Route, it is sent to the Gateway Web Handler.
and
Route: Route the basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates and a collection of filters. A route is matched if aggregate predicate is true.
Since you don't have any predicate specified - your route matches the incoming request and the last one is winning.

spring clould oauth2 redirect to "/" after login with spring cloud gateway

I'm a new comer for the spring oauth2. With the document check, I do a simple function for the oauth2 in a spring cloud alibaba nacos environment.
In the design, I want to add redirect for oauth request from gateway to auth micro service. Then auth micro service authorize with form login, and authorize the scope, then redirect to the redirect url set in oauth url.
When raising request to auth micro service directly, it works well. But if raising request to gateway, after log in, it will redirect to "/" direction and raise 404 error.
With investigation for SavedRequestAwareAuthenticationSuccessHandler, I found the session contained in request in null. Further more, I check the HttpSessionRequestCache, it also gets null session when saving the request. Since the request is not saved, it can't redirect to origin url after login and redirect to default url "/".
But I don't know why the request related session is empty. I also try to do a demo project like following:
#RequestMapping(value = "hello",method = RequestMethod.GET)
public RedirectView hello(HttpServletRequest request){
System.out.println("Hello:"+request.getSession().getId());
return new RedirectView("thanks");
}
#RequestMapping(value = "thanks",method = RequestMethod.GET)
public ResponseEntity<String> thanks(HttpServletRequest request){
System.out.println("Thanks:"+request.getSession().getId());
return new ResponseEntity("aaa",HttpStatus.OK);
}
The printed session id are different when using gateway. But without gateway, they are same. The following is my gateway settings:
server:
port: 8080
spring:
application:
name: flower-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true
routes:
- id: order-routes
uri: lb://flower-order
predicates:
- Path=/order/**
- id: core-routes
uri: lb://flower-core
predicates:
- Path=/**
- id: auth-routes
uri: lb://flower-auth
predicates:
- Path=/oauth/**
So I guess that this is caused by spring cloud gateway. But I don't know how to fix it. Please give me some information. Thanks.
New updates:
As I change the auth gateway to
- id: auth-routes
uri: https://127.0.0.1:8082
predicates:
- Path=/oauth/**
it passes normal behavior. So does it means that it is caused by load balance. But as I just have one running entity, not quite sure. Does this mean it should use the distribute session management such as spring session? As in actual production environment, the auth service won't only have one entity.

Zuul url mapping with spring boot,Eureka

I am building Rest api using microservice architecture. I have multiple apis for user that we have made into multiple projects. I have everything else ready except I am not able to map the user facing url to the application url in zuul.
The user facing url is : user/v1/accountholders/{id}/cards and the actual url for my application is /user-cards/v1/accountholders/{id}/cards.
Here id is the path variable. Below are other similar api url so if there is a way to configure them generically in zuul. Also the context root of the application url is also the project name in Eureka.
Other similar urls are:
client side:- /user/v1/accountholders/{id}/cards/{cardid}
application:- /user-cards/v1/accountholders/{id}/cards/{cardid}
client side:- /user/v1/accountholders
application:- /user-cardholder/v1/accountholder
client side:- /user/v1/accountholders
application:- /user-cardholder/v1/accountholder
client side:- /user/v1/accountholders/{id}
application:- /user-cardholder/v1/accountholders/{id}
client side:- /user/v1/accountholders/{id}/accounts
application:- /user-accounts/v1/accountholders/{id}/accounts
client side:- /user/v1/accountholders/{id}/accounts/{accid}
application:- /user-accounts/v1/accountholders/{id}/accounts/{accid}
Need some help to set this up in the properties or yml file for zuul. I havent been able to make any progress with the mapping stuff yet. Any inputs will be helpful.
SOLVED:-
After getting the input from #Daniel (which is the accepted answer)This is what i used in zuul config:-
zuul:
routes:
User-Cards:
path: /user/v1/accountholders/*/cards/**
url: http://desktop-uvkv1ed:9999/user-cards/v1/accountholders
User-Transactions1:
path: /user/v1/accountholders/*/transactions
url: http://desktop-uvkv1ed:5555/user-transactions/v1/accountholders
service-id: User-Transactions
User-Transactions2:
path: /user/v1/accountholders/*/accounts/*/transactions
url: http://desktop-uvkv1ed:5555/user-transactions/v1/accountholders
service-id: User-Transactions
User-Accounts:
path: /user/v1/accountholders/*/accounts/**
url: http://desktop-uvkv1ed:7777/user-accounts/v1/accountholders
User-Cardholders:
path: /user/v1/accountholders/**
url: http://desktop-uvkv1ed:8888/user-cardholders/v1/accountholders
It is possible to achieve what you are trying to do by giving the correct Zuul config. Lets assume you have the user-cardholder service running on port 8081 and the user-account service on 8082 so that you can successfully answer requests going against:
http://localhost:8081/user-cardholder/accountholders/{id}
http://localhost:8082/user-account/accountholders/{id}/accounts/{accid}
If this is working then you can achive what you are trying for these two services by using the following zuul config:
zuul:
routes:
cardholder:
path: /user/accountholders/*
url: http://localhost:8081/user-cardholder/accountholders/
account:
path: /user/accountholders/*/accounts/**
url: http://localhost:8082/user-accounts/accountholders/
Unfortunately you will also have to add more configs – even when they go against the same backend service – due to the fact that internal and external urls are differing. Otherwise you could just add the option stripPrefix: false and use the same internally as externally.

Resources