2

I am building a user authentication micro service for learning purpose using Spring Boot. I have already developed separately 3 different user authentication methods as 3 different projects (one of using PostgreSQL database with JWT authentication, and another one is using OAuth2 and the other one is using LDAP). Now I need combine these 3 as a single service. I already have setup some steps.

At the moment I have the following:

This my SecurityConfigure.java file:

package com.persistent.userauthentication.security;

import com.persistent.userauthentication.filters.JwtRequestFilter;
import com.persistent.userauthentication.service.AuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfigure extends WebSecurityConfigurerAdapter {

    @Configuration
    @Order(1)
    public static class JwtWebSecurityConfig extends WebSecurityConfigurerAdapter{
        @Autowired
        private AuthService authService;

        @Autowired
        private JwtRequestFilter jwtRequestFilter;

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

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable()
                .requestMatchers()
                    .antMatchers("/jwt/**")
                    .and()
                .authorizeRequests()
                    .antMatchers("/jwt/authenticate").permitAll()
                    .anyRequest().authenticated()
                    .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS); //since we don't want to manage sessions

            http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        }

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

        @Bean
        public PasswordEncoder passwordEncoder(){
            return NoOpPasswordEncoder.getInstance();
        }
    }

    @Configuration
    @Order(2)
    public static class LdapSecurityConfig extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .antMatcher("/ldapauth/**")
                .authorizeRequests()
                    .anyRequest().fullyAuthenticated()
                    .and()
                .formLogin();
        }

        @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth
                .ldapAuthentication()
                    .userDnPatterns("uid={0},ou=people")
                    .groupSearchBase("ou=groups")
                    .contextSource()
                    .url("ldap://localhost:8389/dc=springframework,dc=org")
                    .and()
                .passwordCompare()
                    .passwordEncoder(new BCryptPasswordEncoder())
                    .passwordAttribute("userPassword");
        }
    }

    @Configuration
    @Order(3)
    public static class Oauth2SecurityConfig extends WebSecurityConfigurerAdapter{

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .antMatcher("/googleauth/**")
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .oauth2Login();
        }
    }
}

This is my AuthController.java (controller) file:

package com.persistent.userauthentication.controller;

import com.persistent.userauthentication.model.AuthenticationRequest;
import com.persistent.userauthentication.model.AuthenticationResponse;
import com.persistent.userauthentication.service.AuthService;
import com.persistent.userauthentication.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@RestController
public class AuthController {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private AuthService authService;

    @Autowired
    private JwtUtil jwtTokenUtil;

    @RequestMapping(value = "/jwt/hello", method = RequestMethod.GET)
    public String Hello(){
        return "basic auhentication successfull";
    }

    @GetMapping("/googleauth/hello")
    public String GooglAauth(){
        return "google authentication successful!";
    }

    @RequestMapping(value = "/ldapauth/hello", method = RequestMethod.GET)
    public String LdapAuth(){
        return "ldap authentication successful!";
    }

    @RequestMapping(value = "/jwt/authenticate", method = RequestMethod.POST)
    public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {

        try {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
            );
        } catch (BadCredentialsException e){
            throw new Exception("username or password is incorrect!", e);
        }

        final UserDetails userDetails = authService.loadUserByUsername(authenticationRequest.getUsername());

        final String jwt = jwtTokenUtil.generateToken(userDetails);

        return ResponseEntity.ok(new AuthenticationResponse(jwt));
    }

    @RequestMapping(value = "/jwt/extendtoken", method = RequestMethod.POST)
    public ResponseEntity<?> createNewAuthenticationToken(@RequestHeader("Authorization") String token) throws Exception {

        final String jwt = jwtTokenUtil.refreshToken(token);

        return ResponseEntity.ok(new AuthenticationResponse(jwt));
    }
}

Currently /jwt/hello (with Authorization header and generated JWT code), /jwt/authenticate and jwt/extendtoken endpoints are working properly.

Now I need to setup OAuth2 authentication service to /googleauth/hello endpoint and LDAP authentication service to /ldapauth/hello.
When I call endpoint /googleauth/hello it also redirect to the LDAP login page instead of Google account selection page.

dur
  • 15,689
  • 25
  • 79
  • 125
  • 1
    Does this answer your question? [Spring multiple authentication methods for different api endpoints](https://stackoverflow.com/questions/54706291/spring-multiple-authentication-methods-for-different-api-endpoints) – Eleftheria Stein-Kousathana Oct 28 '21 at 07:28
  • Yes its kind of similar, but that answer not worked for me. I will edit my question with new configurations that I have done so far. If you have good idea about this can you please help me. Because I am a beginner to spring security. – Thisara Jayaweera Oct 29 '21 at 07:35
  • @ThisaraJayaweera Your problem is that both login pages uses the same URL `/login`. Try to change the URL of you login page. – dur Nov 05 '21 at 09:12
  • Does this answer your question? [Spring Security 3.2.1 Multiple login forms with distinct WebSecurityConfigurerAdapters](https://stackoverflow.com/questions/22845474/spring-security-3-2-1-multiple-login-forms-with-distinct-websecurityconfigurerad) – dur Nov 05 '21 at 09:20
  • @dur I think my problem is .antMatcher("/ldapauth/**") is not working as i expected. I need to filter all /ldapauth/** patterns and use ldap authentication method. – Thisara Jayaweera Nov 18 '21 at 23:29

2 Answers2

3

Finally this solution works for me. I had to change my security configurations as below.

@Configuration
@EnableWebSecurity
public class SecurityConfigure extends WebSecurityConfigurerAdapter {


    @Configuration
    @Order(1)
    public static class JwtWebSecurityConfig extends WebSecurityConfigurerAdapter{
        @Autowired
        private AuthService authService;

        @Autowired
        private JwtRequestFilter jwtRequestFilter;

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

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .csrf().disable()
                    .requestMatchers().antMatchers("/jwt/**")
                    .and()
                    .authorizeRequests().antMatchers("/jwt/authenticate").permitAll()
                    .anyRequest().authenticated()
                    .and().sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

            http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

        }

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

        @Bean
        public PasswordEncoder passwordEncoder(){
            return NoOpPasswordEncoder.getInstance();
        }
    }

    @Configuration
    @Order(2)
    public static class LdapSecurityConfig extends WebSecurityConfigurerAdapter{
                @Override
                public void configure(AuthenticationManagerBuilder auth) throws Exception {
                    auth
                            .ldapAuthentication()
                            .userDnPatterns("uid={0},ou=people")
                            .groupSearchBase("ou=groups")
                            .contextSource()
                            .url("ldap://localhost:8389/dc=springframework,dc=org")
                            .and()
                            .passwordCompare()
                            .passwordEncoder(new BCryptPasswordEncoder())
                            .passwordAttribute("userPassword");
                }

                @Override
                protected void configure(HttpSecurity http) throws Exception {
                    http
                            .csrf().disable()
                            .requestMatchers().antMatchers("/ldap/**","/login")
                            .and()
                            .authorizeRequests().antMatchers("/login").permitAll()
                            .anyRequest().fullyAuthenticated()
                            .and()
                            .formLogin();
                }
    }

    @Configuration
    @Order(3)
    public static class Oauth2SecurityConfig extends WebSecurityConfigurerAdapter{

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .csrf().disable()
                    .requestMatchers().antMatchers("/google/**","/oauth2/**","/login/oauth2/**")
                    .and()
                    .authorizeRequests().antMatchers("/oauth2/**","/login/oauth2/**").permitAll()
                    .anyRequest().fullyAuthenticated()
                    .and()
                    .oauth2Login();
        }
    }

}
0

Example to have basic and jwt authentication in spring security config class

@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/**").authenticated()
            .antMatchers("/swagger-ui/**").authenticated()
            .antMatchers("/**").permitAll()
            .and().httpBasic().and().oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
    return http.build();
}
Chinmoy
  • 1,391
  • 13
  • 14