1. 개요
Spring Security 4에서는 메모리 내 인증을 사용하여 일반 텍스트로 비밀번호를 저장할 수 있었습니다.
버전 5의 암호 관리 프로세스에 대한 대대적인 점검으로 암호 인코딩 및 디코딩을 위한 보다 안전한 기본 메커니즘이 도입되었습니다. 이는 Spring 애플리케이션이 일반 텍스트로 비밀번호를 저장하는 경우 Spring Security 5로 업그레이드하면 문제가 발생할 수 있음을 의미합니다.
이 짧은 사용방법(예제)에서는 잠재적인 문제 중 하나를 설명하고 솔루션을 시연합니다.
2. 스프링 시큐리티 4
간단한 메모리 내 인증(Spring 4에 유효)을 제공하는 표준 Security 구성을 보여줌으로써 시작하겠습니다.
@Configuration
public class InMemoryAuthWebSecurityConfigurer
extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("spring")
.password("secret")
.roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/private/**")
.authenticated()
.antMatchers("/public/**")
.permitAll()
.and()
.httpBasic();
}
}
이 구성은 모든 /private/ 매핑된 메서드에 대한 인증과 /public/ 아래의 모든 항목에 대한 공용 액세스를 정의합니다.
Spring Security 5에서 동일한 구성을 사용하면 다음 오류가 발생합니다.
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
이 오류는 메모리 내 인증을 위해 구성된 비밀번호 인코더가 없기 때문에 지정된 비밀번호를 디코딩할 수 없음을 알려줍니다 .
3. 스프링 시큐리티 5
PasswordEncoderFactories 클래스 로 Delegating PasswordEncoder 를 정의하여 이 오류를 수정할 수 있습니다 .
이 인코더를 사용하여 사용자를 구성합니다.
@Configuration
public class InMemoryAuthWebSecurityConfigurer {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
UserDetails user = User.withUsername("spring")
.password(encoder.encode("secret"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
이제 이 구성으로 BCrypt를 사용하여 메모리 내 암호를 다음 형식으로 저장합니다.
{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
자체 비밀번호 인코더 세트를 정의할 수 있지만 PasswordEncoderFactories 에서 제공되는 기본 인코더 를 사용하는 것이 좋습니다 .
Spring Security 버전 5.7.0-M2부터 Spring 은 WebSecurityConfigureAdapter의 사용을 더 이상 사용하지 않으며 WebSecurityConfigureAdapter 없이 구성을 생성할 것을 제안합니다. 이 기사에서는 이에 대해 자세히 설명합니다.
3.2. NoOpPassword인코더
어떤 이유로든 구성된 비밀번호를 인코딩하지 않으려면 NoOpPasswordEncoder 를 사용할 수 있습니다 .
이렇게 하려면 {noop} 식별자 를 사용하여 password() 메서드에 제공하는 암호 문구에 접두사를 붙이기만 하면 됩니다.
@Configuration
public class InMemoryNoOpAuthWebSecurityConfigurer {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withUsername("spring")
.password("{noop}secret")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
이렇게 하면 Spring Security는 사용자가 제공한 비밀번호와 위에서 구성한 비밀번호를 비교할 때 후드 아래에서 NoOpPasswordEncoder 를 사용합니다.
그러나 프로덕션 애플리케이션에서는 이 접근 방식을 사용해서는 안 됩니다. 공식 문서에서 알 수 있듯이 NoOpPasswordEncoder 는 레거시 구현임을 나타내기 위해 더 이상 사용되지 않으며 이를 사용하는 것은 안전하지 않은 것으로 간주 됩니다 .
3.3. 기존 암호 마이그레이션
다음과 같이 기존 비밀번호를 권장되는 Spring Security 5 표준으로 업데이트할 수 있습니다.
- 암호화된 값으로 저장된 일반 텍스트 암호 업데이트:
String encoded = new BCryptPasswordEncoder().encode(plainTextPassword);
- 알려진 인코더 식별자로 해시된 저장된 비밀번호 접두사 추가:
{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
- 저장된 암호의 인코딩 메커니즘을 알 수 없는 경우 사용자에게 암호 업데이트 요청
4. 결론
이 빠른 예제에서는 새로운 암호 저장 메커니즘을 사용하여 유효한 Spring 4 메모리 내 인증 구성을 Spring 5로 업데이트했습니다.
항상 그렇듯이 GitHub 프로젝트 에서 소스 코드를 찾을 수 있습니다 .