Using static methods with Spring Security to get current user details

Tags: , , , ,



I have a requirement to get the details of the current user who has been loggedIn. To get the details, we can use the SecurityContextHolder.getContext() and extract the details. According to,


SecurityContextHolder, SecurityContext and Authentication Objects

By default, the SecurityContextHolder uses a ThreadLocal to store these details, which means that the security context is always available to methods in the same thread of execution. Using a ThreadLocal in this way is quite safe if care is taken to clear the thread after the present principal’s request is processed. Of course, Spring Security takes care of this for you automatically so there is no need to worry about it.


Storing the SecurityContext between requests

In Spring Security, the responsibility for storing the SecurityContext between requests falls to the SecurityContextPersistenceFilter, which by default stores the context as an HttpSession attribute between HTTP requests. It restores the context to the SecurityContextHolder for each request and, crucially, clears the SecurityContextHolder when the request completes

Many other types of applications (for example, a stateless RESTful web service) do not use HTTP sessions and will re-authenticate on every request. However, it is still important that the SecurityContextPersistenceFilter is included in the chain to make sure that the SecurityContextHolder is cleared after each request.


Question is

I required the user details at multiple places at the service layer to return the information based on the user authority, So we have one static method which returns these details. The project consists of REST APIs and session creation policy as SessionCreationPolicy.STATELESS with SecurityContextHolderStrategy as ThreadLocal. The service layer consists of @Transactional.

Now consider the multiple concurrent requests to the APIs,

  • How Spring Security will manage SecurityContextPersistenceFilter for STATELESS applications, is manual configurations are required?
  • Is it okay to use SecurityContextHolderStrategy as ThreadLocal?
  • Are there any chances to get wrong/invalid user details due to the ThreadLocal strategy and static method?

Environment:

Framework: Spring Boot
ORM: Hibernate
Database: Postgres
Architecture: Monolithic (which is going to migrate to microservices)

I will add more details if required


UPDATE:

Thanks @Macro, as mentioned, for SessionCreationPolicy.STATELESS,

SessionManagementConfigurer consist of isStateless() method which return true for stateless policy. Based on that http set the shared object with NullSecurityContextRepository and for request cache NullRequestCache. Hence no value will be available within HttpSessionSecurityContextRepository. So there might not be issue with invalid/wrong details for user with static method

  • Does SecurityContextHolderStrategy have any impact on the user details with MODE_INHERITABLETHREADLOCAL, MODE_THREADLOCAL as no value will be set to shared object with HttpSessionSecurityContextRepository?

Code:

        if (stateless) {
            http.setSharedObject(SecurityContextRepository.class,
                    new NullSecurityContextRepository());
        }

        if (stateless) {
            http.setSharedObject(RequestCache.class, new NullRequestCache());
        }

Code:

static method to get user details

    public static Optional<String> getCurrentUserLogin() {
        SecurityContext securityContext = SecurityContextHolder.getContext();
        return Optional.ofNullable(extractPrincipal(securityContext.getAuthentication()));
    }

    private static String extractPrincipal(Authentication authentication) {
        if (authentication == null) {
            return null;
        } else if (authentication.getPrincipal() instanceof UserDetails) {
            UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
            return springSecurityUser.getUsername();
        } else if (authentication.getPrincipal() instanceof String) {
            return (String) authentication.getPrincipal();
        }
        return null;
    }


    public static Optional<Authentication> getAuthenticatedCurrentUser() {
        log.debug("Request to get authentication for current user");
        SecurityContext securityContext = SecurityContextHolder.getContext();
        return Optional.ofNullable(securityContext.getAuthentication());
    }

sessionManagement

.sessionManagement()
     .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

Notes

SecurityContextHolderStrategy as ThreadLocal

The service layer consists of @Transactional.

Answer

Setting

.sessionManagement()
     .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

will lead to Spring Security using a NullSecurityContextRepository, instead of the default HttpSessionSecurityContextRepository.

It is a simple implementation, in that it will simply not save anything to the HTTP Session and, for every request, create a completely new and empty SecurityContext, hence with no stored authentication etc.

So, answering your first question:

SecurityContextPersistenceFilter will just work as with the HttpSessionRepository, there’s no other manual configuration required.

To answer your second and third question, regarding Threadlocals.

Yes, that’s the whole point of using them and it’s the default strategy anyway. So, every thread has its own SecurityContext / Authentication objects. You are accessing the ThreadLocal with your static method, hence there’s no chance of accessing “wrong” user details. It would be a problem to use the “GlocalSecurityContextHolderStrategy” in your use-case, but that’s not what you are doing, anyway.

Also, keep in mind that all of this has nothing to do with @Transactionals.



Source: stackoverflow