Skip to content

Commit fa1eaf6

Browse files
authored
[FIX] 회원가입시 비밀번호 해시 처리 (#319)
* feat: BCrypt 해시 유틸 추가 * feat: 인코딩, 매치 기능 리팩토링, 추가 * test: 패스워드 인코더 유틸 테스트 추가 * refactor: 패키지 이동 * test(util): BCryptPasswordEncoderUtil 인스턴스화 테스트 추가 - private 생성자를 이용한 인스턴스화 불가능 여부 테스트 추가 - UnsupportedOperationException 예외 발생 확인 * feat(auth): Swagger 문서화에서 UserId와 RoleId 숨김 처리 - @hidden 어노테이션 추가로 Swagger API 문서에서 UserId와 RoleId를 숨김 - io.swagger.v3.oas.annotations.Hidden 임포트 추가 * refactor: 개행 문제 해결
1 parent b4c28c1 commit fa1eaf6

File tree

6 files changed

+88
-3
lines changed

6 files changed

+88
-3
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.somemore.global.auth.annotation;
22

3+
import io.swagger.v3.oas.annotations.Hidden;
4+
35
import java.lang.annotation.ElementType;
46
import java.lang.annotation.Retention;
57
import java.lang.annotation.RetentionPolicy;
68
import java.lang.annotation.Target;
79

810
@Retention(RetentionPolicy.RUNTIME)
911
@Target(ElementType.PARAMETER)
12+
@Hidden
1013
public @interface RoleId {
1114
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.somemore.global.auth.annotation;
22

3+
import io.swagger.v3.oas.annotations.Hidden;
4+
35
import java.lang.annotation.ElementType;
46
import java.lang.annotation.Retention;
57
import java.lang.annotation.RetentionPolicy;
68
import java.lang.annotation.Target;
79

810
@Retention(RetentionPolicy.RUNTIME)
911
@Target(ElementType.PARAMETER)
12+
@Hidden
1013
public @interface UserId {
1114
}

src/main/java/com/somemore/global/auth/idpw/provider/CustomAuthenticationProvider.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.somemore.global.auth.jwt.domain.EncodedToken;
77
import com.somemore.global.auth.jwt.domain.TokenType;
88
import com.somemore.global.auth.jwt.usecase.JwtUseCase;
9+
import com.somemore.global.util.encoder.BCryptPasswordEncoderUtil;
910
import com.somemore.user.domain.User;
1011
import com.somemore.user.domain.UserRole;
1112
import com.somemore.user.usecase.UserQueryUseCase;
@@ -16,7 +17,6 @@
1617
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1718
import org.springframework.security.core.Authentication;
1819
import org.springframework.security.core.AuthenticationException;
19-
import org.springframework.security.crypto.password.PasswordEncoder;
2020
import org.springframework.stereotype.Component;
2121
import org.springframework.transaction.annotation.Transactional;
2222

@@ -28,7 +28,6 @@
2828
public class CustomAuthenticationProvider implements AuthenticationProvider {
2929

3030
private final JwtUseCase jwtUseCase;
31-
private final PasswordEncoder passwordEncoder;
3231
private final UserQueryUseCase userQueryUseCase;
3332
private final NEWVolunteerQueryUseCase volunteerQueryUseCase;
3433
private final NEWCenterQueryUseCase centerQueryUseCase;
@@ -73,7 +72,7 @@ private EncodedToken generateAccessToken(UserIdentity userIdentity) {
7372
}
7473

7574
private void validatePassword(String rawPassword, String encodedPassword) {
76-
if (!passwordEncoder.matches(rawPassword, encodedPassword)) {
75+
if (!BCryptPasswordEncoderUtil.matches(rawPassword, encodedPassword)) {
7776
throw new BadCredentialsException("비밀번호가 일치하지 않습니다.");
7877
}
7978

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.somemore.global.util.encoder;
2+
3+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
4+
import org.springframework.security.crypto.password.PasswordEncoder;
5+
6+
public class BCryptPasswordEncoderUtil {
7+
8+
private static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
9+
10+
private BCryptPasswordEncoderUtil() {
11+
throw new UnsupportedOperationException("유틸리티 클래스는 인스턴스화할 수 없습니다.");
12+
}
13+
14+
public static String encode(String rawPassword) {
15+
return PASSWORD_ENCODER.encode(rawPassword);
16+
}
17+
18+
public static boolean matches(String rawPassword, String encodedPassword) {
19+
return PASSWORD_ENCODER.matches(rawPassword, encodedPassword);
20+
}
21+
}

src/main/java/com/somemore/user/dto/UserAuthInfo.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package com.somemore.user.dto;
22

33
import com.somemore.global.auth.oauth.domain.OAuthProvider;
4+
import com.somemore.global.util.encoder.BCryptPasswordEncoderUtil;
45

56
import java.util.UUID;
67

78
public record UserAuthInfo(String accountId,
89
String accountPassword) {
10+
public UserAuthInfo(String accountId,
11+
String accountPassword) {
12+
this.accountId = accountId;
13+
this.accountPassword = BCryptPasswordEncoderUtil.encode(accountPassword);
14+
}
915

1016
public static UserAuthInfo of(String accountId, String accountPassword) {
1117
return new UserAuthInfo(accountId, accountPassword);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.somemore.global.util.encoder;
2+
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.lang.reflect.InvocationTargetException;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
10+
11+
class BCryptPasswordEncoderUtilTest {
12+
13+
@Test
14+
@DisplayName("비밀번호를 성공적으로 인코딩할 수 있다")
15+
void testEncode() {
16+
// given
17+
String rawPassword = "password123";
18+
19+
// when
20+
String encodedPassword = BCryptPasswordEncoderUtil.encode(rawPassword);
21+
22+
// then
23+
assertThat(encodedPassword).isNotNull();
24+
25+
}
26+
27+
@Test
28+
@DisplayName("비밀번호를 성공적으로 매칭시킬 수 있다")
29+
void testMatches() {
30+
// given
31+
String rawPassword = "password123";
32+
33+
// when
34+
String encodedPassword = BCryptPasswordEncoderUtil.encode(rawPassword);
35+
36+
// then
37+
assertThat(BCryptPasswordEncoderUtil.matches(rawPassword, encodedPassword)).isTrue();
38+
}
39+
40+
@Test
41+
@DisplayName("인스턴스화 할 수 없다.")
42+
void testConstruct() throws Exception {
43+
// given
44+
var constructor = BCryptPasswordEncoderUtil.class.getDeclaredConstructor();
45+
constructor.setAccessible(true);
46+
47+
// when & then
48+
assertThatThrownBy(constructor::newInstance)
49+
.isInstanceOf(InvocationTargetException.class)
50+
.extracting(Throwable::getCause)
51+
.isInstanceOf(UnsupportedOperationException.class);
52+
}
53+
}

0 commit comments

Comments
 (0)