Spring Cloud Gateway as a gateway as well as web application - java

I have a spring cloud gateway application and what I want is like if there are two routes then on one route it should redirect to some external application but for other route it should forward the request to same app with a particular url.
-id: my_local_route
predicates:
- Path="/services/local"
uri: "/mylocal/services/local" //can we do something like that
Please note I want to create my rest services in same app as in spring cloud gateway. I understand it is not correct approach but for my knowledge I wanted to know whether it is possible or not.

If you have some rest APIs within your spring-cloud-gateway project, you don't need to explicitly put the routes for it.
So suppose you have following rest api in gateway project
#RestController
#RequestMapping("test")
class Controller{
#GetMapping("hello")
public String hello(){
return "hello";
}
}
and for external-url, you want to send some traffic to let's say https://httpbin.org. So in gateway application.yml could look something like this:
spring:
cloud:
gateway:
routes:
- id: httpbin-route
uri: https://httpbin.org
predicates:
- Path=/status/**
With this request like
http://localhost:8080/test/hello will be resolved by your rest controller
http://localhost:8080/status/200 will be redirected to httpbin site
If for some reason you have the same root path for both cases, the controller will have precedence.

If you have the same endpoint in gateway predicates and controller, by default controller will take precedence over predicates, if you want predicates to take precedence over controller, just create a BeanPostProcessor to adjust the order:
#Component
public class RequestMappingHandlerMappingBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
((RequestMappingHandlerMapping) bean).setOrder(2); // After RoutePredicateHandlerMapping
}
return bean;
}
}

Related

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.

Define client target URL at runtime using Quarkus & Resteasy

I need to send HTTP requests from my Quarkus application. Following this guide, I have this RestClient:
#Path("/v1")
#RegisterRestClient
public interface CountriesService {
#GET
#Path("/name/{name}")
Set<Country> getByName(#PathParam String name);
}
In the Path annotation, I can configure the path. But the domain/url to call is defined in a configuration file, according to this paragraph.
# Your configuration properties
org.acme.rest.client.CountriesService/mp-rest/url=https://restcountries.eu/rest #
org.acme.rest.client.CountriesService/mp-rest/scope=javax.inject.Singleton #
In my case, I need this URL to be defined programmatically at runtime, as I receive it as a callback URL.
Is there a way to do that?
Quarkus Rest Client, and Quarkus Rest Client Reactive, implement the MicroProfile Rest specification and as such allow creating client stubs with RestClientBuilder programmatically, e.g.:
public class SomeService {
public Response doWorkAgainstApi(URI apiUri, ApiModel apiModel) {
RemoteApi remoteApi = RestClientBuilder.newBuilder()
.baseUri(apiUri)
.build(RemoteApi.class);
return remoteApi.execute(apiModel);
}
}
See https://download.eclipse.org/microprofile/microprofile-rest-client-2.0/microprofile-rest-client-spec-2.0.html#_sample_builder_usage
You cannot achieve this with client created with the #RegisterRestClient annotation

Spring Cloud Gateway YML routing - is there a way to check permissions?

I have some microservices, and a Gateway using Spring Cloud. I'm trying to set up the routing in the Gateway. Ideally, I would like to set up the routing in the YML file, or with a RouteLocator bean.
But currently, in my Gateway, I have REST endpoints for every route, which is just sending the request onwards using a WebClient.
(Prior to this, I have a ReactiveAuthenticationManager filter which validates a JWT token. It returns a UsernamePasswordAuthenticationToken which includes some user authorities it gets from the token).
Here's an example where I have set up a route to a microservice called the tracking service:
Controller:
#RestController
#RequestMapping("/tracking-service/tracking")
public class TrackingController {
#Autowired
private TrackingService trackingService;
#GetMapping
public Flux getAllTracking() {
return trackingService.getAllTracking();
}
}
Service:
#Service
public class TrackingService {
private WebClient webClient;
#PreAuthorize("hasAuthority('MANAGER')")
public Flux getAllTracking() {
//Make HTTP call to the tracking service
}
}
The reason I have done it this way is because of the #PreAuthorize annotation. If the client's JWT token does not include the 'MANAGER' token, then this will return a 403 forbidden status. Not all of the endpoints require the MANAGER authority and some of the endpoints require other authorities.
My question - is it possible to do this when routing with the YML? I was hoping to see something that looks like this, but I'm not sure it is possible? I have read the Spring docs and looked at all the available filters, and there was nothing that does this job that I could see.
spring:
cloud:
gateway:
routes:
- id: tracking
uri: http://tracking-service
predicates:
- Path=/tracking-service/**
filters:
- StripPrefix=1
- PreAuthorize=hasAuthority('MANAGER')
Thanks.
API Gateway and authorization are two separation of concern. Ideally authorization should not be mixed in gateway application. For permission you can directly map it to service like this.
#RestController
public class ResourceREST {
#RequestMapping(value = "/resource/", method = RequestMethod.GET)
#PreAuthorize("#customPermissionEvaluator.hasPrivilege(authentication,'somepermission')")
public Mono<ResponseEntity<?>> user() {
return Mono.just(ResponseEntity.ok(new Message("Access allowed")));
}
}
And define custom permission evaluator
#Service
public class CustomPermissionEvaluator {
public boolean hasPrivilege(Authentication auth, String permission) {
String requiredPermissionCheck = permission.toLowerCase();
for (GrantedAuthority grantedAuth : auth.getAuthorities()) {
if (grantedAuth.getAuthority().contains(requiredPermissionCheck)) {
return true;
}
}
return false;
}
}
As such gateway does not provide any authorization in the routing which is ideal.

spring boot application with both front end and restful

I am Planning to build a web application using Spring Boot as restful service.
my spring boot web restful application should be accessible by other application as well. In case if any one accessing the rest service from other application then my application should work as expected.
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting(#RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
In above example if call is made from outside of application then the rest service should return JSON output.
One way we can have some variable to distinguish as request variable. But I do not want like that. Please share some standard way.
Appreciate your help.
Idiomatic way is to rely on Accept request header.
If requester presents Accept: application/json then return him JSON data (REST API).
If requester provides you with Accept: application/xhtml+xml return him HTML (web frontend).
Implementation-wise you should is to be done use #RequestMapping with consumes argument. You need two methods. If business logic for both paths is the same then in could be reused. Business logic should reside in another method or in separate #Service. Business logic on its own should not know, care or rely on transport protocol (HTTP), serialization of request response or presentation. Business logic should just work with POJOs and leave serialization to #Controller.
#Controller
#RequestMapping("/greeting")
public class GreetingController {
#RequestMapping(consumes="application/json")
#ResponseBody //required if you want to return POJO (spring will serialize it to response body)
public void rest() {
//return POJO, it will be serialized to JSON. or serialize pojo
directly and return response with manually set body and headers.
}
#RequestMapping(consumes="application/xhtml+xml")
public void html() {
//populate model, return string pointing to HTML to View
}
}
I suggest creating two controller classes, the second one using #RestController. Then, have two paths; the second could be "rs/greeting". This approach, which separates the Web and RESTful concerns, is much more extensible and doesn't require any weird headers that most clients don't want to deal with.

spring boot actuator endpoint mapping root class

In spring we can design rest web service like below.
#RestController
public class HelloController {
#RequestMapping(value = "/hello", method = RequestMethod.GET)
public String printWelcome(ModelMap model) {
model.addAttribute("message", "Hello");
return "hello";
}
}
When we do so, #RestController & #RequestMapping will internally manage request mapping part. So when I will hit url i.e. http://localhost:8080/hello it will point to printWelcome method.
I was looking into spring boot actuator source code. If we will use spring boot actuator in our application it will provide us some endpoints, which has exposed as rest APIs like health, metrics, info. So in my application if I am using spring boot actuator, when I will hit the url like "localhost:8080/health" I will get response.
So now my question is in spring boot actuator source code where this URLs get mapped. I have debugged source code of spring boot actuator, but not able to find out the root class of mapping of endpoints.
Can anyone please help ?
here it is , In AbstractEndpoint it says
/**
* Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped
* to a URL (e.g. 'foo' is mapped to '/foo').
*/
If you see HealthEndPoint it extends AbstractEndpoint and does a super("health", false); , thats where it maps to "localhost:8080/health".
All spring-boot-actuator endpoints extends AbstractEndpoint (In Health endpoint case for example: class HealthEndpoint extends AbstractEndpoint<Health>) which construcor has the id of the Endpoint.
/**
* Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped
* to a URL (e.g. 'foo' is mapped to '/foo').
*/
private String id;
Otherwise, it has an invoke method (from interface Endpoint) through it is invoked the endpoint.
/**
* Called to invoke the endpoint.
* #return the results of the invocation
*/
T invoke();
Finally, this endpoints are configured in the class EndpointAutoConfiguration as Bean:
#Bean
#ConditionalOnMissingBean
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint(this.healthAggregator, this.healthIndicators);
}
Take a look this post where explains how to custom your endpoint:
http://blog.codeleak.pl/2014/10/spring-boot-actuator-custom-endpoint.html

Categories

Resources