4

The setup of the RESPApi project is:

  • SpringBoot
  • Spring's OAuth2

In the project we have many clients, so SQL queries almost always have "... and clientId = ?" in the where clause.

We store clientId in the SecurityContext with other user details (we extend Spring's User class).

The question is: how to get the User object in the @Repository?

Possible solutions we can think of:

  1. In every repository implementation add

SecurityContextHolder.getContext().getAuthentication()

cast the result to our custom UserDetails implementation and use it.

Cons: somehow I feel there's a better solution.

  1. Add @AuthenticationPrincipal annotated parameters to the controllers and then pass the parameters to the service layer and then to the repository layer.

Cons: passing the paremeter though 2 layers only to obtain clientId doesn't seem reasonable.

I thought about @Autowired paramter MyUser user in the @Repository class. The first try was to create @Configuration annotated class in which there will be a method

    @Bean
public MyUser getUser() {
    SecurityContext context = SecurityContextHolder.getContext();
    if (context != null) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            return (MyUser) authentication.getPrincipal();
        }
    }
    return null;
}

But the bean is null and I cannot use it.

For now we've ended up with solution nr 1 but I feel there must be a better way.

Any ideas how to solve this problem?

Qwer001
  • 43
  • 5

2 Answers2

1

If you're using Spring Data (or have the time to switch to using it), you can use the SecurityEvaluationContextExtension and use principal directly in your queries: https://stackoverflow.com/a/29692158/1777072

If not, you could hide the static access if it offends (or if you want more control over it changing in future):

@Component
public class AuthenticationHelper {
    public Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
}

Then inject that class into your Repository.

Or your Service. That's probably a better fit than the Repository.

I like to keep Repositories dumb (ultimately using Spring Data to avoid writing them entirely).

And I like to think of Services as being separated out of the web layer, running on separate boxes (even if they aren't). In that situation, you would never pass the Authentication details over HTTP from Controller to Service. The service would obtain authentication details for itself, rather than just trusting what the web layer sent it.

So I think the Service should get the details itself, rather than the Controller passing them through.

David Lavender
  • 8,021
  • 3
  • 35
  • 55
0

Your bean is null because by default beans are singleton and they are created when the application starts, and as you can imagine, you are not going to have a SecurityContext at that point.

Try declaring your bean with request scope, in this way:

@Bean
@Scope(value=WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.TARGET_CLASS)
public MyUser getUser() {
   .....
}
alfcope
  • 2,327
  • 2
  • 13
  • 21