스프링과 JPA 기반 웹 애플리케이션 개발 #13 회원 가입 패스워드 인코딩

Jake Seo·2021년 5월 27일
0

스프링과 JPA 기반 웹 애플리케이션 개발 #13 회원 가입 패스워드 인코딩

해당 내용은 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발의 강의 내용을 바탕으로 작성된 내용입니다.

강의를 학습하며 요약한 내용을 출처를 표기하고 블로깅 또는 문서로 공개하는 것을 허용합니다 라는 원칙 하에 요약 내용을 공개합니다. 출처는 위에 언급되어있듯, 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발입니다.

제가 학습한 소스코드는 https://github.com/n00nietzsche/jakestudy_webapp 에 지속적으로 업로드 됩니다. 매 커밋 메세지에 강의의 어디 부분까지 진행됐는지 기록해놓겠습니다.


회원 가입: 패스워드 인코더

  • 절대로 패스워드를 평문으로 저장해서는 안된다.
    • Account 엔티티를 저장할 때 패스워드 인코딩하기
  • 스프링 시큐리티 권장 PasswordEncoder
    • PasswordEncoderFactories.createDelegatingPasswordEncoder()
      • 여러 해시 알고리즘을 지원하는 패스워드 인코더
      • 기본 알고리즘 bcrypt
  • 해싱 알고리즘과 솔트(salt)
    • 해싱 알고리즘을 쓰는 이유?
      • 평문 비밀번호가 유출되지 않기 위해
    • 솔트를 쓰는 이유?
      • 평문의 해싱된 값을 해커가 이미 알 때를 대비하기 위해 ex) 딕셔너리 어택
      • salt로 인해 해싱된 결과가 바뀐다.
      • salt는 매번 같을 필요 없다. 랜덤하게 주어도 된다.

AppConfig 클래스 추가

@Configuration
public class AppConfig {

    @Bean
    // 기본 생성자로 등록하는지 등록 과정 다시한번 살펴보기
    public PasswordEncoder passwordEncoder() {

        // 사실상 BCryptPasswordEncoder 를 쓰게 된다.
        // BCrypt 는 의도적으로 해싱하는데 시간을 조금 걸리게 만든다.
        // `.encode()` 메소드를 통해 평문 문자열과 salt를 주어 해싱 문자열을 만들고
        // `.matches()` 메소드를 통해 평문과 문자열을 보냈을 때, 보안 인증이 되는지 알 수 있다.
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

PasswordEncoder 타입의 객체 하나를 빈으로 올린다.

AccountService 클래스 내용 변경

    ...
    private final PasswordEncoder passwordEncoder;

    private Account saveNewAccount(SignUpForm signUpForm) {
        Account account = Account.builder()
                .email(signUpForm.getEmail())
                .nickname(signUpForm.getNickname())
                .password(passwordEncoder.encode(signUpForm.getPassword()))
    ...

패스워드를 넣을 때, 의존성 주입 받은 PasswordEncoder를 통해 .encode() 메소드를 한번 돌려준다. 그러면 스프링 시큐리티 내부의 PasswordEncoderFactories.createDelegatingPasswordEncoder() 메소드를 통해 생성된 객체가 주입되어 해당 .encode() 메소드를 수행한다.

여기서 기본 암호화 방식으로 bcrypt가 쓰이며 salt는 매번 랜덤으로 들어간다.

AccountControllerTest 클래스 내용 변경

        String email = "minsu@minsu.co.kr";
        String nickname = "minsu";
        String password = "12345678";

        mockMvc.perform(post("/sign-up")
                    .param("nickname", nickname)
                    .param("email", email)
                    .param("password", password)
                .with(csrf())) // CSRF 토큰도 mock 하기
                .andExpect(status().is3xxRedirection())
                .andExpect(view().name("redirect:/"));

        assertTrue(accountRepository.existsByEmail(email));
        assertTrue(accountRepository.existsByNickname(nickname));

        Account account = accountRepository.findByEmail(email);
        assertNotNull(account);
        assertNotEquals(account.getPassword(), password);

올바른 인풋값을 입력하고 회원가입이 됐을 때, 패스워드가 평문과 일치하지 않는 것에 대해 assertNotEquals()메소드를 통해 확인한다.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글