티스토리 뷰

반응형

Spring 에서 흔히 @Autowired 로 객체를 주입받을 때 아래와 같이 사용합니다.

@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

그러면 BCryptPasBCryptPasswordEncoder 을 Singleton 으로 생성(new)해서 사용하는 것과 같은 효과를 얻을 수 있습니다.

그런데, 간혹 Interface 을 구현(Implement)한 객체 여러개를 주입받고 상황에 따라 그 중 하나를 사용하고 싶을 때에는 어떻게 해야 할까요? 보통은 ApplicationContext#getBean 을 이용해서 등록된 Bean 중 하나를 꺼내서 사용할 것입니다.

passwordEncoder = (PasswordEncoder) applicationContext.getBean("bCryptPasswordEncoder");

 

하지만, 사실 다른 방법도 존재합니다.

예를 들어 PasswordEncoder Interface 을 구현해 만든 Pbkdf2Pbkdf2PasswordEncoder Class 와 Spring Security 에서 제공하는 BCryptPasswordEncoder 을 array 나 List, Map 형태로 받아서 선택해서 사용하려고 한다고 하면 어떻게 해야 할까요?

 

먼저, Pbkdf2PasswordEncoder Class 은 다음과 같이 만듭니다. (secret 와 iteration, keylength 는 생략하겠습니다)

@Component
public class Pbkdf2PasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {

        try {

            byte[] result = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512")
                    .generateSecret(new PBEKeySpec(charSequence.toString().toCharArray(), secret.getBytes(), iteration, keylength))
                    .getEncoded();

            return Base64.getEncoder().encodeToString(result);

        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            return null;
        }

    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return encode(charSequence).equals(s);
    }

}

이렇게만 구현해두면 PasswordEncoder 을 이용해서도 단독으로 주입받을 수 있습니다.

@Autowired
private PasswordEncoder passwordEncoder;

이번에는 BcryptPasswordEncoder 을 주입받을 수 있도록 MyBcryptPasswordEncoder Class 을 만들어보겠습니다.

@Primary
@Component
public class MyBcryptPasswordEncoder implements PasswordEncoder {

    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    public MyBcryptPasswordEncoder() {
        this.bCryptPasswordEncoder = new BCryptPasswordEncoder();
    }

    @Override
    public String encode(CharSequence charSequence) {
        return bCryptPasswordEncoder.encode(charSequence);
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return bCryptPasswordEncoder.matches(charSequence, s);
    }

}

주의할 것은 @Primary 입니다. 앞서 구현한 Pbkdf2PasswordEncoder 와 MyBcryptPasswordEncoder 모두 PasswordEncoder 에 대한 구현체입니다. 그렇기 때문에 이 둘을 Component 로 등록하는 순간 충돌이 발생합니다(NoUniqueBeanDefinitionException). 그렇기 때문에 우선순위를 두어서 단독으로 사용할 경우 주입받을 우선순위를 정해주는 것입니다.

 

이렇게 생성한 뒤, 아래와 같이 PasswordEncoder 을 주입받아 보겠습니다.

@Autowired
private Map<String, PasswordEncoder> passwordEncoderMap;

그러면, Component 로 등록한 두 개의 Class 을 Map 으로 주입해주는 것을 알 수 있습니다. 당연히 Map 의 key 은 Bean Name 이 들어갑니다. 그래서 각각 myBcryptPasswordEncoder, pbkdf2PasswordEncoder 가 key 입니다.

array 나 List, Set 은 key 없이 입력되기 때문에 더 이상 설명은 필요 없을 것 같습니다.

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함