0

I am using spring security for authentication

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


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



    http.csrf().disable().authorizeRequests()
    .antMatchers("/login").hasAnyRole("ADMIN","VISITOR").and().
    formLogin().defaultSuccessUrl("/login").failureUrl("/")
    .loginPage("/login").usernameParameter("username").passwordParameter("password").failureUrl("/").
    and().logout().permitAll().and().exceptionHandling().accessDeniedPage("/403").and()
    .authorizeRequests().antMatchers("/resources/**").permitAll().and().authorizeRequests().
    antMatchers("/api/**").authenticated().and().httpBasic().realmName("MY_TEST_REALM").
    authenticationEntryPoint(getBasicAuthEntryPoint());
}

@Bean
public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint(){
    return new CustomBasicAuthenticationEntryPoint();
}

This is working fine. When i hit /api/login i am able to use basic authentication

But after first successful authentication I am able to use /api/login without authentication.

It is not taking me to auth provider at second time. First time control is going there but not second time.

Ayush
  • 29
  • 6

2 Answers2

1

Register two WebSecurity configurations:

@Configuration
@EnableWebSecurity
@Order(1)
public class StatefulConfig extends WebSecurityConfigurerAdapter {

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


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

        http.csrf().disable().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).and()
       .antMatcher("/web/*").authorizeRequests()
                .antMatchers("/*").hasAnyRole("ADMIN","VISITOR").and().
                formLogin().defaultSuccessUrl("/web/login").failureUrl("/web/error").loginPage("/web/login").usernameParameter("username").passwordParameter("password").failureUrl("/").
                and().logout().logoutUrl("/web/logout").permitAll().and().exceptionHandling().accessDeniedPage("/403").and()
                .authorizeRequests().antMatchers("/resources/**").permitAll();
    }

}

And for rest:

@Configuration
@Order(2)
public class StatelessConfig extends WebSecurityConfigurerAdapter {

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


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

        http.csrf().disable().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
           .antMatcher("/api/*").authorizeRequests()
                .antMatchers("/api/**").authenticated().and().httpBasic().realmName("MY_TEST_REALM").
  authenticationEntryPoint(getBasicAuthEntryPoint());
}

    @Bean
    public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint(){
        return new CustomBasicAuthenticationEntryPoint();
    }

}

Be careful: there are antMatcher(...) and antMatchers(...) methods.

UPDATE: similar problem & solution here

  • Where to start: https://stackoverflow.com/questions/23752593/from-where-to-start-learning-spring AntMatcher vs AntMatchers: https://stackoverflow.com/questions/35890540/when-to-use-spring-securitys-antmatcher – Aleksander Burzec Apr 10 '18 at 23:52
0

Session is created when you log in. Session will be active until you logout (destroy session), or when time expire.
See example

EDIT: Spring application have a few important settings associated with session. The first one is session creation policy (by default IF_REQUIRED - if session linked with request already exists it will be not destroyed and created again). Session is saved in cookie - you can check it hitting f12. Application "check" does cookie exist in request. When you go to login page there are two cases:

  • you don't have session -> login popup appears, you can log in,
  • you have session because SecurityContextHolder contain information about current session.

How does it work?

When you use .httpBasic(), Spring Security registers BasicAuthenticationFilter. In method doFilterInternal you can see:

if (authenticationIsRequired(username)) {
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                        username, tokens[1]);
                authRequest.setDetails(
                        this.authenticationDetailsSource.buildDetails(request));
                Authentication authResult = this.authenticationManager
                        .authenticate(authRequest);

                if (debug) {
                    this.logger.debug("Authentication success: " + authResult);
                }

                SecurityContextHolder.getContext().setAuthentication(authResult);

                this.rememberMeServices.loginSuccess(request, response, authResult);

                onSuccessfulAuthentication(request, response, authResult);
            }

After success first login, authentication is set. When you try to log in again authenticationIsRequired method returns false. Why? Look at the source:

private boolean authenticationIsRequired(String username) {
        // Only reauthenticate if username doesn't match SecurityContextHolder and user
        // isn't authenticated
        // (see SEC-53)
        Authentication existingAuth = SecurityContextHolder.getContext()
                .getAuthentication();

        if (existingAuth == null || !existingAuth.isAuthenticated()) {
            return true;
        }

        // Limit username comparison to providers which use usernames (ie
        // UsernamePasswordAuthenticationToken)
        // (see SEC-348)

        if (existingAuth instanceof UsernamePasswordAuthenticationToken
                && !existingAuth.getName().equals(username)) {
            return true;
        }

        // Handle unusual condition where an AnonymousAuthenticationToken is already
        // present
        // This shouldn't happen very often, as BasicProcessingFitler is meant to be
        // earlier in the filter
        // chain than AnonymousAuthenticationFilter. Nevertheless, presence of both an
        // AnonymousAuthenticationToken
        // together with a BASIC authentication request header should indicate
        // reauthentication using the
        // BASIC protocol is desirable. This behaviour is also consistent with that
        // provided by form and digest,
        // both of which force re-authentication if the respective header is detected (and
        // in doing so replace
        // any existing AnonymousAuthenticationToken). See SEC-610.
        if (existingAuth instanceof AnonymousAuthenticationToken) {
            return true;
        }

        return false;
    }

As you can see getAuthhentication invoked on SecurityContextHolder return object set in previous request. Sorry for my bad English.

UPDATE: you can invalidate session using "/logout" url.

Community
  • 1
  • 1
  • Thanks a lot.... I actually have a requirement like this: I have a web application and i have to produce some rest api for mobile users. I want to have securit for web as well as rest apis ......current config is working for both....web is working absolutely fine...but rest apis when i hit with postman... first time it works fine but later on successful authentication it does not require password in basic auth ..when i do session stateless - api is working is fine .....but web module is not working – Ayush Apr 10 '18 at 20:11
  • in the configure method i wrote ....can you explain the meaning of what i wrote .....like its hard to get – Ayush Apr 10 '18 at 20:30
  • I want to have session in web but want to keep it stateless in rest ...is it possible – Ayush Apr 10 '18 at 20:46