How to redirect user request to other Restful API that runs on other server in Spring boot? - spring-boot

I am a noob in spring boot. I am writing a Gateway for some services. In a condition, I need to forward user request to other services (some Restful API) after authentication. I have done some search on 'forward' and 'redirect'. I think I need 'forward'. But I still have some questions: 1. when I forward it to other URI(eg. abc.dce.com/service/), does the service get the request body. 2.How can I do it in spring boot? Do you guys have a good example that fit my condition? (I admit that I am kind of lazy for this, but there are really many style of forward that confused me.)
//I find this example, but this is forwarding to service in same package //under same Internet.
#Override
public void addViewControllers(ViewControllerRegistry registry) {
// forward requests to /admin and /user to their index.html
registry.addViewController("/portal").setViewName(
"forward:/app/index.html");
}

Since you mention you're new to spring boot, you might want to take a look at spring project that implements a fully-featured gateway. I've used an earlier version of it (zuul) and the current spring-cloud-gateway allows you to implement a complete gateway easily by creating a spring-boot project and configuring. It has a lot of features you'll likely want to implement as a gateway (like adding/removing headers, modifying payloads,..). If you need features they don't support, you can implement via filters and other interfaces they provide. This was initially opensourced from Netflix so it is fairly comprehensive.
https://spring.io/projects/spring-cloud-gateway
Sample project:
https://github.com/spring-cloud-samples/spring-cloud-gateway-sample

Related

Managing authorization at API Gateway (Kong HQ) + Keycloak + Spring boot microservices

I am working on a Microservices architecture where I am using following components:
KongHQ as an API gateway
Keycloak as a IAM solution
Microservices written in Spring Boot.
My basic requirement is to completely decouple authentication/authorization from Spring boot microservices. Therefore, authentication as well as authorization of API should be done at API gateway layer.
Most of the articles and tutorials recomment to integrate Keycloak with Spring Boot as well.
I would really like to know if the requirement is feasible and if so how to do it?
I would not de-couple security from micro-services for two reasons:
access-control is a business requirement I want to unit test
spring-security is probably more powerful (expressive, testable, etc.) than any API gateway security filter
You do not have to integrate Keycloak with spring-boot (actually you should not because Keycloak libs for spring are deprecated).
Spring-security has tools for resource-servers. Sample here.
I also wrote (very) thin layers on top of spring-boot-starter-oauth2-resource-server to ease resource-server configuration. Most config options are available from properties, which save quite some Java code in micro-services. Sample there.
Following advanced tutorial, you can even build a security DSL as expressive as #PreAuthorize("is(#username) or isNice() or onBehalfOf(#username).can('greet')"). Good luck to achieve the same with an API getway.
The repo linked above also contains quite a few annotations for configuring unit-tests security-context:
#Test
#ProxiesAuth(
authorities = { "AUTHOR" },
claims = #OpenIdClaims(preferredUsername = "Tonton Pirate"))
void whenHimselfThenCanGreetFor() throws Exception {
mockMvc.get("/greet/on-behalf-of/Tonton Pirate").andExpect(status().isOk()).andExpect(content().string("Hi Tonton Pirate from Tonton Pirate!"));
}
Here is a very detailed article to introduce the use of API Gateway and keycloak:https://apisix.apache.org/blog/2022/07/06/use-keycloak-with-api-gateway-to-secure-apis/

Configure Spring Gateway with Spring Authorization Server

I have Spring Gateway application with the following Gradle dependencies:
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-gateway'
implementation 'com.netflix.eureka:eureka-core'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
Route configuration:
#Bean
public RouteLocator routes(RouteLocatorBuilder builder, LoggingGatewayFilterFactory loggingFactory) {
return builder.routes()
.route("service_route_clients_summary", r -> r.path("/management/home/clients/summary")
.filters(f -> f.rewritePath("/api/management/home/clients/summary", "/management/home/clients/summary")
.filter(loggingFactory.apply(new LoggingGatewayFilterFactory.Config("My Custom Message", true, true))))
.uri("lb://merchant-hub-admin-service:8000/management/home/clients/summary"))
.build();
}
I want to forward /api/management/home/clients/summary to internal link /management/home/clients/summary after successful authorization with Spring Authorization Server. Is it possible to use Spring Authorization server without exposing it to outside?
What is the proper way to implement this?
Code example: https://github.com/rcbandit111/Spring_Cloud_Gateway_POC
Is it possible to use Spring Authorization server without exposing it to outside?
The Spring Authorization Server is implementing the OAuth2 protocol. If we look at the specs, we can see that it's naturally a client, which wants to authenticate itself: https://datatracker.ietf.org/doc/html/rfc6749#section-1.2. In your setup, it seems like that client is not the gateway itself, but lets say an app. So if that app should use the Spring Authorization Server, it would authenticate with it (most of the time, that means acquiring a token), before the first interaction with the gateway happens.
So to answer that question, if you want to use OAuth2 as it was meant to be used, you would need a direct connection between the app and the Spring Authorization Server, which means, it needs to be exposed to the outside. This makes perfect sense, since OAuth2 is meant to be used not with a single service, but with lots of different services, e.g. to allow single-sign-on.
What is the proper way to implement this?
You could setup your own Spring Authorization Server, implement your custom Authorization Code Grant logic, so that a client can authenticate itself and acquire a token. This could for example be a JSON Web Token (JWT). This can (should) be independent from your gateway and other services.
To authorize requests at the gateway, there are different ways. You could implement a logic yourself, which reads a JWT from the request and authorize it with the Spring Authorization Server or another identity provider, which you might have.
You can also use Spring Cloud Security with the Spring Authorization Server, which is described here: https://spring.io/blog/2019/08/16/securing-services-with-spring-cloud-gateway. This is definitely a proper way to implement it.
One more thing: as you can see, OAuth2 comes with a price, being that it is not trivial to understand and configure. You must understand it in detail, before you deploy such a setup. On the other hand, its a solid standard and you can have feature like single-sign-on out of the box. So if you only need authorization for single or multiple apps to a single service, there are probably easier ways to gain a token and secure an app and OAuth2 is maybe overkill. But OAuth2 really pays off, if you have lots of services and lots of apps which should be authenticated with a central (sometimes company-wide) solution.

how to make spring mvc functions available for rest calls

I have a spring mvc application which runs correctly,now another colleague wants to call the same functions from another application but he needs REST URL of my functions.
how is it possible to provide the same functionality through spring REST?
is it just with new annotations .please provide some resource to show me how to do it.
when server has a service, only legal clients which had any contracts with server can access it. And clients can use service by the way such as: use RestTemplate to get/post request to URL of service, and clients can get data as JSON, or XML type if you have an equivalent object as this image:
Also, a service can be support as a interface, ex: google search is a service supported by google, but it's not rest service.
If you know each other URL address you can consume each other REST API from java code by using RestTemplate object.
I would advise you to go over the Spring starter guide which deals with that issue, here is the link (Consuming a RESTful Web Service):
https://spring.io/guides/gs/consuming-rest/

Authentication and authorization in Spring Data REST

I am implementing a Spring Data REST based app and I would like to know if there is an elegant way to implement authentication and authorization rules using this framework or related frameworks.
All HTTP requests to the REST server must carry authentication headers, I need to check them and decide to authorize or not based on the HTTP method and the association of the authenticated user with the resource being requested. For example, (the app is the REST server of an e-learning system), the instructors can access only their own course sections, students can access only the courses sections they are subscribed, etc.
I would like to know if there is a default way to implement authorization in Spring Data REST. If the answer is no, could you make a suggestion for my issue? I am thinking about:
Servlet Filters
Spring Security
Spring Data REST Handlers (how to access the HTTP headers?)
The best bet for you is Spring Security.
That would help you achieve authorization is much simpler manner.
Spring Security would require you an implementation that looks at request headers and performs the log-in operation programmatically.
Refer the accepted answer here.. I had followed the same and implemented the security layer in front of my rest services ( which were build using RestEasy )
RESTful Authentication via Spring
There is an alternate method as well..
Refer
http://www.baeldung.com/spring-security-authentication-provider
In both cases you can disable the session creation by declaring the stateless authentication in spring security, this would help you improve the performance considerably when large volume of hits are made to the state-less REST services..

Rest-SOAP gateaway for external services (Spring + Camel)

I need to build REST-SOAP gateaway between 2 external services
First web services makes SOAP requests and awaits SOAP response. Second service (mine, written in Play Framework 1.2.4) works only using RESTful approach. I don`t want to integrate SOAP related things with second service for many reasons. So I need some third service to act between them.
I have looked into using Spring web-app with Apache Camel, but still can't get the full picture because there are so many modules for Camel. CXF-RS and SOAP components looks promissing, but I can't figure out how to implement proxying using them.
First of all, how to make Camel listen for the specified SOAP request. And then, how to route response from RESTful service back to calling service.
I tried to do it using only spring configuration.
Camel CXF will do the trick for your soap endpoint.
First you need to write an endpoint
#WebService
public interface QuoteInEndpoint {
#WebResult(name = "quote")
public Quote price(#WebParam(name = "symbol") String symbol);
}
Then you need to declare it
<cxf:cxfEndpoint id="quoteIn" address="http://localhost:9002" serviceClass="my.package.QuoteInEndpoint" />
You can then build a route from this endpoint
from("cxf:bean:quoteIn")//
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
//do whatever you need to get your object and transform it for your rest service
}
})//
.to("http://myplayframeworkserver/myservice")//
Camel will start the route, expose the wsdl of your soap service at localhost:9002, and every soap request will be send to your rest server. The process method can be use to shape your objects to the correct format for your rest service (I assume json). Instead of using a processor, you might use another Camel component to do the job (Camel JSON if you need json)
There is no straight forward way to simply proxy between soap and rest. REST services, is all about resources and CRUD - create/read/update/delete. The payload can be whatever, often JSON, but XML, plain text or any orther format is valid. SOAP is XML only with custom definied methods.
I understand the confusion about all the components related to this in Camel.
There are a few aspects you need to have in mind, while chosing your approach.
How much of the SOAP stack do you really need? Most likely you only want the basic featuers, such as receiving a simple soap-envelope and extract the payload without WS-addressing, ws-security etc. etc.
Do you have a contract first or code first approach? That is, do you have your soap service already definied by java classes/interfaces or do you have a WSDL?
Do you have your camel instance deployed inside a servlet container (which is quite common), such as Tomcat, Jetty or a JavaEE app server? If you, you might need to use that servlet container to handle requests by some reason (to get all requests from the same port/server/Domain name by some reason such as web server virtual host, firewalls etc). Then CXF might ge a bit tricky. Otherwise, camel can put up listeners with the built-in jetty core.
So:
Contract first and camel inside serverletcontainer - I prefer spring-ws, since it's very easy to get started with. spring-ws component. Just do the initial wireing in spring and you do not even need to generate things from a WSDL, just simply point out which soap-action, uri or rootq name to get messages from:
from("spring-ws:soapaction:http://example.com/GetFoo?endpointMapping=#endpointMapping")
then you have the XML.
If you need to host the web service from camel, CXF in payload mode is quite decent and will behave pretty much the same.
from("cxf:somename:http://localhost:8765?wsdl=somewsdlfile.wsdl&dataFormat=PAYLOAD")
If you have the service definied in Java already, you could use the SOAP dataformat with the Jetty component to get a very lightweight solution.
SoapJaxbDataFormat soap = new SoapJaxbDataFormat("com.example.customerservice", new ServiceInterfaceStrategy(CustomerService.class));
from("jetty:http://localhost:9832/soapsrv")
.marshal(soap) // and other transforms here
.to("http://somerestservicehost/srv");
Or. go with the full CXF solution with CXF or CXF-bean. There are plenty of examples on the camel website. But the component is rather large and can be somewhat tricky.
For rest, there are also choices, but that part is more straight forward. Rest is very much about creating some content (by extracting it from the soap message, and perhaps map xml to json), which might be easiest to achieve with some plain old java code. Then just invoke a HTTP endpoint towards your rest server. The HTTP4 or HTTP component will do a lot of this for you.
CXFRS is good if you like CXF, and can provide some help, specifically if you want to model your rest service with classes

Resources