Keval Bhatt
Keval Bhatt

Reputation: 177

How to manage session with spring security based on request type?

I would like to configure web security layer based on my request type.

If the request starts with /rest then it should use Basic authentication with stateless session management and for login authentication then it should use CSRF with stateful session management.

I have tried below code.

@Override
protected void configure(HttpSecurity http) throws Exception 
{
    http
        .authorizeRequests()
        .antMatchers("/rest/**").hasRole("SUPER_ADMIN") 
        .anyRequest().fullyAuthenticated()
        .and()
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .httpBasic()
        .authenticationEntryPoint(authenticationEntryPoint)
        .and()
        .formLogin().and().logout().permitAll();
}

It works with basic authentication but it doesn't work for the login request because the session is not stateful. Can anyone please help me to configure Spring security. I am new to Spring security.

Upvotes: 2

Views: 4748

Answers (2)

PraveenKumar Lalasangi
PraveenKumar Lalasangi

Reputation: 3523

You need
1. Rest API's to be authenticated by basic authentication
2. Your web application be authenticated by form login.
And authorization is other part in both cases that you can set it as per your requirement.

Let me explain what was wrong with your approach. By your approach you can achieve only one authentication entry point from one configuration. i.e, you can't achieve multiple authentication entry point.

Now coming to your first requirement of achieving multiple authentication entry point.
1. For Rest API resources -- authentication by HttpBasicAuthentication for antMatcher /rest/**
2. For WebApp resources -- authentication by Form Login for antMatcher other than /rest/**

To achieve this
1. You need to have implementation of WebSecurityConfigurerAdapter of different configuration order and different antMatcher patterns.
2. Order of each configuration is important.
- wildcard pattern(/**) should be placed last order
- non wildcard pattern or restricted pattern(/rest/**) should be placed first order
3. As those configuration classes are static and inner classes for a class which is annotated @EnableWebSecurity you should be careful while defining bean using @bean and autowiring using @Autowired.

Note:
Most of people makes mistake by not defining antmather for authorizeRequest()
If first configuration @Order(1) class is configured as below
http.authorizeRequests()
2nd configuration will become dead configuration because
http.authorizeRequests() => http.antMatcher("/**").authorizeRequests()
And all URL's will be configured only for first configuration only.

Refer code given below for better understanding.

@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration
{
    @Bean
    public PasswordEncoder passwordEncoder() 
    {
        return new BCryptPasswordEncoder();
    }

    @Configuration
    @Order(1)
    public static class BasicAuthSecurityConfig extends WebSecurityConfigurerAdapter
    {
        @Autowired
        private PasswordEncoder passwordEncoder;

        @Autowired
        public void configureInMemoryAuthentication(AuthenticationManagerBuilder auth) throws Exception
        {
            auth.inMemoryAuthentication()
                    .withUser("superadmin")
                    .password(passwordEncoder.encode("superadmin@123#"))
                    .roles("SUPER_ADMIN");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
            http.csrf().disable()
                .antMatcher("/rest/**")
                    .authorizeRequests()
                .antMatchers("/rest/**").hasRole("SUPER_ADMIN")
            .and().httpBasic();

            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }

    @Configuration
    @Order(2)
    public static class LoginFormSecurityConfig extends WebSecurityConfigurerAdapter
    {
        @Autowired
        private PasswordEncoder passwordEncoder;

        @Autowired
        public void configureInMemoryAuthentication(AuthenticationManagerBuilder auth) throws Exception
        {
            auth.inMemoryAuthentication()
                    .withUser("user")
                    .password(passwordEncoder.encode("user@123#"))
                    .roles("USER");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
            http
                .antMatcher("/**") //wild card i.e, allow all (But already /rest/** is filtered by 1st config)
                    .authorizeRequests()
                .antMatchers("/resources/**").permitAll()
                .antMatchers("/**").authenticated()
            .and().formLogin()
                .defaultSuccessUrl("/app/user/dashboard")
            .and().exceptionHandling()
                .accessDeniedPage("/403")
            .and().logout()
                .invalidateHttpSession(true);

            http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired");
        }
    }
}

This question has requirement of different sets of URL's(/rest/** and other than /rest/**) for different authentication filters. Here user's (for both basic auth and form login) may be authenticated against a single table (say user_details) or multiple tables (say api_users and web_users)

If you have requirement like there is no different set of URL's but two sets of users say customer and employees(staff) both are accessing same application but they needs to be authenticated against different tables(say users and customer table) in that case refer my another answer Spring Security user authentication against customers and employee table

Upvotes: 4

Gaurav Srivastav
Gaurav Srivastav

Reputation: 2551

You have to allow users to access login page without authentication and same you can do with static pages. See below configuration.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        UserDetails user =
             User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(user);
    }
}

Upvotes: 0

Related Questions