Spring Security's Securitycontextholder: Session or Request Bound

Spring security's SecurityContextHolder: session or request bound?

It depends on how you configured it (or lets say, you can configure a different behaviour).

In a Web application you will use the ThreadLocalSecurityContextHolderStrategy which interacts with SecurityContextPersistenceFilter.

The Java Doc of SecurityContextPersistenceFilter starts with:

Populates the {@link
SecurityContextHolder} with
information obtained from the
configured {@link
SecurityContextRepository} prior to
the request and stores it back in the
repository once the request has
completed and clearing the context
holder. By default it uses an {@link
HttpSessionSecurityContextRepository}.
See this class for information
HttpSession related
configuration options.

Btw: HttpSessionSecurityContextRepository is the only implementation of SecurityContextRepository (I have found in the default libs)

It works like this:

  • The HttpSessionSecurityContextRepository uses the httpSession (Key="SPRING_SECURITY_CONTEXT") to store an SecurityContext Object.
  • The SecurityContextPersistenceFilter is an filter that uses an SecurityContextRepository for example the HttpSessionSecurityContextRepository to load and store SecurityContext Objects. If an HttpRequest passes the filter, the filter get the SecurityContext from the repository and put it in the SecurityContextHolder (SecurityContextHolder#setContext)
  • The SecurityContextHolder has two methods setContext and getContext. Both uses a SecurityContextHolderStrategy to specify what exactly is done in the set- and get-Context methods. - For example the ThreadLocalSecurityContextHolderStrategy uses a thread local to store the context.

So in summary: The user principal (element of SecurityContext) is stored in the HTTP Session. And for each request it is put in a thread local from where you access it.

How do I get the Session Object in Spring?

Your friend here is org.springframework.web.context.request.RequestContextHolder

// example usage
public static HttpSession session() {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest().getSession(true); // true == allow create
}

This will be populated by the standard spring mvc dispatch servlet, but if you are using a different web framework you have add org.springframework.web.filter.RequestContextFilter as a filter in your web.xml to manage the holder.

EDIT: just as a side issue what are you actually trying to do, I'm not sure you should need access to the HttpSession in the retieveUser method of a UserDetailsService. Spring security will put the UserDetails object in the session for you any how. It can be retrieved by accessing the SecurityContextHolder:

public static UserDetails currentUserDetails(){
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null) {
Object principal = authentication.getPrincipal();
return principal instanceof UserDetails ? (UserDetails) principal : null;
}
return null;
}

Is UserDetails or principal really stored in Session?

Yes, each execution thread has an associated SecurityContext context. You can retrieve the authentication / user details by using SecurityContextHolder like this:

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

Here's a good tutorial about this: http://www.baeldung.com/get-user-in-spring-security

Can SecurityContextHolder Shared across users

By default, the implementation of SecurityContextHolder is bound to an instance of ThreadLocal:

  • It is scalable as much as your container provides simultaneous threads to Spring Security.
  • The abstraction is not an implementation of HttpSession; however, it provides integration with the underlying HTTP Servlet provider to transfer HTTP security information to the application layer managed by Spring.
  • The composition over ThreadLocal enables the user of API to be able take advantage of working with multiple views of data in different threads. That's also why if you have proper thread configurations in your HTTP container, you should not have a scalability issue in terms of number of users.

Put user in HttpSession with Spring Security default login and authenticate

Assuming you wanted to add user to session on seccessful login, You can create the AuthenticationSuccessHandler like below and register using successHandler(new AuthenticationSuccessHandlerImpl())

Update:
If we create the object AuthenticationSuccessHandlerImpl, it will not be spring mananged and hence autowire into your Securityconfig and use it like shown below.

Here autowire the AuthenticationSuccessHandler in your WebSecurityConfig

@Autowired
AuthenticationSuccessHandler authenticationSuccessHandler;

and use it
WebSecurityConfig.java

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/registration").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll().successHandler(authenticationSuccessHandler) // See here
.and()
.logout()
.permitAll();
}

The AuthenticationSuccessHandlerImpl.java

import java.io.IOException;
import java.security.Principal;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import com.techdisqus.auth.repository.UserRepository;

@Component
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler{

@Autowired HttpSession session; //autowiring session

@Autowired UserRepository repository; //autowire the user repo

private static final Logger logger = LoggerFactory.getLogger(AuthenticationSuccessHandlerImpl.class);
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// TODO Auto-generated method stub
String userName = "";
if(authentication.getPrincipal() instanceof Principal) {
userName = ((Principal)authentication.getPrincipal()).getName();

}else {
userName = ((User)authentication.getPrincipal()).getUsername();
}
logger.info("userName: " + userName);
//HttpSession session = request.getSession();
session.setAttribute("userId", userName);

}

}

Hope this helps.

Is SecurityContextHolder necessary

SecurityContextHolder holds the authentication information in a ThreadLocal context (by default). It is a fundamental helper class to Spring Security, which provides access to the security context. You (and the framework) can access to the authentication in every methods run in the same thread. It is necessery even if you protect urls or use method security, but sometimes you need user information in business logic too and you can get it from there.
So the answer is yes, it is always needed, if you use spring security.



Related Topics



Leave a reply



Submit