1. 소개
최신 Spring Security 릴리스에서는 많은 것이 변경되었습니다. 이러한 변경 사항 중 하나는 응용 프로그램에서 암호 인코딩을 처리하는 방법입니다.
이 사용방법(예제)에서는 이러한 변경 사항 중 일부를 살펴보겠습니다.
나중에 새 위임 메커니즘을 구성하는 방법과 사용자가 인식하지 못하는 사이에 기존 암호 인코딩을 업데이트하는 방법을 살펴보겠습니다.
2. Spring Security 5.x 관련 변경 사항
Spring Security 팀 은 org.springframework.security.authentication.encoding 의 PasswordEncoder 를 더 이상 사용되지 않는 것으로 선언했습니다 . 이전 인터페이스는 임의로 생성된 솔트용으로 설계되지 않았기 때문에 이는 논리적인 움직임이었습니다. 결과적으로 버전 5는 이 인터페이스를 제거했습니다.
또한 Spring Security는 인코딩된 비밀번호를 처리하는 방식을 변경합니다. 이전 버전에서는 각 응용 프로그램이 하나의 암호 인코딩 알고리즘만 사용했습니다.
기본적으로 StandardPasswordEncoder 가 이를 처리했습니다. 인코딩에 SHA-256을 사용했습니다. 암호 인코더를 변경하면 다른 알고리즘으로 전환할 수 있습니다. 하지만 우리 애플리케이션은 정확히 하나의 알고리즘을 고수해야 했습니다.
버전 5.0에서는 암호 인코딩 위임 개념을 도입했습니다. 이제 서로 다른 암호에 대해 서로 다른 인코딩을 사용할 수 있습니다. Spring은 인코딩된 암호를 접두어로 지정하는 식별자로 알고리즘을 인식합니다.
다음은 bcrypt로 인코딩된 암호의 예입니다.
{bcrypt}$2b$12$FaLabMRystU4MLAasNOKb.HUElBAabuQdX59RWHq5X.9Ghm692NEi
맨 처음에 bcrypt가 중괄호 안에 어떻게 지정되었는지 확인하십시오.
3. 위임 구성
암호 해시에 접두사가 없으면 위임 프로세스에서 기본 인코더를 사용합니다. 따라서 기본적으로 StandardPasswordEncoder 를 얻습니다 .
따라서 이전 Spring Security 버전의 기본 구성과 호환됩니다.
버전 5에서 Spring Security는 PasswordEncoderFactories.createDelegatingPasswordEncoder()를 도입했습니다. 이 팩터리 메서드는 구성된 DelegationPasswordEncoder 인스턴스를 반환합니다 .
접두사가 없는 암호의 경우 해당 인스턴스는 방금 언급한 기본 동작을 보장합니다. 그리고 접두사를 포함하는 암호 해시의 경우 그에 따라 위임이 수행됩니다.
Spring Security 팀은 해당 JavaDoc 의 최신 버전에 지원되는 알고리즘을 나열합니다 .
물론 Spring은 우리가 이 동작을 설정할 수 있게 해준다.
다음을 지원한다고 가정해 보겠습니다.
- 새로운 기본값으로 bcrypt
- 대안 으로 스크립트
- SHA-256은 현재 사용되는 알고리즘입니다.
이 설정의 구성은 다음과 같습니다.
@Bean
public PasswordEncoder delegatingPasswordEncoder() {
PasswordEncoder defaultEncoder = new StandardPasswordEncoder();
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
DelegatingPasswordEncoder passworEncoder = new DelegatingPasswordEncoder(
"bcrypt", encoders);
passworEncoder.setDefaultPasswordEncoderForMatches(defaultEncoder);
return passworEncoder;
}
4. 암호 인코딩 알고리즘 마이그레이션
이전 섹션에서는 필요에 따라 암호 인코딩을 구성하는 방법을 살펴보았습니다. 따라서 이제 이미 인코딩된 암호를 새 알고리즘으로 전환하는 방법에 대해 작업하겠습니다.
인코딩을 SHA-256 에서 bcrypt 로 변경하고 싶지만 사용자가 비밀번호를 변경하는 것을 원하지 않는다고 상상해 봅시다 .
한 가지 가능한 해결책은 로그인 요청을 사용하는 것입니다. 이 시점에서 일반 텍스트로 자격 증명에 액세스할 수 있습니다. 현재 비밀번호를 가져와서 다시 인코딩할 수 있는 순간입니다.
결과적으로 이를 위해 Spring의 AuthenticationSuccessEvent 를 사용할 수 있습니다. 이 이벤트는 사용자가 애플리케이션에 성공적으로 로그인한 후에 발생합니다.
다음은 예제 코드입니다.
@Bean
public ApplicationListener<AuthenticationSuccessEvent>
authenticationSuccessListener( PasswordEncoder encoder) {
return (AuthenticationSuccessEvent event) -> {
Authentication auth = event.getAuthentication();
if (auth instanceof UsernamePasswordAuthenticationToken
&& auth.getCredentials() != null) {
CharSequence clearTextPass = (CharSequence) auth.getCredentials();
String newPasswordHash = encoder.encode(clearTextPass);
// [...] Update user's password
((UsernamePasswordAuthenticationToken) auth).eraseCredentials();
}
};
}
이전 스니펫에서:
- 제공된 인증 세부정보에서 일반 텍스트로 된 사용자 비밀번호를 검색했습니다.
- 새 알고리즘으로 새 암호 해시 생성
- 인증 토큰에서 일반 텍스트 암호를 제거했습니다.
기본적으로 일반 텍스트로 비밀번호를 추출하는 것은 Spring Security가 가능한 한 빨리 삭제하기 때문에 불가능합니다.
따라서 비밀번호의 일반 텍스트 버전을 유지하도록 Spring을 구성해야 합니다.
또한 인코딩 위임을 등록해야 합니다.
@Configuration
public class PasswordStorageWebSecurityConfigurer {
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.eraseCredentials(false)
.userDetailsService(getUserDefaultDetailsService())
.passwordEncoder(passwordEncoder());
return authenticationManagerBuilder.build();
}
// ...
}
5. 결론
이 빠른 기사에서는 5.x에서 사용할 수 있는 몇 가지 새로운 암호 인코딩 기능에 대해 설명했습니다.
또한 암호를 인코딩하기 위해 여러 암호 인코딩 알고리즘을 구성하는 방법도 살펴보았습니다. 또한 기존 암호를 깨지 않고 암호 인코딩을 변경할 수 있는 방법을 모색했습니다.
마지막으로 Spring 이벤트를 사용하여 암호화된 사용자 암호를 투명하게 업데이트하여 사용자에게 공개하지 않고도 인코딩 전략을 원활하게 변경할 수 있는 방법을 설명했습니다.
마지막으로 항상 그렇듯이 모든 코드 예제는 GitHub 리포지토리 에서 사용할 수 있습니다 .