2

I am trying to put in place an authentication based on a custom token in the request header.

I have read the accepted answer of this question and created a custom token, filter and authentication provider.

Problem :

When I try to "GET /login" :

  • The filter is called
  • The token is created
  • The authenticationProvider is not called. Even its method supports is not called!

In the browser console, I can see 2 HTTP 302 calls to /login.

Any idea?

EDIT : Actually, the authentication provider is ignored only if I call the endpoint from angular (resulting in an AJAX/XHR call). If I call the endpoint from Postman, the authentication provider is called.

EDIT : Spring security DEBUG logs :

2016-10-27 19:57:46.724 DEBUG 9752 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 1 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2016-10-27 19:57:46.725 DEBUG 9752 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 2 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2016-10-27 19:57:46.725 DEBUG 9752 --- [nio-8080-exec-3] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2016-10-27 19:57:46.725 DEBUG 9752 --- [nio-8080-exec-3] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2016-10-27 19:57:46.725 DEBUG 9752 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2016-10-27 19:57:46.725 DEBUG 9752 --- [nio-8080-exec-3] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@43bffae5
2016-10-27 19:57:46.725 DEBUG 9752 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 4 of 10 in additional filter chain; firing Filter: 'LogoutFilter'
2016-10-27 19:57:46.725 DEBUG 9752 --- [nio-8080-exec-3] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login'; against '/logout'
2016-10-27 19:57:46.725 DEBUG 9752 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : /login at position 5 of 10 in additional filter chain; firing Filter: 'MyFilter'
2016-10-27 19:57:46.726 DEBUG 9752 --- [nio-8080-exec-3] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2016-10-27 19:57:46.726 DEBUG 9752 --- [nio-8080-exec-3] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2016-10-27 19:57:46.768 DEBUG 9752 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy        : /login at position 1 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2016-10-27 19:57:46.769 DEBUG 9752 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy        : /login at position 2 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2016-10-27 19:57:46.769 DEBUG 9752 --- [nio-8080-exec-4] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2016-10-27 19:57:46.770 DEBUG 9752 --- [nio-8080-exec-4] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2016-10-27 19:57:46.770 DEBUG 9752 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy        : /login at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2016-10-27 19:57:46.771 DEBUG 9752 --- [nio-8080-exec-4] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@43bffae5
2016-10-27 19:57:46.771 DEBUG 9752 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy        : /login at position 4 of 10 in additional filter chain; firing Filter: 'LogoutFilter'
2016-10-27 19:57:46.771 DEBUG 9752 --- [nio-8080-exec-4] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login'; against '/logout'
2016-10-27 19:57:46.771 DEBUG 9752 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy        : /login at position 5 of 10 in additional filter chain; firing Filter: 'MyFilter'
2016-10-27 19:57:46.772 DEBUG 9752 --- [nio-8080-exec-4] w.c.HttpSessionSecurityContextRepository : HttpSession being created as SecurityContext is non-default
2016-10-27 19:57:46.774 DEBUG 9752 --- [nio-8080-exec-4] w.c.HttpSessionSecurityContextRepository : SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl@17085d: Authentication: com.mycompany.myapp.configuration.MyToken@17085d: Principal: null; Credentials: [PROTECTED]; Authenticated: false; Details: null; Not granted any authorities'
2016-10-27 19:57:46.774 DEBUG 9752 --- [nio-8080-exec-4] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

Token :

public class MyToken extends AbstractAuthenticationToken {

    private String token;

    public MyToken(String token) {
        super(null);
        this.token = token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }

    @Override
    public Object getPrincipal() {
        return null;
    }
}

Filter :

public class MyFilter extends AbstractAuthenticationProcessingFilter {

    public MyFilter() {
        super("/login");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        String x_token = request.getHeader("x_token");
        String method = request.getMethod();
        if(x_token != null && method.equals("GET")) {
            return new MyToken(x_token);
        }
        return null;
    }
}

Authentication provider :

@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        MyToken token = (MyToken) authentication;
        if(token.getCredentials() != null) {
            token.setAuthenticated(true);
            return token;
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(MyToken.class);
    }
}

And finally, the security configuration :

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;

    @Override
    protected void configure( HttpSecurity http ) throws Exception {
        // configure filters
        http.addFilterBefore( new MyFilter(), UsernamePasswordAuthenticationFilter.class );

        // configure authentication providers
        http.authenticationProvider(myAuthenticationProvider);

        // disable csrf
        http.csrf().disable();

        // setup security
        http.authorizeRequests()
                .anyRequest()
                .fullyAuthenticated()
                .and().httpBasic();
    }
}
Community
  • 1
  • 1
Arnaud Denoyelle
  • 29,980
  • 16
  • 92
  • 148
  • @dur Added spring-security DEBUG logs. Also, I found out that if I call the endpoint with a regular request instead of XHR, the authentication provider is called. – Arnaud Denoyelle Oct 27 '16 at 18:02
  • @dur Did not know about this class and do not have it. I tried to declare one but it is not called. I looked at the following question and found that the accepted answer consists in building an `AuthenticationManager` by giving a list of `AuthenticationProvider`. It seems to be the same thing as giving an `AuthenticationProvider` to `HttpSecurity`, isn't it? http://stackoverflow.com/questions/31826233/custom-authentication-manager-with-spring-security-and-java-configuration – Arnaud Denoyelle Oct 27 '16 at 18:35

1 Answers1

0

You have to call the AuthenticationManagerin your custom subclass of AbstractAuthenticationProcessingFilter, see Spring Security Reference:

The filter calls the configured AuthenticationManager to process each authentication request.

See also AbstractAuthenticationProcessingFilter#attemptAuthentication:

Performs actual authentication. The implementation should do one of the following:

  1. Return a populated authentication token for the authenticated user, indicating successful authentication
  2. Return null, indicating that the authentication process is still in progress. Before returning, the implementation should perform any additional work required to complete the process.
  3. Throw an AuthenticationException if the authentication process fails

Your implementation doesn't return an authenticated token, see logs:

SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl@17085d: Authentication: com.mycompany.myapp.configuration.MyToken@17085d: Principal: null; Credentials: [PROTECTED]; Authenticated: false; Details: null; Not granted any authorities'

See also:

dur
  • 15,689
  • 25
  • 79
  • 125