0

I am trying to set spring security for my application. It has multiple entry points which are following,

1. /odk/**
- All routes prefix with /odk/ should be authenticated with Digest Auth and response accordingly
2. /odkx/**
- All routes prefix with /odkx/ should also authenticate with Digest Auth and response accordingly
3. /api/**
- Al routes prefix with /api/ should authenticated with JWT Authentication
4. All other routes
- All routes that does not match any above should be authenticated with login form authentication of spring security.

I have wrote the code also added @order for each entry point to be executed accordingly. My problem is that all filters are executed while the request should be handled only by relevant entry point.

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
        web.ignoring().antMatchers("/css/**");
        web.ignoring().antMatchers("/jsp/**");
    }

    @Configuration
    @Order(1)
    public static class DigestAuthSecurityConfiguration extends WebSecurityConfigurerAdapter {

        @Autowired
        private CustomDigestUserService customDigestUserService;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.cors().and().csrf().disable();

            http.antMatcher("/odk/**")
                .authorizeRequests().antMatchers("/odk/**")
                .fullyAuthenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilter(digestAuthFilter())BasicAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(digestEntryPoint());
        }


        DigestAuthenticationFilter digestAuthFilter() throws Exception {
            DigestAuthenticationFilter digestAuthenticationFilter = new DigestAuthenticationFilter();

            digestAuthenticationFilter.setUserDetailsService(customDigestUserService);
            digestAuthenticationFilter.setAuthenticationEntryPoint(digestEntryPoint());
            digestAuthenticationFilter.setPasswordAlreadyEncoded(false);
            return digestAuthenticationFilter;
        }


        DigestAuthenticationEntryPoint digestEntryPoint() {
            DigestAuthenticationEntryPoint bauth = new DigestAuthenticationEntryPoint();
            bauth.setRealmName("Digest ASIMS");
            bauth.setKey("MySecureKey");
            return bauth;
        }

    }

    @Configuration
    @Order(2)
    public static class DigestAuthSecurityConfigurationODKX extends WebSecurityConfigurerAdapter {

        @Autowired
        private CustomDigestUserService customDigestUserService;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.cors().and().csrf().disable();

            http.antMatcher("/odkx/**")
                .authorizeRequests().antMatchers("/odkx/**")
                .fullyAuthenticated()
                .and()
              .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilter(digestAuthFilterODKX())BasicAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(digestEntryPointODKX());
        }

        DigestAuthenticationFilter digestAuthFilterODKX() throws Exception {
            DigestAuthenticationFilter digestAuthenticationFilter = new DigestAuthenticationFilter();

            digestAuthenticationFilter.setUserDetailsService(customDigestUserService);
            digestAuthenticationFilter.setAuthenticationEntryPoint(digestEntryPointODKX());
            digestAuthenticationFilter.setPasswordAlreadyEncoded(false);
            return digestAuthenticationFilter;
        }

        DigestAuthenticationEntryPoint digestEntryPointODKX() {
            DigestAuthenticationEntryPoint bauth = new DigestAuthenticationEntryPoint();
            bauth.setRealmName("Digest ASIMS");
            bauth.setKey("MySecureKey");
            return bauth;
        }

        @Override
        public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
            authenticationManagerBuilder.userDetailsService(customDigestUserService);
        }

    }

    @Configuration
    @Order(3)
    public static class JwtAuthSecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Autowired
        private CustomUserService customUserService;

        @Autowired
        private JwtAuthenticationEntryPoint unauthorizedHandler;

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

            http.cors().and().csrf().disable();
            http.antMatcher("/api/**")
                    .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                    .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

            // Add our custom JWT security filter
            http.addFilterBefore(jwtAuthenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
        }

        @Autowired
        public JwtAuthenticationFilter jwtAuthenticationTokenFilterBean() throws Exception {
            return new JwtAuthenticationFilter();
        }

        @Override
        public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
            authenticationManagerBuilder.userDetailsService(customUserService).passwordEncoder(jwtPasswordEncoder());
        }

        public BCryptPasswordEncoder jwtPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }

        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    }

    @Configuration
    @Order(4)
    public static class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Autowired
        private CustomUserService customUserService;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.cors().and().csrf().disable();
            http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                   .loginPage("/login")
                   .permitAll()
                .and()
                .logout()
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/login");
        }

        public BCryptPasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }

        public DaoAuthenticationProvider authenticationProvider() {
            DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
            auth.setUserDetailsService(customUserService);
            auth.setPasswordEncoder(passwordEncoder());
            auth.setHideUserNotFoundExceptions(false);
            return auth;
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(authenticationProvider());
        }
    }

}

Here is the log

2019-08-12 20:40:05 DEBUG org.springframework.security.web.FilterChainProxy - /odk/formList at position 6 of 12 in additional filter chain; firing Filter: 'DigestAuthenticationFilter'
2019-08-12 20:40:05 DEBUG org.springframework.security.web.authentication.www.DigestAuthenticationFilter - Digest Authorization header received from user agent: Digest username="admin", realm="Digest ASIMS", nonce="MTU2NTYyNjUwNTI1MTplNjFjYjVhODU2ODU3ZmFiNTdmMzI2NGMwZmYyOGE1MQ==", uri="/odk/formList", algorithm="MD5", qop=auth, nc=00000001, cnonce="QLgL7tFi", response="b58667867ba602024aa1c64ffd0c24a5"
2019-08-12 20:40:05 DEBUG org.springframework.security.web.authentication.www.DigestAuthenticationFilter - Extracted username: 'admin'; realm: 'Digest ASIMS'; nonce: 'MTU2NTYyNjUwNTI1MTplNjFjYjVhODU2ODU3ZmFiNTdmMzI2NGMwZmYyOGE1MQ=='; uri: '/odk/formList'; response: 'b58667867ba602024aa1c64ffd0c24a5'
Entry to CustomDigestUserServiceImpl: admin
2019-08-12 20:40:05 DEBUG org.hibernate.SQL - 
2019-08-12 20:40:13 DEBUG org.springframework.security.web.authentication.www.DigestAuthenticationFilter - Authentication success for user: 'admin' with response: 'b58667867ba602024aa1c64ffd0c24a5'
2019-08-12 20:40:31 DEBUG org.springframework.security.web.FilterChainProxy - /odk/formList at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2019-08-12 20:40:31 DEBUG org.springframework.security.web.FilterChainProxy - /odk/formList at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2019-08-12 20:40:31 DEBUG org.springframework.security.web.FilterChainProxy - /odk/formList at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2019-08-12 20:40:31 DEBUG org.springframework.security.web.authentication.AnonymousAuthenticationFilter - SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ac07c509: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: VIEW,EDIT,DELETE; Credentials: [PROTECTED]; Authenticated: false; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Not granted any authorities'
2019-08-12 20:40:31 DEBUG org.springframework.security.web.FilterChainProxy - /odk/formList at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
2019-08-12 20:40:31 DEBUG org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy - Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@2a54a6c3
2019-08-12 20:40:31 DEBUG org.springframework.security.web.FilterChainProxy - /odk/formList at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2019-08-12 20:40:31 DEBUG org.springframework.security.web.FilterChainProxy - /odk/formList at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2019-08-12 20:40:31 DEBUG org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/odk/formList'; against '/odk/**'
2019-08-12 20:40:31 DEBUG org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /odk/formList; Attributes: [fullyAuthenticated]
2019-08-12 20:40:31 DEBUG org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
Entry to CustomUserService: admin
2019-08-12 20:40:31 DEBUG org.hibernate.SQL - 
2019-08-12 20:40:31 DEBUG org.springframework.security.authentication.dao.DaoAuthenticationProvider - Authentication failed: password does not match stored value
2019-08-12 20:40:31 DEBUG org.springframework.security.web.access.ExceptionTranslationFilter - Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.authentication.BadCredentialsException: Bad credentials 
H. Pardess
  • 51
  • 1
  • 12
  • 2
    Your filters are exposed as beans, so Spring Boot adds them to the servlet filter chain, too. – dur Aug 09 '19 at 12:30
  • Ok, I found the problem. You have to set [`setCreateAuthenticatedToken(true)`](https://docs.spring.io/spring-security/site/docs/4.2.12.RELEASE/apidocs/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.html#setCreateAuthenticatedToken-boolean-). Without this flag you are still anonymous. – dur Aug 12 '19 at 18:42
  • 1
    thank you. digestAuthenticationFilter.setCreateAuthenticatedToken(true); line of code fixed digest auth related entry points. – H. Pardess Aug 12 '19 at 19:14
  • I also had the problem with /api/** entry point. As you suggested to remove @ Bean and replace it with @ Autowired for JWT filter (jwtAuthenticationTokenFilterBean). I done so and also added @ Component to JWT filter but there are some @ Autowired fields in JWT filter which was sometimes null. I tried but finally " [Unable to autowire the service inside my authentication filter in Spring](https://stackoverflow.com/a/32495757/8403256) " solved the issue. – H. Pardess Aug 14 '19 at 04:25
  • @dur, I have another problem with Digest auth entry points. The unauthorized requests that have wrong credentials are handled by 4th entry point. Although the AccessDeniedException is thrown but it is not handled in relevant 1st or 2nd entry points. but subsequent filters is executed and finally last entry point handles the request and response back to user. – H. Pardess Aug 15 '19 at 04:32
  • @dur, also when i request wrong url that does not exist in specific entry point such as /odk/xyz or /odkx/xyz. The request is not handled by relevant entry point which should response as 404 but the request is handled by 4th entry point and response to user. – H. Pardess Aug 15 '19 at 04:34

0 Answers0