diff --git a/build.gradle b/build.gradle index ac3a27e..171f5c5 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,9 @@ dependencies { runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3' + //crypto + implementation 'org.mindrot:jbcrypt:0.4' + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' diff --git a/src/main/java/com/seong/shoutlink/domain/auth/SimplePasswordEncoder.java b/src/main/java/com/seong/shoutlink/domain/auth/SimplePasswordEncoder.java deleted file mode 100644 index 8ed1255..0000000 --- a/src/main/java/com/seong/shoutlink/domain/auth/SimplePasswordEncoder.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seong.shoutlink.domain.auth; - -public class SimplePasswordEncoder implements PasswordEncoder { - - @Override - public String encode(String rawPassword) { - return new StringBuilder(rawPassword).reverse().toString(); - } - - @Override - public boolean isMatches(String rawPassword, String encodedPassword) { - return encode(rawPassword).equals(encodedPassword); - } - - @Override - public boolean isNotMatches(String rawPassword, String encodedPassword) { - return !isMatches(rawPassword, encodedPassword); - } -} diff --git a/src/main/java/com/seong/shoutlink/global/auth/crypto/BCryptPasswordEncoder.java b/src/main/java/com/seong/shoutlink/global/auth/crypto/BCryptPasswordEncoder.java new file mode 100644 index 0000000..83a2070 --- /dev/null +++ b/src/main/java/com/seong/shoutlink/global/auth/crypto/BCryptPasswordEncoder.java @@ -0,0 +1,22 @@ +package com.seong.shoutlink.global.auth.crypto; + +import com.seong.shoutlink.domain.auth.PasswordEncoder; +import org.mindrot.jbcrypt.BCrypt; + +public class BCryptPasswordEncoder implements PasswordEncoder { + + @Override + public String encode(String rawPassword) { + return BCrypt.hashpw(rawPassword, BCrypt.gensalt()); + } + + @Override + public boolean isMatches(String rawPassword, String encodedPassword) { + return BCrypt.checkpw(rawPassword, encodedPassword); + } + + @Override + public boolean isNotMatches(String rawPassword, String encodedPassword) { + return !isMatches(rawPassword, encodedPassword); + } +} diff --git a/src/main/java/com/seong/shoutlink/global/config/AuthConfig.java b/src/main/java/com/seong/shoutlink/global/config/AuthConfig.java index 58a4974..fe6b9ef 100644 --- a/src/main/java/com/seong/shoutlink/global/config/AuthConfig.java +++ b/src/main/java/com/seong/shoutlink/global/config/AuthConfig.java @@ -2,9 +2,9 @@ import com.seong.shoutlink.domain.auth.JwtProvider; import com.seong.shoutlink.domain.auth.PasswordEncoder; -import com.seong.shoutlink.domain.auth.SimplePasswordEncoder; import com.seong.shoutlink.global.auth.authentication.AuthenticationContext; import com.seong.shoutlink.global.auth.authentication.JwtAuthenticationProvider; +import com.seong.shoutlink.global.auth.crypto.BCryptPasswordEncoder; import com.seong.shoutlink.global.auth.jwt.JJwtProvider; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -25,7 +25,7 @@ public JwtProvider jwtProvider( @Bean public PasswordEncoder passwordEncoder() { - return new SimplePasswordEncoder(); + return new BCryptPasswordEncoder(); } @Bean diff --git a/src/test/java/com/seong/shoutlink/global/auth/crypto/BCryptPasswordEncoderTest.java b/src/test/java/com/seong/shoutlink/global/auth/crypto/BCryptPasswordEncoderTest.java new file mode 100644 index 0000000..1d2dfd6 --- /dev/null +++ b/src/test/java/com/seong/shoutlink/global/auth/crypto/BCryptPasswordEncoderTest.java @@ -0,0 +1,104 @@ +package com.seong.shoutlink.global.auth.crypto; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class BCryptPasswordEncoderTest { + + BCryptPasswordEncoder bCryptPasswordEncoder; + + @BeforeEach + void setUp() { + bCryptPasswordEncoder = new BCryptPasswordEncoder(); + } + + @Nested + @DisplayName("encode 호출 시") + class EncodeTest { + + @Test + @DisplayName("성공: 일방향 해싱됨") + void encode() { + //given + String password = "asdf1234!"; + + //when + String encoded = bCryptPasswordEncoder.encode(password); + + //then + assertThat(encoded).isNotEqualTo(password); + } + } + + @Nested + @DisplayName("matches 호출 시") + class MatchesTest { + + @Test + @DisplayName("성공: 플레인 텍스트와 해시값이 동일하면 true") + void matches_ThenTrue() { + //given + String password = "asdf1234!"; + String encoded = bCryptPasswordEncoder.encode(password); + + //when + boolean result = bCryptPasswordEncoder.isMatches(password, encoded); + + //then + assertThat(result).isTrue(); + } + + @Test + @DisplayName("성공: 플레인 텍스트와 해시값이 동일하지 않으면 false") + void noneMatches_ThenFalse() { + //given + String password = "asdf1234!"; + String encoded = bCryptPasswordEncoder.encode(password); + String noneMatches = password + "a"; + + //when + boolean result = bCryptPasswordEncoder.isMatches(noneMatches, encoded); + + //then + assertThat(result).isFalse(); + } + } + + @Nested + @DisplayName("noneMatches 호출 시") + class NoneMatchesTest { + + @Test + @DisplayName("성공: 플레인 텍스트와 해시값이 동일하지 않으면 true") + void noneMatches_ThenTrue() { + //given + String password = "asdf1234!"; + String encoded = bCryptPasswordEncoder.encode(password); + String noneMatch = password + "a"; + + //when + boolean result = bCryptPasswordEncoder.isNotMatches(noneMatch, encoded); + + //then + assertThat(result).isTrue(); + } + + @Test + @DisplayName("성공: 플레인 텍스트와 해시값이 동일하면 false") + void matches_ThenFalse() { + //given + String matches = "asdf1234!"; + String encoded = bCryptPasswordEncoder.encode(matches); + + //when + boolean result = bCryptPasswordEncoder.isNotMatches(matches, encoded); + + //then + assertThat(result).isFalse(); + } + } +}