PasswordEncoder
계약의 정의스프링 시큐리티에 사용자 암호를 검증하는 방법을 알려준다. PasswordEncoder
는 인증 프로세스에서 암호가 유효한지를 확인한다. 모든 시스템은 인코딩된 암호를 저장하며 아무도 읽을 수 없게 해시를 저장하는 게 좋다. PasswordEncoder
도 암호를 인코딩할 수 있다.
public interface PasswordEncoder {
String encode(CharSequence rawPassword);
boolean matches(CharSequence rawPassword, String encodedPassword);
default boolean upgradeEncoding(String encodedPassword) {
return false;
//true를 반환하도록 메서드를 재정의하면 인코딩 암호를 보안 향상을 위해 재인코딩한다.
}
}
PasswordEncoder
의 제공된 구현 선택NoOpPasswordEncoder
암호를 인코딩하지 않고 일반 텍스트로 유지StandardPasswordEncoder
SHA256을 이용해 해시. 구식이므로 새 구현에는 쓰지 말 것.Pbkdf2passwordEncoder
PBKDF2를 이용BCryptPasswordEncoder
bcrypt 강력 해싱 함수로 암호를 인코딩SCryptPasswordEncoder
scrypt 해싱함수로 암호를 인코딩.DelegatingPasswordEncoder
를 이용한 여러 인코딩 전략암호 일치를 위해 다양한 구현을 적용해야 하는 경우다. 인코딩 알고리즘이 변경되는 경우, 여러 종류의 해시를 지원해야 하게 되는데, DelegatingPasswordEncoder
객체를 이용할 수 있다.
PasswordEncoder
의 한 구현으로, 자체 인코딩 알고리즘은 없고 같은 계약의 다른 구현 인스턴스에 작업을 위임한다. 해시는 해당 해시를 의미하는 알고리즘의 이름을 나타내는 접두사로 시작한다.
e.g.) 암호에 {noop}
접두사가 붙으면 NoOpPasswordEncoder
에 구현을 위임함.
DelegatingPasswordEncoder
의 정의 방법//config
@Bean
public PasswordEncoder passwordEncoder(){
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("bcrypt", new BCryptPasswordEncoder());
//...
return new DelegatingPasswordEncoder("bcrypt", encoders); //default 지정
}
스프링 시큐리티는 편의상 모든 표준 제공 PasswordEncoder
의 구현에 대한 맵을 가진 DelegatingPasswordEncoder
를 생성하는 방법을 제공한다.
PasswordEncoder passwordEncoder = PasswordEncoderFactories
.createDelegatingPasswordEncoder();
SSCM
)암호화 및 복호화 함수와 키 생성 기능은 자바 언어에서 기본 제공되지 않으므로 개발자가 이런 기능에 보다 쉽게 접근하기 위한 종속성을 추가할 때 제약이 있다.
스프링 시큐리티는 별도의 라이브러리를 이용할 필요 없이 프로젝트의 종속성을 줄일 수 있는 자체 솔루션을 제공한다. 암호 인코더도 SSCM의 일부분이다.
특정한 종류의 키를 생성하는 객체로서 일반적으로 암호화나 해싱 알고리즘에 필요하다.
BytesKeyGenerator
및 StringKeyGenerator
는 키 생성기의 두 가지 주요 유형을 나타내는 인터페이스로, 팩터리 클래스 KeyGenerators
로 직접 만들 수 있다.
public interface StringKeyGenerator {
String generateKey();
}
//------------------------------------------------------
StringKeyGenerator keyGenerator = KeyGenerators.string();
String salt = keyGenerator.generateKey();
//8바이트 키를 생성하고 16진수 문자열로 인코딩하고, 메서드는 이 작업의 결과를 문자열로 반환
public interface BytesKeyGenerator{
int getKeyLength();
byte[] generateKey();
}
//------------------------------------------------------
BytesKeyGenerator keyGenerator = KeyGenerators.secureRandom();
byte [] key= keyGenerator.generateKey();
int keyLength = keyGenerator.getKeyLength();
기본 BytesKeyGenerator
는 8바이트 길이의 키를 생성한다. 다른 키 길이를 지정하려면 키 생성기 인스턴스를 얻을 때 KeyGenerators.secureRandom()
메서드에 원하는 값을 전달하면 된다.
secureRandom()
메서드는 매번 고유한 키를 생성하지만, 같은 키 생성기를 호출하면 같은 키 값을 반환하는 구현이 적합한 경우도 있다. 이 경우는 KeyGenerators.shared(int length)
메서드로 생성하면 된다.
BytesKeyGenerator keyGenerator = KeyGenerators.secureRandom(16);
//------------------------------------------------------
BytesKeyGenerator keyGenerator = KeyGenerators.shared(16);
byte [] key1 = keyGenerator.generateKey();
byte [] key2 = keyGenerator.generateKey();
//key1, key2는 동일한 값을 가짐.
암호기는 암호화와 복호화 작업을 지원하며 SSCM에는 이를 위해 BytesEncryptor
및 TextEncryptor
의 두 유형의 암호기가 정의되어 있다.
팩터리 클래스 Encryptors
는 여러 가능성을 제공하는데, BytesEncryptor
는 Encryptors.standard()
또는 Encryptors.stronger()
메서드를 이용하면 된다. 내부적으로 표준 바이트 암호기는 256바이트 AES 암호화를 이용하고, 더 강력한 암호기 인스턴스는 Encryptors.stronger()
를 호출하면 된다. 이는 256바이트 AES 암호화 작업 모드로 GCM을 이용한다. (표준모드는 CBC)
BytesEncryptor e = Encryptors.stronger(password, salt)
TextEncryptor
에는 세 주요 형식이 있는데, Encryptors.text()
, Encryptors.delux()
, Encryptors.queryableText()
메서드들이다. 이외에도 값을 암호화하지 않는 더미 TextEncryptor
를 반환하는 메서드도 있다(Encryptors.noOpText()
)
Encryptors.text()
와 Encryptors.delux()
의 경우 같은 메서드를 반복 호출해도 다른 출력이 반환되는데, 그 이유는 암호화 프로세스에 임의의 초기화 벡터가 생성되기 때문이다.
실제 상황에서는 이러한 작동 방식을 원하지 않을 수 있다. 이러한 유형의 입력을 쿼리 가능 텍스트라하는데, 이 암호기는 순차 암호화 작업에서 입력이 같으면 같은 출력을 생성하도록 한다.