spring-bootspring-securitydigestdigest-authentication

Digest authentication with spring security: 401 recieved as expected but with two WWW-Authenticate headers


When I send PUT request with correct username and password, it works fine. But when I send request with wrong password, I received 401 which is ok, but in I got 2 WWW-Authenticate headers:

Response headers: HTTP/1.1 401

WWW-Authenticate: Digest realm="NOKIA.COM", qop="auth", nonce="MTU1MjM3MDk2MDQ2MjpmOWNjYjVmNGU5ODA0ZmY0YWY0MjIxNDlhY2U2ODJiMQ=="

X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Cache-Control: no-cache, no-store, max-age=0, must-revalidate Pragma: no-cache Expires: 0 X-Frame-Options: DENY

WWW-Authenticate: Digest realm="NOKIA.COM", qop="auth", nonce="MTU1MjM3MDk2MDQ2NjoxOTQ4MDhjNzBjYjkyMGI1Y2Q2YjU3OGMyMTM2NmE3OQ=="

Content-Length: 0 Date: Tue, 12 Mar 2019 06:08:20 GMT

@EnableWebSecurity

@Configuration @Component public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
DummyUserService userDetail;

@Autowired
DigestAuthenticationFilter digestFilter;

@Autowired
DigestAuthenticationEntryPoint digestEntryPoint;

@Override
protected void configure( HttpSecurity http ) throws Exception
{        

    http.addFilter(digestFilter)              // register digest entry point
    .exceptionHandling().authenticationEntryPoint(digestEntryPoint)     // on exception ask for digest authentication
    .and()
    .authorizeRequests()
    .anyRequest().authenticated()
    .and().csrf().disable();

    http.httpBasic().disable();

}

@Bean
public PasswordEncoder passwordEncoder() {
    return new PasswordEncoder() {
        @Override
        public String encode(CharSequence rawPassword) {
            return rawPassword.toString();
        }
        @Override
        public boolean matches(CharSequence rawPassword, String encodedPassword) {
            return rawPassword.toString().equals(encodedPassword);
        }
    };
}

}

    @Bean
DigestAuthenticationFilter digestFilter( DigestAuthenticationEntryPoint digestAuthenticationEntryPoint,
                                         UserCache digestUserCache, UserDetailsService userDetailsService )
{
    DigestAuthenticationFilter filter = new DigestAuthenticationFilter();
    filter.setAuthenticationEntryPoint( digestAuthenticationEntryPoint );
    filter.setUserDetailsService( userDetailsService );
    filter.setUserCache( digestUserCache );
    return filter;
}

@Bean
UserCache digestUserCache() throws Exception
{
    return new SpringCacheBasedUserCache( new ConcurrentMapCache( "digestUserCache" ) );
}

@Bean
DigestAuthenticationEntryPoint digestAuthenticationEntry()
{
    DigestAuthenticationEntryPoint digestAuthenticationEntry = new DigestAuthenticationEntryPoint();
    digestAuthenticationEntry.setRealmName( "XXX.COM" );
    digestAuthenticationEntry.setKey( "XXX" );
    digestAuthenticationEntry.setNonceValiditySeconds( 60 );
    return digestAuthenticationEntry;
}

Please someone can give me some help. Many thanks!


Solution

  • I solved this problem by myself. For request with incorrect auth, DigestAuthenticationEntryPoint was called twice by both digestFilter and exceptionFilter.

    Overwrite DigestAuthenticationEntryPoint:

    public class CustomDigestAuthenticationEntryPoint extends DigestAuthenticationEntryPoint
    {
        @Override
        public void commence( HttpServletRequest request, HttpServletResponse response,
                              AuthenticationException authException )
            throws IOException, ServletException
        {
            HttpServletResponse httpResponse = ( HttpServletResponse ) response;
            String authHeader = httpResponse.getHeader( "WWW-Authenticate" );
            if( authHeader != null )
            {
                httpResponse.sendError( HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase() );
            }
            else
            {
                super.commence( request, httpResponse, authException );
            }
        }
    }