Spring Security 프로젝트 설정 3 - Security Config

✒️ 2025-05-28 14:21 내용 수정



흐름

spring_security_authentication_flow.png


Config 추가

  1. config 패키지에 ApplicationConfig 클래스를 추가한다.
package com.example.security.config;  
  
import lombok.RequiredArgsConstructor;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.security.authentication.AuthenticationManager;  
import org.springframework.security.authentication.AuthenticationProvider;  
import org.springframework.security.config.annotation.web.builders.HttpSecurity;  
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;  
import org.springframework.security.config.http.SessionCreationPolicy;  
import org.springframework.security.web.SecurityFilterChain;  
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;  
  
@Configuration  
@EnableWebSecurity  
@RequiredArgsConstructor  
public class SecurityConfig {  
  
    private final JwtAuthenticationFilter jwtAuthFilter;  
    private final AuthenticationProvider authenticationProvider;  
  
    @Bean  
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{  
  
        http  
			// session stateless로 인해 꺼 둠  
			.csrf((auth)->auth.disable())  
			.authorizeRequests()  
			.requestMatchers("/") // 나열된 요청들은  
			.permitAll() // 모두 허용  
			.anyRequest() // 그 외의 모든 요청은  
			.authenticated() // 인증 필요  
			.and()  
			.sessionManagement((session)->  
					session // session state는 저장되면 안되므로 stateless로 설정  
					.sessionCreationPolicy(SessionCreationPolicy.STATELESS))  
			.authenticationProvider(authenticationProvider)  
			.addFilterBefore(jwtAuthFilter,  
					UsernamePasswordAuthenticationFilter.class); // jwt 필터 가동  
  
  
        return http.build();  
    }  
}
  1. AuthenticationProviderAuthenticationManager를 사용하기 위해 각 객체의 Bean을 추가한다.
    • 비밀번호 암호화를 위한 PasswordEncoder도 추가한다.
package com.example.security.config;  
  
import com.example.security.user.UserRepository;  
import lombok.RequiredArgsConstructor;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.security.authentication.AuthenticationManager;  
import org.springframework.security.authentication.AuthenticationProvider;  
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;  
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;  
import org.springframework.security.core.userdetails.UserDetailsService;  
import org.springframework.security.core.userdetails.UsernameNotFoundException;  
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;  
import org.springframework.security.crypto.password.PasswordEncoder;  
  
@Configuration  
@RequiredArgsConstructor  
public class ApplicationConfig {  
  
    private final UserRepository userRepository;  
  
    @Bean  
    public UserDetailsService userDetailsService() {  
        return username -> userRepository.findByEmail(username)  
                .orElseThrow(()->new UsernameNotFoundException("User not found"));  
    }  
  
    // 데이터 접근 역할 - UserDetail 정보 접근 등  
    @Bean  
    public AuthenticationProvider authenticationProvider() {  
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();  
        // 사용할 UserDetailService 지정 - 이미 생성해둔 userDetailsService 사용  
        authProvider.setUserDetailsService(userDetailsService());  
        // 비밀번호 암호화 지정  
        authProvider.setPasswordEncoder(passwordEncoder());  
        return authProvider;  
    }  
  
    // AuthenticationManager 추가  
    @Bean  
    public AuthenticationManager authenticationManager(
	    AuthenticationConfiguration config
    )  
    throws Exception{  
        return config.getAuthenticationManager();  
    }  
  
    // 비밀번호 암호화  
    @Bean  
    public PasswordEncoder passwordEncoder() {  
        // Bcrypt encoder 반환  
        return new BCryptPasswordEncoder();  
    }  
}

AuthenticationProvider bean 관련 warning

[  restartedMain] r$InitializeUserDetailsManagerConfigurer : 
Global AuthenticationManager configured with an AuthenticationProvider bean. 
UserDetailsService beans will not be used for username/password login. 
Consider removing the AuthenticationProvider bean. 
Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider.
  1. AuthenticationProvider Bean을 제거했더니 Bean을 못 찾는다는 에러가 발생하며 애플리케이션 실행이 안된다.
    • AuthenticationProvider를 사용하는 SecurityConfig에서 에러가 발생한 것 같았다.
@Configuration  
@RequiredArgsConstructor  
public class ApplicationConfig {  
  
    private final UserRepository userRepository;
    
	// Bean을 제거했더니 SecurityConfig에서 문제 발생
	public AuthenticationProvider authenticationProvider() {  
		DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();  
		// 사용할 UserDetailService 지정 - 이미 생성해둔 userDetailsService 사용  
		authProvider.setUserDetailsService(userDetailsService());  
		// 비밀번호 암호화 지정  
		authProvider.setPasswordEncoder(passwordEncoder());  
		return authProvider;  
	}
}  
@Configuration  
@EnableWebSecurity  
@RequiredArgsConstructor  
public class SecurityConfig {  
  
    private final JwtAuthenticationFilter jwtAuthFilter;  
    private final AuthenticationProvider authenticationProvider;  
  
    @Bean  
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{  
  
        http  
			// ...			
			// 여기서 문제가 발생
			.authenticationProvider(authenticationProvider)  
	}
}
  1. ApplicationConfig에 있던 AuthenticationProvider, AuthenticationManager, userDetailsService()을 모두 SecurityConfig에 옮겨서 수정했더니 이번엔 SecurityConfig, JwtAuthenticationFilter 간의 순환 참조 문제가 발생했다.
    • UserDetailsService가 필요한 메소드들이 두 클래스 간에 맞물리면서 발생했다.
@Configuration  
@EnableWebSecurity  
@RequiredArgsConstructor  
public class SecurityConfig {  
  
    private final JwtAuthenticationFilter jwtAuthFilter;  
	private final UserRepository userRepository;  
	  
	// 사용자 조회  - UserDetailsService이 엮인 순환 참조 문제가 발생함
	@Bean  
	public UserDetailsService userDetailsService() {  
	    return username -> userRepository.findByEmail(username)  
	            .orElseThrow(()->new UsernameNotFoundException("User not found"));  
	}  
	  
	// 데이터 접근 역할 - UserDetail 정보 접근 등  
	@Bean  
	public AuthenticationProvider authenticationProvider() {  
	    DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();  
	    // 사용할 UserDetailService 지정 - 이미 생성해둔 userDetailsService 사용  
	    authProvider.setUserDetailsService(userDetailsService());  
	    // 비밀번호 암호화 지정  
	    authProvider.setPasswordEncoder(passwordEncoder());  
	    return authProvider;  
	}  
	  
	// AuthenticationManager 추가  
	@Bean  
	public AuthenticationManager authenticationManager(AuthenticationConfiguration config)  
	        throws Exception{  
	    return config.getAuthenticationManager();  
	}  
	  
	// 비밀번호 암호화  
	@Bean  
	public PasswordEncoder passwordEncoder() {  
	    // Bcrypt encoder 반환  
	    return new BCryptPasswordEncoder();  
	}
    @Bean  
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{  
  
        http  
			// ...
                .authenticationProvider(authenticationProvider())  
                .addFilterBefore(jwtAuthFilter,  
                        UsernamePasswordAuthenticationFilter.class); // jwt 필터 가동  
			// ...
        return http.build();  
    }  
  
	// ...
}

spring_security_provider_warning 1.png