Spring Security - configure(AuthenticationManagerBuilder auth) 관련 오류

Bruce Han·2022년 7월 2일
1
post-thumbnail

개요

간단하게 inMemory 방식의 인증 사용자를 등록하는 예제를 학습하다가 여러 오류를 마주하게 되고, 이를 해결하는 과정을 기록했습니다.

학습하려던 첫 소스


@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				.antMatchers("/").permitAll()
				.antMatchers("/css/**", "/js/**", "/img/**").permitAll()
				.antMatchers("/guest/**").permitAll()
				.antMatchers("/member/**").hasAnyRole("USER", "ADMIN")
				.antMatchers("/admin/**").hasRole("ADMIN")
				.anyRequest().authenticated();
		http.formLogin()
				.permitAll();
		http.logout()
				.permitAll();
	}

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
			.withUser("user").password(passwordEncoder().encode("1234")).roles("USER")
			.and()
			.withUser("admin").password(passwordEncoder().encode("1234")).roles("ADMIN");
	}
	
	@Bean
	public BCryptPasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

위와 같은 소스로 security를 이용하려고 하면

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'webSecurityConfig': Requested bean is currently in creation: Is there an unresolvable circular reference?

'webSecurityConfig'라는 빈을 만들려고 하는데 생성 중이며, 혹시 확인할 수 없는 순환참조가 있지는 않니?

라는 오류와 함께 실행되지 않습니다.

순환참조? 라고 생각해서 밑에 있던 @Bean을 제거해봤습니다.


@Bean 제거

    // @Bean
    BCryptPasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

"null"이라는 id에 대해 매핑된 PasswordEncoder가 없다고 나옵니다.

순환참조 오류는 나오지 않았지만, 입력한 것을 받아줄 객체가 없어 인식을 못 하는 것 같습니다.


@Bean 추가 및 @Autowired 제거


	// @Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
			.withUser("user").password(passwordEncoder().encode("1234")).roles("USER")
			.and()
			.withUser("admin").password(passwordEncoder().encode("1234")).roles("ADMIN");
	}
	
	@Bean
	BCryptPasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

(수정) 밑에 출력되는 내용은 Spring Security 의존성이 추가되면 생성되는 임시 패스워드입니다.
생성된 패스워드는 기본으로 사용할 수 있는 'user'계정의 패스워드입니다.
프로젝트 초기에 아무 계정도 없을 때 사용할 수 있는 임시 패스워드 역할을 합니다.

Using generated security password: 4a5f3ce3-c597-466f-bbe6-84a06a739360

This generated password is for development use only. Your security configuration must be updated before running your application in production.

이 생성된 비밀번호는 개발용입니다. 프로덕션에서 애플리케이션을 실행하기 전에 보안 구성을 업데이트해야 합니다.

라는 메시지와 함께 애플리케이션이 실행됩니다.

하지만, 로그인을 시도하면 위 사진과 같이

'자격 증명에 실패하였습니다'

라는 메시지가 뜨면서 로그인이 안 되는 것을 확인할 수 있습니다.
--> (수정) 잘못된 패스워드를 입력하면 이렇게 됩니다.

o.a.c.util.SessionIdGeneratorBase : Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [124] milliseconds.

[SHA1PRNG]방식을 사용한 세션 ID 생성을 위한 SecureRandom 인스턴스 생성에 사용된 시간(milliseconds)이 얼만큼 있다~ 라는 메시지가 나옵니다.

o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt

비밀번호가 BCrypt 방식으로 인코딩되지 않았다는 메시지도 나옵니다.


어떻게 해결하지 고민하다가

다른 스프링 부트 참고서에는 Security를 어떻게 다루고 있나를 알아보기 위해 알아봤습니다.

먼저 테스트 코드를 통해 "1234"라는 비밀번호를 인코딩하고, 인코딩한 결과값을 다시 원래 코드에 집어넣는 방식으로 실습한 것이 보였습니다.

그리고

	@Override
	public void configure(AuthenticationManagerBuilder auth) throws Exception {
    	...
    }

원래 소스에는 configureGlobal()메서드로 활용하려고 했는데 여기에서는 configure(AuthenticationManagerBuilder auth)메서드를 재정의하여 또 활용하고 있는 것이 보였습니다.

그래서 현재 실습 중인 Spring Security 5.6.6 버전의 공식문서 내용을 찾아봤습니다.(feat.IntelliJ)

처음에 헤매던 예제와 비교해보면 inMemory를 사용하는 방식이 비슷한 것 같아 configure(AuthenticationManagerBuilder auth)를 재정의하여 구현해봤습니다.

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
			.withUser("user").password(passwordEncoder().encode("1234")).roles("USER")
			.and()
			.withUser("admin").password(passwordEncoder().encode("1234")).roles("ADMIN");
	}
	
	@Bean
	BCryptPasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

id 는 user, password는 1234로 로그인 후 인증에 성공했을 때 화면이 출력됩니다.
구현에 성공했습니다.🎉

	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		this.disableLocalConfigureAuthenticationBldr = true;
	}

원형을 재정의한 후에 안에다가 inMemoryAuthentication()을 통해서 인증 방식을 거치는 것이, 지금 예제에서는 맞다고 판단하고 이를 마치겠습니다.

References

  • 예제로 배우는 스프링 부트 : JSP, Oracle, MyBatis와 연동한 웹 애플리케이션 만들기 (이재환 저)
  • 코드로 배우는 스프링 부트 웹 프로젝트 (지은이 : 구멍가게 코딩단)
  • 4.1.4버전 공식문서
  • 5.6.6버전 공식문서
profile
만 가지 발차기를 한 번씩 연습하는 사람은 두렵지 않다. 내가 두려워 하는 사람은 한 가지 발차기를 만 번씩 연습하는 사람이다. - Bruce Lee

0개의 댓글