Michal
Michal

Reputation: 15669

How to communicate user ID between Spring Boot microservice applications

I have three services:

  1. Auth (OAuth2 token provision and username + passwords, nothing else)
  2. User-related resources (linked devices, date of birth etc.)
  3. Domain data

All three of them are Spring Boot applications. Authentication is done via OAuth2 token provision, using JDBC Token Store. Logging in, obtaining the token works well, requesting data from either of the other two resource servers works well too. However I am not happy with how I'm doing it, as it seems a bit clunky:

All of the data on resource servers is linked to the user via server-side generated user ID. I believe linking it to the username is a bad idea should the user ever want to change his/her username. Right now, the way I'm getting the user ID is in short:

@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<SampleData> getData(OAuth2Authentication authentication) {
    Authentication auth = authentication.getUserAuthentication();
    final Map<String, Object> map = (Map) auth.getDetails();

    // parse user data from the map ...
}

The method getDetails returns the whole User object on the Auth server as a map - because that's the way I linked it in application.properties file:

security.oauth2.resource.userInfoUri=http://localhost:9000/api-auth/user

But I can't bring myself to be OK with this solution, because:

  1. All user-facing methods would have to start with this parsing and even after I made it into a separate class, it's still the same two lines for every method.
  2. I feel like the methods shouldn't even have the object OAuth2Authentication authentication as the input - it should be in a layer above and not here (could be my wrong feeling, though).

My question is - can I do it more efficiently? In a more clean and sustainable way?

I'd like to have:

@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<SampleData> getData(String userId) {
    // interact with data with the userId
}

And definitely not depend on the client sending this data - this has to come from the Auth server.

My (so far failed) thoughts:

  1. JWT token usage - failed on the fact that I'm using JDBC Token Store and that seems to be not compatible with JWT. Also JWT tokens cannot be revoked as they're not stored and have to be dealt with through short-lived access/refresh tokens. But if nothing else works, I am open to this approach.
  2. Filters - my favorite, I thought this would be it, but I couldn't make it work - I receive the request in a e.g. OncePerRequestFilter and parse the data. But then I was unable to distribute the parsed object straight into the controller. This seemed like the solution, but I guess it's not meant for this, perhaps.

Am I approaching this completely wrong or am I missing some Spring Boot detail that solves this automatically?

Upvotes: 2

Views: 1888

Answers (1)

StanislavL
StanislavL

Reputation: 57421

I would use the option 2 with OncePerRequestFilter. You can parse it once and store the results in a ThreadLocal or InheritableThreadLocal if you create new threads.

Then the thread local data can be accessed from your controllers or even deeper from service layer.

ThreadLocal:

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

Upvotes: 1

Related Questions