22

I recently started learning about Spring Security and today I stepped on this basic (I believe) question: Why can't I access the current Principal inside a Servlet Filter as demonstrated in the class below:

package com.acme.test;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

@Component
public class TestFilter implements Filter {

    /*
     * (non-Javadoc)
     * 
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
     * javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        SecurityContext securityContext = SecurityContextHolder.getContext();
        Authentication auth = securityContext.getAuthentication();

        // auth is null here

        chain.doFilter(request, response);
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.servlet.Filter#destroy()
     */
    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}

The Authentication object retrieved with Authentication auth = securityContext.getAuthentication(); is null. While using the above snippet inside an MVC @Controller works just fine (as expected).

Why is this happening?

dimi
  • 1,496
  • 2
  • 15
  • 27
  • I would look at spring security filter chain, to override the default spring configuration and insert the filter at the right position. The Authentication might only be available only in a certain position http://docs.spring.io/spring-security/site/docs/3.1.x/reference/security-filter-chain.html – jpprade Sep 30 '14 at 20:46
  • @jpprade thanks. I'm mostly interested in generic Filters, so I'd prefer not to override a security Filter. It seems (from the answers below) that configuring the Filter's order works. – dimi Oct 01 '14 at 08:30

5 Answers5

29

inside doFilter:

HttpServletRequest request = (HttpServletRequest) request;
HttpSession session = request.getSession(false);

SecurityContextImpl sci = (SecurityContextImpl) session.getAttribute("SPRING_SECURITY_CONTEXT");

if (sci != null) {
        UserDetails cud = (UserDetails) sci.getAuthentication().getPrincipal();
        // do whatever you need here with the UserDetails
}

Hope this helps

felix
  • 9,007
  • 7
  • 41
  • 62
Vipul Paralikar
  • 1,508
  • 10
  • 22
  • That worked indeed...@mordechai-tamam 's solution also worked. Could you please explain what's happening here...I am mostly interested in understanding what's going on. – dimi Oct 01 '14 at 08:19
  • HttpSessionSecurityContextRepository stores the security context in the HttpSession between requests. And for each request it is put in a ThreadLocal from where you access it. Here there is nice write up by @Ralph: http://stackoverflow.com/questions/6408007/spring-securitys-securitycontextholder-session-or-request-bound – Vipul Paralikar Oct 01 '14 at 09:15
  • Thanks for the info Vipul..interesting stuff. Still, this doesn't fully explain why SecurityContextHolder.getContext(); doesn't get a reference to a SecurityContext that contains the current User. Is this because my Filter runs prior to the SecurityContextPersistenceFilter? – dimi Oct 01 '14 at 13:18
  • I think I got it now...followed all Filter invocations and saw the call to SecurityContextHolder#clearContext() by SecurityContextPersistenceFilter after all filters in the chain run. I am going to accept this answer as this is what I am going to use. Though the answers below helped a lot in understanding what's going on. – dimi Oct 01 '14 at 17:51
16

The following snippet works and provides a Principal instance:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;

    Principal principal = req.getUserPrincipal();

    if (principal != null) {
        // do something with the Principal
    }

    chain.doFilter(request,  response);
}
Shimakaze
  • 173
  • 1
  • 4
5
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null) {
  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;
Ariel
  • 25,995
  • 5
  • 59
  • 69
3

You can see here, that in order to access the SecurityContext, the security filter must come first.

If you are asking how to do it, it depends on the way you configured your web application. In my case, I'm using Spring-Boot , based on Servlet-3 configuration style, and Spring context configuration in Java (no XML) So, my configuration looks like this:

@Configuration
@EnableWebMvc
@EnableWebMvcSecurity
public class WebCtxConfig extends WebMvcConfigurerAdapter {

    @Autowired
    ApplicationContext ctx;


    @Bean
    FilterRegistrationBean springSecurityFilter() {
        FilterChainProxy o = (FilterChainProxy) ctx
                .getBean(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
        FilterRegistrationBean trVal = new FilterRegistrationBean();
        trVal.setFilter(o);
        trVal.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE + 1);
        return trVal;
    }

    @Bean
    public FilterRegistrationBean applicationContextIdFilter(final IThreadLifecycleManager threadLifecycleManager) {
        FilterRegistrationBean retVal = new FilterRegistrationBean();
        YourFilter filter = new YourFilter();
        retVal.setFilter(filter);
        retVal.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE + 2);
        return retVal;
    }
}

Note that by setting the order, you are able to controll the filters order.

Modi
  • 2,200
  • 4
  • 23
  • 37
  • This worked and looks good. Wondering why @vipul-paralikar 's snippet also worked. Which is the right approach? – dimi Oct 01 '14 at 08:23
  • My answer, demonstrates the way you can order your filters, so that you are able to place your additional filters before/after the FilterChainProxy. The point is that before the FilterChainProxy was invoked(one of the chains is actually doing it), the security context is empty. So, my solution fits in case that your filter is not related to any action related to the authentication and still, relies on the fact that a user is already authenticated. – Modi Oct 02 '14 at 04:19
2

The good old web.xml deployment descriptor is an easy way to determine the order of filters. From the Servlet 3.0 specification : The order the container uses in building the chain of filters to be applied for a particular request URI is as follows:

  1. First, the matching filter mappings in the same order that these elements appear in the deployment descriptor.
  2. Next, the matching filter mappings in the same order that these elements appear in the deployment descriptor.

In short, you must put the <filter-mapping> for you filter after the one for Spring security.

Also from the same specification document : If the order in which the listeners, servlets, filters are invoked is important to an application then a deployment descriptor must be used.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thanks; I am not using XML configuration, but your answer helps understand the significance of the filter ordering. – dimi Oct 01 '14 at 13:33