Is it correct to make a call to another Spring microservice inside a filter? - spring

I built a Spring authentication microservice that takes care of authenticating each REST request. The authentication mechanism has been built using JWT. Each request should present an Authentication: Bearer header.
I also built a gateway microservice that exposes some of the API of the back end microservices. Each request to the gateway should be authenticated. I was thinking about implementing a OncePerRequestFilter, from which I could call the authentication microservice.
#Component
public class AuthFilter extends OncePerRequestFilter {
#Autowired
private RestTemplate restTemplate;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// User restTemplate to call the authentication microservice and authenticate the request.
}
}
My question is probably a little too broad, but I wanted to try anyway.
Are there any issues on making a HTTP request inside a Spring filter? Could this lead to hangs or anything in some edge cases or is it just a poor design?

It is okay to call the external service inside a Filter. Spring Security also often does it in various cases (e.g. authenticate against LDAP, get JWK to verify JWT signature etc.) It may not call the external service directly inside the filter , but the filter will delegate to other object to call the external service. But the idea is the same, just make sure that you handle the case when the external service is not available such as setting a reasonable timeout on the HTTP request when calling the external service. Consider it as fail if it cannot receive the response from the external service after timeout.
P.S. Look like you are implementing your own authorization flow. If your authentication service support OAuth2 , you can consider to try Spring Security 5 's OAuth2 support which may make your life easier.

I agree with you that this approach is contradictory.
I see 2 main constraints in this approach:
this filter in request scope, so you can face with connection/read timeout;
more complex error handling (nested structure requires more accuracy in that).
If you can avoid these constraints, you can use this approach: RestTemplate is synchronous and blocking, so your filter will wait for results of RestTemplate call. With WebClient, such task could be unachievable.
Obviously that this implementation contradicts to SOLID, so better change your design/architecture.

It's a general practice to make HTTP calls from one microservice to another.
When dealing with authentication in REST API using JWT tokens it's a common practice to load cryptographic keys from a well known URL (https://YOUR_DOMAIN/.well-known/jwks.json) as JSON Web Key Set (JWKS).
According to SOLID principles, it's a good practice to create a dedicated class (Spring service) responsible for calling another service instead of injecting RestTemplate directly into the Filter.
The less responsibilities class has, the easier (less error-prone) it is to make changes to it in the future and cover code with unit tests. When all the logic is located in a single class, you have a lot of test cases and complicated mocking. When class has a single responsibility and calls another classes, it's easy to mock these classes and test only this single responsibility.
When a microservice calls another microservice via REST API, there are a few aspects that should be taken into account:
Failover - have multiple instances of the microservice to tolerate failure if a single instance
Scalability - dynamically start more microservice instances to handle growing load
Load balancing - route requests to different instances of the microservice
Service discovery - allow microservices to find each other by logical name instead of hard-coding host and port values
Timeouts - drop a request to a downstream microservice after exceeding timeout instead of waiting for the response forever
Retries - retry failed requests to a downstream microservice
Circuit breaker - prevent network or service failure from cascading to other services
These aspects are covered by Kubernetes + Istio or Spring Cloud stack.
If synchronous REST API calls between microservices are replaced with asynchronous messaging then almost all of these aspects are not so important.
For example, service A, instead of calling service B via REST API, subscribes on events emitted by service B to a Kafka topic. Every time event occurs, service A receives notification and persists the required data in own DB.
Such approach allows to achieve maximum decoupling between services but leads to eventual consistency. It means, it doesn't suit authentication and other strongly consistent cases. E.g. checking account balance is strongly consistent and should be done using synchronous call.

Related

calling a rest endpoint in a springboot application

which is better alternative for calling REST endpoint in springboot application, calling REST endpoints using WebClient or calling REST endpoints using RestTemplate ?
Spring’s documentation recommends using WebClient, but that’s only a valid recommendation for reactive apps. If you aren’t writing a reactive app, use OpenFeign instead. Like anything else in software, it fits well for some cases, but might complicate things for others. Choosing WebClient to implement the REST endpoint calls is strongly coupled to making your app reactive
RestTemplate gives many advantages if you are using it from within Springboot application, i.e. in your server side to another part of your own app - sort of like an internal call. Because the RestTemplate "knows" all your entities and beans and so if you need to send over or receive an object which is known within your springboot application RestTemplate can map them automatically which is a very nice advantage. If you sending a request to some third party api and do not pass or receive your known entities RestTemplate is still a valid option but it just becomes just another Http client. Its just simply there as part of Springboot provided tools. But in this case you may use any other client as well.

Authentication and Data Validation in Microservices

I am currently working on a project which has a GraphQL service that takes care of handling all client requests and communicates with other microservices as needed be. This GraphQL service is the only service exposed to the client, basically kind of like an API gateway.
Taking this into account, I was wondering if all microservices are required to have authentication/authorization handlers, as well as input and data validation. Since these microservices can only be accessed by the GraphQL service and are never exposed to the public, is there any risk to not performing these mentioned tasks on them? Can't the main GraphQL service simply take care of all the authentication, authorization and input validation and then proceed to only send requests to the microservices having these steps occurred successfully?

How to propagate User's authentication (and authorization) to other microservices

I've created a microservice (using Spring boot) for landing page. User provides the credentials and upon successful login, he can see the dashboard. I've used RestTemplate in-order to traverse to other microservice from the dashboard. Below is the code snippets:
#Autowired
private RestTemplate restTemplate;
#GetMapping("/employeeCenter")
public String openEmployeeCenterApp() {
logger.info("Invoking EmployeeCenter micro-service");
return restTemplate.getForEntity("http://EMPLOYEE-SERVICE/empCenter/", String.class).getBody();
}
Which is working fine. But I would like to propagate the User's authentication (and authorization) to any other microservices; so that based on the user's privilage, I can show/hide stuffs. What would be the suitable way to do that?
There are many concerns here.
When a "landing page" service (let's call it "A") shows the dashboard for user with identity "Foo" and tries to contact an another service "B" which is an another process via Rest template, you should implement the identity propagation of "Foo", its not the job for the spring security. Spring security will help you in authentication / authorization of user "Foo" inside the service "A", but it has nothing to do with identity propagation.
After all, service "B" can be something not written with spring or java at all, or even some external system.
There are many approaches here depending on requirements that usually boil down to:
Rely on the fact that only landing page (service A) is your "gateway" to the outer world and hence on this service requires security
Protect all the services (like putting spring security or any other security component in each service)
Like "1" but differentiate between security gateway and landing page - have two microservices, so that gateway will be protected and will be "opened" to the end users but also to internal communication. The difference is that if you need to call service "B" from service "A" you can do it through the gateway and it will check the security
Another concern here is what exactly means the "act" of calling the service B from service A.
One thing is to tell "I'm user Foo (currently in a Service A) and I'm calling B as Foo.
Another thing is telling "I'm service A (that has its own identity) and I'm calling service B on behalf of user Foo".
All these question are in domain of security related architecture and many solutions can be adopted. If you have to do such an analysis (although its kind of out of scope for this question) make sure that you understand that performance implications of each approach:
- HTTP hops can be expensive
- DB hops (in case you have to turn to the database) are expensive
Apply caching wherever possible
Now, technically speaking, you should provide some Filter that will add Identity Headers taken from the Thread Local or similar solution if you run reactive stack.
So in the most simple situation you can put an identity object on some thread local and get it in the filter to add requests.
So that bottom line when you construct a request in a RestTemplate (that in turn relies on okHttp, Apache Http Client or whatever) it will have additional headers that will provide the identity information (and again its up to you to decide what exactly is an "identity" here).
The service "B" in turn should parse this object again in some kind of filter (ideally before it hits the rest controller if "B" is in spring as well) or even with spring security if you have it installed on Service B, and make its "protection decisions" based on that identity, then again, put it on thread local if B will query other services and so forth.
You can try using WebSecurityConfigurerAdapter and override the configuration of AuthenticationManagerBuilder bean which will let you set roles.
This article has a good example: https://www.journaldev.com/8748/spring-security-role-based-access-authorization-example

Zuul Proxy: Aggregated mapper on proxy routing

I have an application which looks like this
Now, whenever a request is received at the gateway, Zuul filter AuthorizationInterceptor gets invoked which does authentication/authorization by making some calls with User Service. If the authorization succeeds then actual microservice is called.
Now the with the new requirement, I need to write some aggregated API which takes data from two services and combine the data. So I added #RestController on Gateway Proxy but before that rest controller gets invoked AuthorizationInterceptor is not getting invoked. How can I tell ZuulFilter to get invoked even when the request is locally served.
Is there any solution for this? Should this be architected in a different way?
I don't think it's a good idea to put this kind of logic into the gateway service.
What you are referring to is an aggregator service which can be after the gateway as the other two services but instead of storing data or doing some business logic, it just takes the data from the two other services and aggregates them. This way the AuthorizationInterceptor will be invoked on the gateway level as well.
Another hacky approach would be to create a custom ZuulFilter which handles an imaginary endpoint call and aggregates the data. This way your AuthorizationInterceptor will be called as well but it's not a good idea to put this kind of logic into your edge service.

Spring Security - Method level security and non-authenticated invocations

I have a vanilla Spring Boot application that consists of a controller, a service and a DAO layer. The controller code calls the service code and so on.
To implement some semblance of security, I am currently using Spring Security 4.0.x's global method security annotations in combination with Spring Security ACL to lock down my service methods.
Requests that go through my controllers are auth-ed and authorized just fine because a principal / user is in context. HOWEVER, I also have some additional non-user facing code that listens for messages from an AWS queue. Within this listener code I invoke some secured services (to stay DRY and not duplicate business logic) but for this situation no user is in scope.
Generally speaking:
For a situation like the one I'm describing, what is a good / acceptable way to authenticate user-less method invocations e.g. ones that don't come through an HTTP request (or to bypass the check)? I am considering manually setting the SecurityContextHolder with a "system user" in my message listener code but this has some code smell.
Is method level security better applied at the controller level?

Resources