I've an interceptor class where in postHandle method, I need to capture all the event request for my application for Audit. I want to implement the gateway pattern between my service and repository. I don't know how to implement the gateway layer which can convert my request object of HttpServletRequest to my domain object and pass it to repository layer. Below is my code:
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
String requestURI = request.getRequestURI();
auditService.save((Audit)reuest);
}
How can I send the request object to service and then to repository through gateway?
(I'm speaking from a Spring Integration stand-point, but this should apply).
The gateway pattern is used to put data into the system from a service. This "service" could be a simple java class or could be tied to a connector in same way to be input from an outside source. The gateway defines what "channel" is used and essentially what data is on that channel (though that need not be explicit).
To create a gateway, you would define what data exists on the gateway and how that will connect with other services. You could use spring integration, or some form of Listeners. Or another. The choice is yours.
Define the gateway where the data comes into your system. This looks to be your Http Request Handler. Here you would push data into the gateway, and don't have a care what happens on the other end. You would then create two endpoints, one for your service so the data is processed correctly and one for your repository so an archive is maintained. (You could even add another later if you wanted.)
Related
Background
Each request will run through an API Gateway, each request will have a JWT with custom claim "x-token". Now, this "x-token" is an id that I will use to get the corresponding data in the DB. The "x-token" will be used in about 2 (out of 3) of my microservices.
Question
Where should this API call should happen?
My current plans are:
1. [API GATEWAY FILTER APPROACH]
Add an API Filter in my Spring Cloud Gateway to modify every (GET/POST) request
-Create a Filter and inside the filter perform an api call to get xTokenObject associated with the "x-token", and then modify to add the xTokenObject in the #RequestBody so that on the Controller part of my microservice I could easily use it.
PROS
API Gateway will be the only one have access to it
I can easily use it on the controller function
Sample Code on the Controller of my Microservice where the 'xTokenObject' was added on the Gateway Filter:
#PostMapping()
public ResponseEntity<SomeObject> someMethod(#RequestBody #Valid XTokenObject xTokenObject){
return ResponseEntity.ok(this.someService.someFunction(xTokenObject));
}
CONS
What if there is already a #RequestBody? How should I add this xTokenObject to that #RequestBody? And how can I access it on the Controller of my microservice?
2. [MICROSERVICE CALL]
Modify the header to add "x-token" in the Gateway Filter and then use it to easily call the API on the #Service of my microservice
PROS
Implementation is easy since adding of custom header is easy on the Gateway Filter than modifying the request body
Sample Code on the Service of my Microservice:
#Service
public class SomeService {
public SomeObject someFunction(String xTokenId) {
// API CALL HERE USING THE xTokenId
return someObject;
}
}
CONS
All of my microservice will have a connection to it
Additional Question and Considerations:
Which one is faster? Or there is another better way? The data on the Token Data Microservice might contain some sensitive data so it will be encrypted. So it will be on the same DB of my auth-server but different table. I will also consider using Redis to cache these data. I included a photo on this question so you guys have an overview.
Maybe this is a strange question (I'm new with Microservices). But I'm looking for some info on how proceed with this. Does not need to be Spring specific, but that's the framework I'm using at the moment.
Example:
Lets say we have two Microservices
a) http://myurlfortesting.com:8085/api/rest/serviceone
b) http://myurlfortesting.com:8090/api/rest/servicetwo
and we have setup Spring Zuul (acting as the API Gateway) with the following rules that forward the incoming calls:
/rest/one -> http://myurlfortesting.com:8085/api/rest/serviceone
/rest/two -> http://myurlfortesting.com:8090/api/rest/servicetwo
The question...
Is there a way to stop users from directly accessing the services mentioned in A and B (only allow the ones that come through the API Gateway)?
Can this be done with Springs Zuul (Acting as a API Gateway) by setting up some extra filters or do we set it up in Microservices endpoints?
Would even like to know if there is a way to not even processing the direct calls on the Microservices endpoints that don't come via the API Gateway.
Maybe this is solved with server specific rules and has nothing to do with Spring?
Many thanks,
/D
Assuming that you have a firewall in place, you could restrict inbound traffic to server to the ports that your Zuul endpoints are exposed on and disallow anyone from accessing the microservices' ports directly.
If you want to avoid going the firewall route, you could force the endpoints to check for a specific HTTP header or something that is set by Zuul prior to forwarding a request, but that would be hacky and easy to circumvent. Based on my past experiences, the "right" way would be to do this via a firewall. Your app should be responsible for dealing with requests. Your firewall should be responsible for deciding who can hit specific endpoints.
Generally, such kind of situation are handled by implementing proper OAuth server wherein only your API gateway will handle the token validation. Any direct call to microservice will not have proper token exchange and hence requests will be aborted.
In case, you have deployed your micro-services on any cloud then you can acheive this by exposing routes to only API gateway.
And yes, firewall blocking, IP whitelisting are some of the other ways in restricting the access to your microservices.
Use a reverse proxy. We use Nginx for the same purpose. Api gateways should always be deployed behind a load balancer in production scenarios to avoid the gateway being a single point of failure(If it is not a managed service like AWS API gateway). Also, the gateway and services are deployed within a VPC and not visible to the public.
Hey I finally find a solution to accept request just from the API Gateway by using microservices architecture, for that you can create a filter, and like Zuul act as a proxy, checking the header 'X-Forwarded-Host', if it doesn't match with the gateway service then return an Unauthorised exception.
public class CustomGatewayFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String proxyForwardedHostHeader = request.getHeader("X-Forwarded-Host");
if (proxyForwardedHostHeader == null || !proxyForwardedHostHeader.equals(GatewayConstant.getGatewayURL())) {
UnauthorisedException unauthorisedException = new UnauthorisedException("Unauthorized Access",
"Unauthorized Access, you should pass through the API gateway");
byte[] responseToSend = restResponseBytes(unauthorisedException.getErrorResponse());
((HttpServletResponse) response).setHeader("Content-Type", "application/json");
((HttpServletResponse) response).setStatus(401);
response.getOutputStream().write(responseToSend);
return;
}
chain.doFilter(request, response);
}
private byte[] restResponseBytes(ErrorResponse errorResponse) throws IOException {
String serialized = new ObjectMapper().writeValueAsString(errorResponse);
return serialized.getBytes();
}
}
do not forget to add your custom filter in SpringSecurity Configuration
.and().addFilterBefore(new CustomGatewayFilter(), ConcurrentSessionFilter.class);
The right way to do this with AWS API Gateway would be with the recently launched 'VPC Link' integration, which secures the connection between API Gateway and your backend inside your VPC.
https://aws.amazon.com/about-aws/whats-new/2017/11/amazon-api-gateway-supports-endpoint-integrations-with-private-vpcs/
So I have a Jersey REST service on a Tomcat server that takes client requests, processes them with an Entity Manager to get data from a database, then sends back a response to the client. So my question is, when is my REST class (containing the URL paths and such) created/destroyed? Is it created fresh with every new AJAX request or does it stay running and open on the server indefinitely?
Thanks!
Jersey basically runs as a single servlet that handles all of the requests. When a request is received, the URI+media types is matched (by the servlet implementation) against all the paths you have defined in your various Jersey-annotated classes. If a match is found, Jersey instantiates the relevant class and invokes the proper method and does all the appropriate magic based on annotations and return types.
The one Jersey servlet gets started once. Your Jersey-annotated class gets a new instance for each request that it handles. I usually have a bunch of #Context-annotated parameters for my constructor, so that I have all the relevant context for the request (request, URI, headers, security context, etc.) available to my API implementations.
I currently have a number of web applications which access a common service running in JBoss 5.0. The service is very simple, using Guice and POJOs. The web applications are authenticated and know who the user is and what roles they have. When calling the service how should I pass this authentication information to the service?
It would seem the simple approach is to simply add a parameter to the interface to take the user information. Possibly a Subject. But this has the downside of cluttering up the interface with contextual information that isn't specific to the job in hand.
void doSomething(Subject subject, ...) {
}
The alternative I have seen is to use ThreadLocal storage, put the user information in there before making the call and make this accessible via some utility class that the service can use. This cleans up the interface but hides the fact that the client of the service has to set the user information before making the call.
Is there another way of doing this? I get the feeling the AOP may be of use here too but can't quite see how. Is there some "best practice" I am missing? Would EJB help?
This cleans up the interface but hides the fact that the client of the
service has to set the user information before making the call.
True, but if you need to pass something to a particular method across the application then you are defeating the purpose of using Dependency Injection. It's there so that you don't have to pass a bunch of services and objects to other services and objects and so forth, they are created with everything they need.
Is there another way of doing this? I get the feeling the AOP may be
of use here too but can't quite see how. Is there some "best practice"
I am missing? Would EJB help?
The other way of doing this would be to use a single filter on every Servlet that calls the services that need the Subject / User. Set the user in the filter, and clear the user at the end in a try-finally block. In fact, OWASP Esapi uses this style when setting their ThreadLocalUser, it allows the User to be available in every part of the application.
Something like this:
#Singleton
public MyUserFilter extends FilterOfTheMonth {
private final Provider<Authenticator> authProvider;
#Inject
MyUserFilter(Provider<Authenticator> auth) {
this.authProvider = auth;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws java.io.IOException, ServletException {
try {
// Authenticate and SET the current user utilizing the request and/or
// session objects
authProvider.get().authenticateUser(HttpRequest currentRequest);
// Continue on here along the servlet chain
... other processing
} finally {
authProvider.get().getRidOfCurrentUser();
}
}
}
Have you considered moving the authentication process to the common service? Then you only need the session ID in the common service to identify all information about the user the request is coming from.
Lets assume a simple Spring MVC Controller that receives the ID of a domain object. The Controller should call a service that should do something with that domain object.
Where do you "convert" the ID of the domain object into the domain object by loading it from the database? This should not be done by the Controller. So the service method interface has to use accept the ID of the domain object instead of the domain object itself. But the interface of the service would be nicer if it takes the domain object as a parameter.
What are your thoughts about this common use case? How do you solve this?
The controller should pass the id down into the service layer and then get back whatever is needed to render the rest of the HTTP response.
So -
Map<String,Object> doGet (#RequestParam("id") int id) {
return serviceLayer.getStuffByDomainObjectId(id);
}
Anything else is just going to be polluting the web layer, which shouldn't care at all about persistence. The entire purpose of the service layer is to get domain objects and tell them to perform their business logic. So, a database call should reside in the service layer as such -
public Map<String,Object> getStuffByDomainObjectId(int id) {
DomainObject domainObject = dao.getDomainObjectById(id);
domainObject.businessLogicMethod();
return domainObject.map();
}
in a project of mine I used the service layer:
class ProductService {
void removeById(long id);
}
I think this would depend on whether the service is remote or local. As a rule I try to pass IDs where possible to remote services but prefer objects for local ones.
The reasoning behind this is that it reduces network traffic by only sending what is absolutely necessary to remote services and prevents multiple calls to DAOs for local services (although with Hibernate caching this might be a mute point for local services).