4

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.

5
  • 1
    Does this answer your question? Spring multiple authentication methods for different api endpoints Commented Oct 28, 2021 at 7: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. Commented Oct 29, 2021 at 7:35
  • @ThisaraJayaweera Your problem is that both login pages uses the same URL /login. Try to change the URL of you login page. Commented Nov 5, 2021 at 9:12
  • Does this answer your question? Spring Security 3.2.1 Multiple login forms with distinct WebSecurityConfigurerAdapters Commented Nov 5, 2021 at 9: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. Commented Nov 18, 2021 at 23:29

2 Answers 2

4

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();
        }
    }

}
Sign up to request clarification or add additional context in comments.

Comments

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();
}

2 Comments

This doesn't work for me. When accessing an endpoint using basic auth I get a org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken. Did you actually try this?
UPDATE: Our code defines an OpaqueTokenIntrospector for the oauth2 auth. That inhibits Spring's creation of a UserDetailsService bean in UserDetailsServiceAutoConfiguration which then stops it from creating a DaoAuthenticationProvider which results in the error I got. I had to explicitly define a UserDetailsService bean.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.