본문 바로가기
공부/프로그래밍

[springboot] @EnableResourceServer 사용 중 Using generated security password 가 보일 때

by demonic_ 2021. 5. 12.
반응형

Spring Security를 사용하고 있는데 서버가 올라가면서 다음의 메세지가 보였다.

Using generated security password = xxxxxxxx

 

처음엔 무엇인가 싶어 봤는데 알고보니 Security가 AutoConfiguration 되면서 생성하는 거였다. 공식문서에 따르면 다음과 같다.

https://www.baeldung.com/spring-boot-security-autoconfiguration

 

Spring Boot Security Auto-Configuration | Baeldung

A quick and practical guide to Spring Boot's default Spring Security configuration.

www.baeldung.com

 

password를 생성하는 파일을 한번 보기로 했다. 생성하는 곳은 UserDetailsServiceAutoConfiguration 파일이었다.

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean(
		value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class },
		type = { "org.springframework.security.oauth2.jwt.JwtDecoder",
				"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector" })
public class UserDetailsServiceAutoConfiguration {
...
	private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {
		String password = user.getPassword();
		if (user.isPasswordGenerated()) {
			logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
		}
		if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
			return password;
		}
		return NOOP_PASSWORD_PREFIX + password;
	}
}

 

logger.info에 보면 로그에서 본 문구가 그대로 있다. 그래서 여기다가 break point를 걸면 여기에서 멈춘다.

 

@ConditionalOnMissingBean 를 살펴보면 3개의 클래스가 걸려있지 않는다면 이것이 활성화 되는 것이다.

AuthenticationManager.class

AuthenticationProvider.class

UserDetailsService.class

 

Security를 쓴다는건 인증을 허가하는 부분이 있어야 하는데 위의 3개는 그것을 하는 클래스다. 그런데 이것들이 비어있다보니 비밀번호를 자동 생성하여 로그에 찍힌 것이다.

 

 

그럼 이것을 막는 방법은 무엇일까?

인터넷에 보니 SpringBootApplication에 옵션으로 막는게 있었다

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class Application {
...

 

다음처럼 옵션을 줘서 사용을 막는 것이다. 근데 이것은 SpringSecurity의 모든기능을 막는 것이다.

 

내 경우 ResourceServer 를 이용해 인증을 참고하고 있었는데, 위 설정을 하게되면 다음의 에러를 보게 된다.

***************************
APPLICATION FAILED TO START
***************************

Description:

A component required a bean of type 'org.springframework.security.config.annotation.ObjectPostProcessor' that could not be found.


Action:

Consider defining a bean of type 'org.springframework.security.config.annotation.ObjectPostProcessor' in your configuration.

Disconnected from the target VM, address: '127.0.0.1:54275', transport: 'socket'

Process finished with exit code 0

 

이런 오류가 생기는 이유는 @EnableResourceServer 를 사용하기 때문이다.

 

ResourceServerConfiguration 을 빈으로 등록하는 과정에서 ApplicationContext 를 주입받는데, 그때 WebSecurityConfigurerAdapter 를 상속받아 생성한다. 문제는 WebSecurityConfigurerAdapter가 생성되면서 setApplicationContext 에서 ObjectPostProcessor Bean을 찾는데 여기서 실패하여 그렇다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ResourceServerConfiguration.class)
public @interface EnableResourceServer {

}
@Configuration
public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter implements Ordered {
...
  @Autowired
  private ApplicationContext context;
...
public abstract class WebSecurityConfigurerAdapter implements
		WebSecurityConfigurer<WebSecurity> {
...
	@Autowired
	public void setApplicationContext(ApplicationContext context) {
		this.context = context;

		ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
		LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);

		authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
		localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
			@Override
			public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
				authenticationBuilder.eraseCredentials(eraseCredentials);
				return super.eraseCredentials(eraseCredentials);
			}

		};
	}
...

 

그래서 이것을 안보이게 하는 방법은 의외로 간단하다. 다시 로그를 찍는 메서드를 살펴보자

public class UserDetailsServiceAutoConfiguration {
...
	private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {
		String password = user.getPassword();
		if (user.isPasswordGenerated()) {
			logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
		}
		if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
			return password;
		}
		return NOOP_PASSWORD_PREFIX + password;
	}
}

isPasswordGenerated 만 통과하면 문제가 없어보인다. 이것을 설정하는 방법은 application.properties 에서 가능하다. 우선 이 클래스를 살펴보면 다음과 같다.

@ConfigurationProperties(prefix = "spring.security")
public class SecurityProperties {
...
        // 기본값이 true 다
		private boolean passwordGenerated = true;
...
		public void setPassword(String password) {
			if (!StringUtils.hasLength(password)) {
				return;
			}
			this.passwordGenerated = false;
			this.password = password;
		}

비밀번호를 설정하면 false로 변하는 것이다. 이 설정을 application.properties에 하면 된다.

spring.security.user.password=[패스워드]

 

이렇게 설정하면 더이상 생성되지 않는다.

 

 

끝.

 

반응형

댓글