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
'코딩 공부 > 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 |