본문 바로가기
코딩 공부/web & Java

[Spring] BCryptPasswordEncoder

by 현장 2025. 4. 4.

BCryptPasswordEncoder

스프링 시큐리티(Spring Seurity) 프레임워크에서 제공하는 클래스 중 하나로 비밀번호를 암호화하는 데 사용할 수 있는 메서드를 가진 클래스입니다.

🏷️ 특징

  • BCryptPasswordEncoder는 BCrypt 해싱 함수(BCrypt hashing function)를 사용해서 비밀번호를 인코딩해주는 메서드와 사용자의 의해 제출된 비밀번호와 저장소에 저장되어 있는 비밀번호의 일치 여부를 확인해 주는 메서드를 제공합니다.
  • PasswordEncoder 인터페이스를 구현한 클래스입니다.
  • 생성자의 인자 값(verstion, strength, SecureRandom instance)을 통해서 해시의 강도를 조절할 수 있습니다.

🏷️ 의존성

// security
implementation 'org.springframework.boot:spring-boot-starter-security'

🏷️ 생성자

 

✅ 기본 생성자

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

기본 강도인 10으로 생성이 됩니다.

강도 지정 생성자

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(14);

지정된 강도인 14로 인스턴스를 생성가능하며 안에 int값을 변경하여 강도를 지정합니다.

SecureRandom 지정 생성자

SecureRandom random = new SecureRandom();
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(10, random);

강도는 그대로 10으로 적용되며, random은 솔트를 더 안전하고 제어 가능하게 생성하는 데 사용됩니다.

❗솔트(Salt)란?

해시 함수는 같은 입력에 대해 항상 같은 출력(해시 값)을 만듭니다. 그래서 같은 비밀번호를 여러 사용자가 쓰면, 해시 값이 똑같아져서 공격자가 이를 추측할 수 있습니다. 이를 방지하기 위해 "솔트(salt)"라는 랜덤 데이터를 비밀번호에 섞어서 해싱합니다.

// 솔트가 없을 시
hash("password123") ➝ a1b2c3d4

// 솔트가 있는 경우
user1: salt = "xyz", hash("xyzpassword123") ➝ 91ab4f
user2: salt = "abc", hash("abcpassword123") ➝ 82de1a

✔️ 강도

BCryptPasswordEncoder에서 강도(strength)를 설정하는 것은 해시 함수의 연산 횟수(log rounds)를 설정하는 것입니다. 이 값(4 <= strength <= 31)이 높을수록 암호를 인코딩(해싱)할 때 더 많은 계산을 수행하게 되어 보안은 강화되지만, 속도는 느려집니다.

strength 해싱 연산 횟수 특징
4 16 매우 빠름, 보안 낮음
10 1,024 기본값, 균형 잡힘
12 4,096 보안 증가, 속도 저하
14 16,384 보안 강화, 로그인 지연 가능성 있음
16 이상 65,536+ 매우 높은 보안, 속도 매우 느림

결과적으로 아래와 같이 추천을 합니다.

  • 일반 웹 애플리케이션에서는 10~12 수준이 일반적입니다.
  • 보안이 중요한 경우(예: 은행, 인증 시스템)에는 12~14까지도 고려 가능.
  • 너무 높게 설정하면 서버 부하가 심해질 수 있으니, 시스템 성능과 트래픽을 고려해 적절한 값을 선택해야 합니다.

🏷️ 메소드 구성

✅ encode()

String encodedPassword = encoder.encode("password");

주어진 비밀번호를 BCrypt로 해싱하며, 매번 다른 솔트를 사용하여 같은 비밀번호도 다른 해시값을 생성합니다.

✅ matches() 

boolean isMatch = encoder.matches("password", encodedPassword);

입력된 비밀번호와 저장된 인코딩된 비밀번호를 비교합니다. 내부적으로 저장된 해시에서 솔트를 추출하고, 입력된 비밀번호를 같은 솔트로 해싱하여 비교합니다.

✅ upgradeEncoding()

boolean needsUpgrade = encoder.upgradeEncoding(encodedPassword);

주어진 인코딩된 비밀번호가 현재 설정된 강도보다 낮은 강도로 인코딩되었는지 확인합니다. true를 반환하면 해당 비밀번호를 새로운 강도로 다시 인코딩해야 함을 의미합니다.

🏷️ 예시

@RequiredArgsConstructor
@Service
public class UserAccountService {

    private final UserAccountRepository userAccountRepository;
    
    private final ServiceUtils serviceUtils; // service 관련된 repository 로직이 있는 클래스
    private final BCryptPasswordEncoder encoder;
    private final JwtTokenProvider jwtTokenProvider; // jwt 관련 클래스


    // 회원가입
    @Transactional
    public UserAccountDto regist(String userId, String password, String email, String nickname) {
        // 회원 가입하는 userId 중복 체크
        userAccountRepository.findById(userId).ifPresent(it -> {
            // userId 존재시 로직 생략
        });

        // 회원 가입 진행
        UserAccount userAccount = userAccountRepository.save(
                UserAccount.of(userId, encoder.encode(password), email, nickname, null));

        return UserAccountDto.fromEntity(userAccount);
    }

    // 로그인
    public String login(String userId, String password) {
        // 회원 가입 체크
        UserAccount userAccount = serviceUtils.getUserAccountOrException(userId);

        // 비밀 번호 체크
        if (!encoder.matches(password, userAccount.getPassword())) {
            // 비밀 번호 없는 경우 로직 생략
        }

        // 토큰 생성
        return jwtTokenProvider.createToken(userId);
    }
}

📖 Reference

metamong-data 님의 블로그

'코딩 공부 > web & Java' 카테고리의 다른 글

[Spring] Pageable  (0) 2025.04.08
[Java] Comparable과 Comparator  (0) 2025.04.04
[Java] Stream  (0) 2025.04.02
[JPA] CRUDRepository와 JPARepository  (0) 2025.04.01
[Mockito] ArgumentMatchers 및 any,eq  (1) 2024.10.21