Project

[Project] Lv_6 스케줄 프로젝트(심화)

kimyongjun0129 2025. 5. 22. 17:51

 


 

요구사항

Lv.3에서 추가한 비밀번호 필드에 들어가는 비밀번호를 암호화합니다.

  • ✅ 암호화를 위한 PasswordEncoder를 직접 만들어 사용합니다.

 


 

요구 구현

build.gradle

더보기
dependencies {
    implementation 'at.favre.lib:bcrypt:0.10.2'
}
  • `at.favre.lib`라는 그룹에서 제공하는 `bcrypt` 라이브러리의 `버전 0.10.2`를 프로젝트에 추가합니다.
  • Bcrypt 해시 함수를 Java에서 사용할 수 있게해주는 라이브러리입니다.
    • 평문 비밀번호를 안전하게 해싱합니다. (암호화)
    • 해시된 비밀번호와 사용자가 입력한 비밀번호를 비교하여 검증

 

 

PasswordEncoder

더보기
@Component
public class PasswordEncoder {

    public String encode(String rawPassword) {
        return BCrypt.withDefaults().hashToString(BCrypt.MIN_COST, rawPassword.toCharArray());
    }

    public boolean matches(String rawPassword, String encodedPassword) {
        BCrypt.Result result = BCrypt.verifyer().verify(rawPassword.toCharArray(), encodedPassword);
        return result.verified;
    }
}

1. encode() 코드 설명

  • `BCrypt.withDefaults()` : 기본 설정을 사용해 Bcrypt 인스턴스를 생성합니다
  • `hashToString(...)` : 비밀번호를 해싱하여 문자열 형태의 해시값을 반환합니다.
    • `BCrypt.MIN_COST` : `Cost Factor`가 들어가는 자리입니다.
    • `rawPassword.toCharArray()` : 보안상 char 배열로 변환 (Java에서는 문자열은 불변이므로 보안상 취약할 수 있습니다.)
  • 출력 결과

 

더보기

Cost Factor란?

 

`Cost Factor` : 해싱 알고리즘의 복잡도를 조절하는 숫자입니다.

`2^cost`만큼의 연산을 수행하므로, 값이 클수록 해싱 속도는 느려지고 보안이 강해집니다.

Cost Factor 연산 횟수 보안 수준 속도
4 (`MIN_COST`) 2⁴ = 16 회 매우 낮음 (테스트용) 매우 빠름
10 (기본값) 2¹⁰ = 1,024 회 보통 (실사용 가능) 중간
12 4,096 회 안전함 느림
14+ 16,384+ 회 매우 안전 느림

 

다음과 같이 상수로 값이 정의되어있습니다.

 


 

2. matches 코드 설명

  • 입력
    • `rawPassword` : 사용자가 로그인할 때 입력한 평문 비밀번호입니다.
    • `encodePassword` : 저장된 해시 비밀번호 (데이터베이스에서 꺼낸 값)
  • 동작
    • `BCrypt.verifyer().verify(...) : 주어진 평문 비밀번호를 기존 해시값과 비교
    • `result.verified` : 비밀번호가 일치하면 `true`, 아니면 `false`
  • 출력 : 두 비밀번호가 일치하는지 여부 (`true`/ `false`)

 

 

LoginService

더보기
@Service
@RequiredArgsConstructor
public class LoginService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public void login(LoginRequestDto requestDto, HttpServletRequest request) {
        // 유저 이메일 검증
        User user = userRepository.findByEmailOrElseThrow(requestDto.getEmail());

        // 비밀 번호 검증
        boolean matches = passwordEncoder.matches(requestDto.getPassword(), user.getPassword());

        // 비밀 번호 검증 후 예외 발생
        if (!matches) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "This password is incorrect.");
        }

        HttpSession session = request.getSession();

        session.setAttribute("login-userId", user.getId());
    }
}

 


 

회고

DB에 비밀번호가 평문으로 저장되면, 개발자나 해커가 DB에 접속만 할 수 있다면 사용자의 비밀번호는 그대로 노출이되기 때문에 이전 프로젝트에서 의문점을 갖고있었습니다. 그래서 이번에 해시 알고리즘을 통해 비밀번호를 암호화하는 과정을 거치면서 의문점이 해결되었습니다.