I am currently studying Spring Security Fundamentals. I was reading documentation and source code as I like to get as much detailed information about what happens under the hood as possible. Everything is somewhat understandable except one thing:
SecurityContextPersistenceFilter
Filter class is first filter of Spring Security according to this page The implementation of this class's doFilter()
method looks like this:
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request.getAttribute("__spring_security_scpf_applied") != null) {
chain.doFilter(request, response);
} else {
request.setAttribute("__spring_security_scpf_applied", Boolean.TRUE);
if (this.forceEagerSessionCreation) {
HttpSession session = request.getSession();
if (this.logger.isDebugEnabled() && session.isNew()) {
this.logger.debug(LogMessage.format("Created session %s eagerly", session.getId()));
}
}
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
boolean var10 = false;
try {
var10 = true;
SecurityContextHolder.setContext(contextBeforeChainExecution);
if (contextBeforeChainExecution.getAuthentication() == null) {
this.logger.debug("Set SecurityContextHolder to empty SecurityContext");
} else if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", contextBeforeChainExecution));
}
chain.doFilter(holder.getRequest(), holder.getResponse());
var10 = false;
} finally {
if (var10) {
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
SecurityContextHolder.clearContext();
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute("__spring_security_scpf_applied");
this.logger.debug("Cleared SecurityContextHolder to complete request");
}
}
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
SecurityContextHolder.clearContext();
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute("__spring_security_scpf_applied");
this.logger.debug("Cleared SecurityContextHolder to complete request");
}
}
The question I have is this: obviously, after filter chain has done its job, SecurityContextHolder.clearContext()
method is called either in finally block or at the end of the method. However I know that, I can still access SecurityContext object with SecurityContextHolder.getContext()
in my controller and service layer. How is it possible if above code clears SecurityContextHolder? Am I missing something?
First of all, the documentation you are referring to is version 4.1.2 of spring security, which was released 21 december 2016. Spring security is currently on 5.6.1 and the filter list looks a lot different in the current documentation.
Then the answer to your question in the above code is, because of this line
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
The SecurityContextHolder
is a singleton, so the SecurityContext
is fetched (which will create a copy) then it is cleared in the singleton
and lastly this.repo.save
saves the context for the next time.
How this is saved, is different, per default believe it uses HttpSession (which means you will get a session cookie), which is implemented in Spring in the HttpSessionSecurityContextRepository, but you can set up a separate database for it to be persistently saved if you'd like it, so that it still remains after restarts etc.