title: Spring Security - custom filter
body: <p>I'm using this environment:</p>
  
  <ul>
  <li>spring 4.0.5 </li>
  <li>spring security 3.2.4</li>
  </ul>
  
  <p>In my environment I have a SSO system and I need to integrate my web applicationin this system. This system is a private product. The final result of the SSO mechanism is to put a parameter in the request header. So what I should do in my application is:</p>
  
  <ul>
  <li>check if a header parameter is present in the request</li>
  <li>if the header parameter is not present, I have to redirect to the
  login page of this system</li>
  <li>if the header parameter is present, I have to authenticate on the
  Spring security context</li>
  </ul>
  
  <p>This scenario is similar to the CAS integration scenario; so I started from CAS integration; I wrote my custom filter, I wrote my custom entry point and all other classes needed to handle the requests, but I'm facing some issues; now I can authenticate in spring context but, also after authenticated, it's like if I'm not authenticated. Maybe I'm missing something.</p>
  
  <p>I'll report all involved classes and my security context.</p>
  
  <p><strong>security-context.xml</strong></p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:security="http://www.springframework.org/schema/security"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"&gt;
          &lt;!-- Nessuna sicurezza per avere i CSS e JS --&gt;
          &lt;security:http pattern="/css/**" security="none"/&gt;
          &lt;security:http pattern="/DataTables-1.9.4/**" security="none"/&gt;
          &lt;security:http pattern="/images/**" security="none"/&gt;
          &lt;security:http pattern="/jquery-ui-1.10.3/**" security="none"/&gt;
          &lt;security:http pattern="/json/**" security="none"/&gt;
          &lt;security:http pattern="/jstree-v.pre1.0/**" security="none"/&gt;
          &lt;security:http pattern="/ol213/**" security="none"/&gt;
          &lt;security:http pattern="/sld/**" security="none"/&gt;
          &lt;!-- Configurazione sicurezza --&gt;
          &lt;security:http use-expressions="true" disable-url-rewriting="true" 
          entry-point-ref="webSealEntryPoint" request-matcher-ref="webSealReqMatcher"&gt;
              &lt;security:custom-filter ref="webSealAuthFilter" position="FIRST"/&gt;
              &lt;security:intercept-url pattern="/comiajax/pages/*" access="hasAnyRole('OMNIA_ADMIN','DOMAIN_ADMIN','OMNIA_GUEST', 'DOMAIN_GUEST')"/&gt;
              &lt;security:session-management&gt;
                  &lt;security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1"/&gt;
              &lt;/security:session-management&gt;
          &lt;/security:http&gt;
          &lt;security:authentication-manager alias="authenticationManager"&gt;
              &lt;security:authentication-provider ref="webSealAuthProvider" /&gt;
          &lt;/security:authentication-manager&gt;
      &lt;bean id="webSealAuthFilter" class="it.angelo.spring.web.security.auth.filter.WebSealAuthenticationFilter"&gt;
          &lt;constructor-arg ref="webSealReqMatcher"/&gt;
          &lt;property name="authenticationManager" ref="authenticationManager"/&gt;
          &lt;property name="authenticationDetailsSource"&gt;
              &lt;bean class="it.angelo.spring.web.security.auth.ServiceAuthenticationDetailsSource"/&gt;
          &lt;/property&gt;
      &lt;/bean&gt;
      &lt;bean id="webSealEntryPoint" class="it.angelo.spring.web.security.entrypoint.WebSealEntryPoint"&gt;
          &lt;property name="usernameHeaderParam" value="iv-user" /&gt;
      &lt;/bean&gt;
      &lt;bean id="webSealAuthProvider" class="it.angelo.spring.web.security.auth.providers.WcmSealAuthProvider"&gt;
          &lt;property name="authenticationUserDetailsService" ref="wcmSecurityUserSvc"/&gt;
      &lt;/bean&gt;
      &lt;security:user-service id="userService"&gt;
          &lt;security:user name="angelo" password="angelo" authorities="OMNIA_ADMIN,"/&gt;
          &lt;security:user name="gina" password="gina" authorities="DOMAIN_ADMIN"/&gt;
          &lt;security:user name="costa" password="costa" authorities="OMNIA_GUEST"/&gt;
          &lt;security:user name="marianna" password="marianna" authorities="DOMAIN_GUEST"/&gt;
      &lt;/security:user-service&gt;
      &lt;bean id="webSealReqMatcher" class="it.angelo.spring.web.security.auth.requestmatchers.WebSealRequestMatcher"/&gt;
      &lt;bean id="wcmSecurityUserSvc" class="it.angelo.spring.web.security.userdetails.GrantedFromIvUsernameAttributesUserDetailsService"/&gt;
  &lt;/beans&gt;
  </code></pre>
  
  <p><strong>My custom filter</strong></p>
  
  <pre><code>public class WebSealAuthenticationFilter extends AbstractAuthenticationProcessingFilter
  {
      protected WebSealAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher)
      {
          super(requiresAuthenticationRequestMatcher);
      }
  
      private static final Log logger = LogFactory.getLog(WebSealAuthenticationFilter.class.getName());
  
      @Override
      public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException
      {
          WebSealIvUserNameAuthenticationToken auth = new WebSealIvUserNameAuthenticationToken(request.getHeader(WebSealRequestMatcher.DEFAULT_HEADER_PARAM));
          auth.setDetails(this.authenticationDetailsSource.buildDetails(request));
          return this.getAuthenticationManager().authenticate(auth);
      }
  
      @Override
      protected final void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException
      {
          if (logger.isDebugEnabled()) {
              logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
          }
  
          SecurityContextHolder.getContext().setAuthentication(authResult);
  
          // Propaga l'evento
          if (this.eventPublisher != null) {
              eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
          }
          request.getSession().setAttribute("autenticato", new Boolean(true));
          chain.doFilter(request, response);
      }
  }
  </code></pre>
  
  <p><strong>My request matcher</strong> (I know i can use the org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher class and in future I'll use it)</p>
  
  <pre><code>public class WebSealRequestMatcher implements RequestMatcher
  {
      public static final String DEFAULT_HEADER_PARAM = "iv-user";
      @Override
      public boolean matches(HttpServletRequest request)
      {
          String headerParam = request.getHeader(DEFAULT_HEADER_PARAM);
          return !StringUtils.hasText(headerParam);
      }   
  }
  </code></pre>
  
  <p><strong>My AuthenticationProvider</strong></p>
  
  <pre><code>public class WcmSealAuthProvider implements AuthenticationProvider
  {
      private static final Log logger = LogFactory.getLog(WcmSealAuthProvider.class.getName());
      private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
      private AuthenticationUserDetailsService&lt;WebSealIvUserNameAuthenticationToken&gt; authenticationUserDetailsService;
      public void setAuthenticationUserDetailsService(final AuthenticationUserDetailsService&lt;WebSealIvUserNameAuthenticationToken&gt; svc)
      {
          this.authenticationUserDetailsService = svc;
      }
  
      public AuthenticationUserDetailsService&lt;WebSealIvUserNameAuthenticationToken&gt; getAuthenticationUserDetailsService()
      {
          return authenticationUserDetailsService;
      }
      @Override
      public Authentication authenticate(Authentication authentication) throws AuthenticationException
      {
          if( !supports(authentication.getClass()) )
          {
              if( logger.isInfoEnabled() )
              {
                  logger.info("Classe "+authentication.getClass().getName()+" non supportata");
              }
              return null;
          }
          if( authentication instanceof WebSealIvUserNameAuthenticationToken )
          {
              return doAuthenticationNow(authentication);
          }
          return null;
      }
  
      @Override
      public boolean supports(Class&lt;?&gt; authentication)
      {
          return WebSealIvUserNameAuthenticationToken.class.isAssignableFrom(authentication);
      }
      private WebSealAuthenticationToken doAuthenticationNow(final Authentication authentication)
      {
  
          try {
              final String ivUserName = ((WebSealIvUserNameAuthenticationToken)authentication).getIvUserName();
              UserDetails userDetails = loadUserByIvUserName(ivUserName);
  
              return new WebSealAuthenticationToken(userDetails, userDetails.getAuthorities(), 
                      this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, userDetails.getUsername());
          } catch (final Exception e) {
              throw new BadCredentialsException(e.getMessage(), e);
          }
      }
  
      protected UserDetails loadUserByIvUserName( final String ivUserName )
      {
          return this.authenticationUserDetailsService.loadUserDetails(new WebSealIvUserNameAuthenticationToken(ivUserName));
      }
  }
  </code></pre>
  
  <p><strong>My AuthenticationTokens</strong></p>
  
  <pre><code>public class WebSealIvUserNameAuthenticationToken extends AbstractAuthenticationToken implements Serializable
  {
  
      private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
      private String ivUserName;
      public WebSealIvUserNameAuthenticationToken(String ivUserName)
      {
          super(new ArrayList&lt;GrantedAuthority&gt;());
          this.ivUserName = ivUserName;
      }
      @Override
      public Object getCredentials()
      {
          return null;
      }
  
      @Override
      public Object getPrincipal()
      {
          return null;
      }
      public String getIvUserName()
      {
          return ivUserName;
      }
  }
  
  public final class WebSealAuthenticationToken extends AbstractAuthenticationToken implements Serializable
  {
  
      private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
      private final Object credentials;
      private final Object principal;
      private final UserDetails userDetails;
  
      private String ivUserName;
      public WebSealAuthenticationToken(  final Object principal, 
                                          final Object credentials,
                                          final Collection&lt;? extends GrantedAuthority&gt; authorities, 
                                          final UserDetails userDetails,
                                          final String ivUserName)
      {
          super(authorities);
          if (((principal == null) || "".equals(principal) || (credentials == null)
                  || "".equals(credentials) || (authorities == null) || (userDetails == null) 
                  || !StringUtils.hasText(ivUserName))) {
  
                  throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
              }
          this.principal = principal;
          this.credentials = credentials;
          this.userDetails = userDetails;
          this.ivUserName = ivUserName;
          setAuthenticated(true);
      }
      @Override
      public Object getCredentials()
      {
          return this.credentials;
      }
  
      @Override
      public Object getPrincipal()
      {
          return this.principal;
      }
      public UserDetails getUserDetails() 
      {
          return userDetails;
      }
  
      public String getIvUserName()
      {
          return ivUserName;
      }
      @Override
      public boolean equals(final Object obj) {
          if (!super.equals(obj)) {
              return false;
          }
  
          if (obj instanceof WebSealAuthenticationToken) {
              WebSealAuthenticationToken test = (WebSealAuthenticationToken) obj;
  
              if (!this.ivUserName.equals(test.getIvUserName())) {
                  return false;
              }
              return true;
          }
  
          return false;
      }
  }
  
  My AuthenticationUserDetailsService
  
  public abstract class AbstractUserDetailsService implements AuthenticationUserDetailsService&lt;WebSealIvUserNameAuthenticationToken&gt;
  {
  
      @Override
      public UserDetails loadUserDetails(final WebSealIvUserNameAuthenticationToken token) throws UsernameNotFoundException
      {
          return loadUserDetails(token.getIvUserName());
      }
      protected abstract UserDetails loadUserDetails(String ivUserName);
  }
  
  public class GrantedFromIvUsernameAttributesUserDetailsService extends AbstractUserDetailsService
  {
      private static final String NON_EXISTENT_PASSWORD_VALUE = "NO_PASSWORD";
      @Override
      protected UserDetails loadUserDetails(String ivUserName)
      {
          // TODO Inserire il codice per recuperare l'utente da PUMA
          final List&lt;GrantedAuthority&gt; grantedAuthorities = new ArrayList&lt;GrantedAuthority&gt;();
          grantedAuthorities.add(new SimpleGrantedAuthority("OMNIA_ADMIN"));
          grantedAuthorities.add(new SimpleGrantedAuthority("DOMAIN_ADMIN"));
          grantedAuthorities.add(new SimpleGrantedAuthority("OMNIA_GUEST"));
          grantedAuthorities.add(new SimpleGrantedAuthority("DOMAIN_GUEST"));
          return new User("fromWebSeal", NON_EXISTENT_PASSWORD_VALUE, true, true, true, true, grantedAuthorities);
      }
  
  }
  </code></pre>
  
  <p><strong>My AuthenticationDetailsSource</strong></p>
  
  <pre><code>public class ServiceAuthenticationDetailsSource implements AuthenticationDetailsSource&lt;HttpServletRequest, ServiceAuthenticationDetails&gt;
  {
  
      @Override
      public ServiceAuthenticationDetails buildDetails(HttpServletRequest request)
      {
          return new DefaultServiceAuthenticationDetails(request);
      }
  
  }
  
  public class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails implements ServiceAuthenticationDetails
  {
      private static final long serialVersionUID = 6056034551086630421L;
      private String ivUserName;
      public DefaultServiceAuthenticationDetails(HttpServletRequest request)
      {
          super(request);
          String headerParam = request.getHeader(WebSealRequestMatcher.DEFAULT_HEADER_PARAM);
          this.ivUserName = StringUtils.hasText(headerParam) ? headerParam : "nonValorizzato"+System.nanoTime();
      }
      @Override
      public String getIvUserName()
      {
          return ivUserName;
      }
      @Override
      public int hashCode() {
          final int prime = 31;
          int result = super.hashCode();
          result = prime * result + ivUserName.hashCode();
          return result;
      }
      @Override
      public boolean equals(Object obj) {
          if (this == obj) {
              return true;
          }
          if (!super.equals(obj) || !(obj instanceof DefaultServiceAuthenticationDetails)) {
              return false;
          }
          ServiceAuthenticationDetails that = (ServiceAuthenticationDetails) obj;
          return ivUserName.equals(that.getIvUserName());
      }
  }
  public interface ServiceAuthenticationDetails extends Serializable
  {
  
      public String getIvUserName();
  }
  </code></pre>
  
  <p>It seems to me to be all correct; anyway when I try to access to the page <code>http://myHost:myPort/media/comiajax/pages/uploadmedia</code> I have a loop and the page can't be loaded.....
  Can anybod help me? </p>
  
  <p>Any tips is welcome</p>
  
  <p>Thank you</p>
  
  <p>Angelo</p>