Skip to content

Commit

Permalink
Member 저장 시 save 메서드 사용 및 패스워드 암호화 로직 수정 (#41)
Browse files Browse the repository at this point in the history
* chore(BE): MemberRepository 인터페이스 수정
save 메서드 사용하기 위해 기존에 구현했던 insert 메서드 삭제

* feat(BE): Member 도메인 클래스 수정
생성자 대신 정적 팩토리 메서드 사용하여 생성하도록 수정
createdAt, updatedAt, isDeleted 멤버 변수에는 생성 시 항상 같은 값이 들어가므로 인자로 따로 받지 않고 생성자에서 직접 저장
빈 생성자 구현

* feat(BE): MemberRepositoryTest 수정
Member 클래스의 정적 팩토리 메서드로 객체 생성 부분 수정
기존에 사용하던 insert 메서드 대신 saveAll 메서드를 통해 Member 저장

* feat(BE): MemberServiceImpl 수정
Member 클래스의 정적 팩토리 메서드로 Member 생성
기존에 사용하던 insert 메서드 대신 save 메서드로 Member 저장

* chore(BE): MemberSignUpValidatorTest 수정
메서드명의 통일을 위해 일부 메서드명 수정

* chore(BE): 프론트엔드와 협의를 통해 이메일, 패스워드 정규식 변경 TODO 삭제

* chore(BE): Member 도메인 클래스 수정
멤버 변수로 salt 추가
of 메서드에 salt 추가
salt getter 추가

* chore(BE): MemberRepositoryTest 수정
insertMembers 메서드에서 Member 생성하는 부분에 salt 추가

* chore(BE): PasswordEncryptor 인터페이스에 getSalt 메서드 추가

* feat(BE): PasswordEncryptorImpl 클래스 수정
salt 값 사용해서 encrypt 메서드 로직 수정
salt 값 생성하는 getSalt 메서드 구현

* chore(BE): PasswordEncryptorImplTest 수정
변경된 encrypt 메서드와 match 메서드에 맞게 테스트 코드 수정

* chore(BE): MemberServiceImpl 클래스 수정
PasswordEncryptor의 getSalt 메서드를 통해 salt 생성
생성한 salt를 사용하여 비밀번호 암호화
암호화된 비밀번호와 salt 값을 사용하여 Member 객체 생성하도록 수정
  • Loading branch information
pjs0418 authored Feb 8, 2023
1 parent 5c43b38 commit 90d6b4f
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 113 deletions.
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 28 additions & 16 deletions backend/src/main/java/dev/be/dorothy/member/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,34 @@
public class Member {
@Id
private Long idx;
private final String memberId;
private final String password;
private final String name;
private final String email;
private final String image;
private final LocalDateTime createdAt;
private final LocalDateTime updatedAt;
private final boolean isDeleted;
private final MemberRole role;

public Member(String memberId, String password, String name, String email, String image, LocalDateTime createdAt, LocalDateTime updatedAt, boolean isDeleted, MemberRole role) {
private String memberId;
private String password;
private String salt;
private String name;
private String email;
private String image;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private boolean isDeleted;
private MemberRole role;

private Member(String memberId, String password, String salt, String name, String email, MemberRole memberRole) {
this.memberId = memberId;
this.password = password;
this.salt = salt;
this.name = name;
this.email = email;
this.image = image;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.isDeleted = isDeleted;
this.role = role;
this.image = "";
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
this.isDeleted = false;
this.role = memberRole;
}

public Member() {}

public static Member of(String memberId, String password, String salt, String name, String email, MemberRole memberRole) {
return new Member(memberId, password, salt, name, email, memberRole);
}

public Long getIdx() {
Expand All @@ -41,6 +49,10 @@ public String getPassword() {
return password;
}

public String getSalt() {
return salt;
}

public String getName() {
return name;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,15 @@
package dev.be.dorothy.member.repository;

import dev.be.dorothy.member.Member;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface MemberRepository extends CrudRepository<Member, Long> {
@Modifying
@Query("insert into member values (null, :memberId, :password, :name, :email, :image, :created_at, :updated_at, :is_deleted, :role );")
void insert(
@Param("memberId") String memberId,
@Param("password") String password,
@Param("name") String name,
@Param("email") String email,
@Param("image") String image,
@Param("created_at") String createdAt,
@Param("updated_at") String updatedAt,
@Param("is_deleted") boolean isDeleted,
@Param("role") String role);

@Query("select count(*) from member where member_id = :memberId;")
int countByMemberId(
@Param("memberId") String memberId
);
int countByMemberId(@Param("memberId") String memberId);

@Query("select * from member where member_id = :memberId")
Optional<Member> findByMemberId(@Param("memberId") String memberId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package dev.be.dorothy.member.service;

import dev.be.dorothy.exception.BadRequestException;
import dev.be.dorothy.exception.InternalServerErrorException;
import dev.be.dorothy.member.Member;
import dev.be.dorothy.member.MemberRole;
import dev.be.dorothy.member.repository.MemberRepository;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Optional;

@Service
public class MemberServiceImpl implements MemberService {
private final MemberSignUpValidator memberSignUpValidator;
Expand All @@ -27,28 +23,28 @@ public MemberResDto signUp(SignUpReqDto signUpReqDto) {
memberSignUpValidator.validateId(signUpReqDto.getMemberId());
memberSignUpValidator.validateEmailRegex(signUpReqDto.getEmail());
memberSignUpValidator.validatePassword(signUpReqDto.getPassword(), signUpReqDto.getPasswordCheck());
memberRepository.insert(

String salt = passwordEncryptor.getSalt();
String hashedPassword = passwordEncryptor.encrypt(signUpReqDto.getPassword(), salt);

Member member = Member.of(
signUpReqDto.getMemberId(),
passwordEncryptor.encrypt(signUpReqDto.getPassword()),
hashedPassword,
salt,
signUpReqDto.getName(),
signUpReqDto.getEmail(),
"",
LocalDateTime.now().toString(),
LocalDateTime.now().toString(),
false,
MemberRole.MEMBER.name()
MemberRole.MEMBER
);
member = memberRepository.save(member);

Optional<Member> optionalMember = memberRepository.findByMemberId(signUpReqDto.getMemberId());
Member member = optionalMember.orElseThrow(InternalServerErrorException::new);
return MemberResDto.from(member);
}

@Override
public MemberResDto login(LoginReqDto loginReqDto) {
Member member = memberRepository.findByMemberId(loginReqDto.getMemberId())
.orElseThrow(() -> new BadRequestException("입력 정보가 올바르지 않습니다."));
passwordEncryptor.match(loginReqDto.getPassword(), member.getPassword());
passwordEncryptor.match(loginReqDto.getPassword(), member.getSalt(), member.getPassword());

return MemberResDto.from(member);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public MemberSignUpValidatorImpl(MemberRepository memberRepository) {

@Override
public void validateEmailRegex(String email) {
// TODO regex -> ^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$
String regex = "^[_a-z0-9-]+(.[_a-z0-9-]+)*@(?:\\w+\\.)+\\w+$";
validateRegex(regex, email);
}
Expand All @@ -31,7 +30,6 @@ public void validateId(String memberId) {

@Override
public void validatePassword(String password, String passwordCheck) {
// TODO regex -> ^.*(?=^.{8,15}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&+=]).*$
String regex = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[~!@#$%^&*()+|=])[A-Za-z\\d~!@#$%^&*()+|=]{8,16}$";
validateRegex(regex, password);
boolean isEquals = password.equals(passwordCheck);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package dev.be.dorothy.member.service;

public interface PasswordEncryptor {
String encrypt(String password);
void match(String password, String hashedPassword);
String encrypt(String password, String salt);

void match(String password, String hashedPassword, String salt);

String getSalt();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,41 @@

import dev.be.dorothy.exception.BadRequestException;

import java.nio.charset.StandardCharsets;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;

public class PasswordEncryptorImpl implements PasswordEncryptor {

private final MessageDigest messageDigest;

public PasswordEncryptorImpl(MessageDigest messageDigest) {
this.messageDigest = messageDigest;
}

@Override
public String encrypt(String password) {
byte[] sha256Hash = messageDigest.digest(password.getBytes(StandardCharsets.UTF_8));
StringBuilder result = new StringBuilder();
for (byte hash : sha256Hash) {
result.append(Integer.toString((0xff & hash) + 0x100, 16).substring(1));
}
return result.toString();
public String encrypt(String password, String salt) {
password += salt;
messageDigest.update(password.getBytes());

return String.format("%064x", new BigInteger(1, messageDigest.digest()));
}

@Override
public void match(String password, String hashedPassword) {
String requestedPassword = encrypt(password);
public void match(String password, String salt, String hashedPassword) {
String requestedPassword = encrypt(password, salt);
boolean equals = requestedPassword.equals(hashedPassword);
if (!equals) {
throw new BadRequestException("입력 정보가 올바르지 않습니다.");
}
}

@Override
public String getSalt() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[16];
random.nextBytes(bytes);

return new String(Base64.getEncoder().encode(bytes));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dev.be.dorothy.member.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import dev.be.dorothy.auth.AuthenticationFilter;
import dev.be.dorothy.auth.authentication.UsernameAndPasswordTokenProvider;
import dev.be.dorothy.exception.BadRequestException;
import dev.be.dorothy.member.Member;
import dev.be.dorothy.member.MemberRole;
Expand All @@ -16,16 +18,13 @@
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import java.time.LocalDateTime;

import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(controllers = MemberController.class)
@WebMvcTest({MemberController.class, UsernameAndPasswordTokenProvider.class, AuthenticationFilter.class})
@DisplayName("MemberController Test")
public class MemberControllerTest {

@Autowired
MockMvc mockMvc;

Expand All @@ -40,7 +39,7 @@ void login() throws Exception {
// given
LoginReqDto loginReqDto = new LoginReqDto("sol", "1234");
String content = objectMapper.writeValueAsString(loginReqDto);
Member member = new Member("sol", "1234", "sol", "", "", LocalDateTime.now(), LocalDateTime.now(), false, MemberRole.MEMBER);
Member member = Member.of("sol", "1234", "2p7VxertGPCkNfnr", "sol", "", MemberRole.MEMBER);
given(memberService.login(loginReqDto)).willReturn(MemberResDto.from(member));

// when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

Expand All @@ -22,57 +21,36 @@ public class MemberRepositoryTest {

@BeforeAll
void insertMembers() {
Member member1 = new Member(
Member member1 = Member.of(
"dorothy",
"abcd1234",
"2p7VxertGPCkNfnr",
"dorothy",
"[email protected]",
"",
LocalDateTime.now(),
LocalDateTime.now(),
false,
MemberRole.MEMBER
);

Member member2 = new Member(
Member member2 = Member.of(
"lucas",
"abcd1234",
"RCDlu97BoefSufzH",
"lucas",
"[email protected]",
"",
LocalDateTime.now(),
LocalDateTime.now(),
false,
MemberRole.MEMBER
);

Member member3 = new Member(
Member member3 = Member.of(
"skywalker",
"abcd1234",
"N27TS6t8Vj9JrzAz",
"skywalker",
"[email protected]",
"",
LocalDateTime.now(),
LocalDateTime.now(),
false,
MemberRole.MEMBER
);

List<Member> members = List.of(member1, member2, member3);

for (Member member: members) {
memberRepository.insert(
member.getMemberId(),
member.getPassword(),
member.getName(),
member.getEmail(),
member.getImage(),
member.getCreatedAt().toString(),
member.getUpdatedAt().toString(),
member.isDeleted(),
member.getRole().name()
);
}
memberRepository.saveAll(members);
}

@AfterAll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ void validateIdExists() {
@DisplayName("로그인 테스트 - 실패 케이스(password가 일치하지 않는 경우)")
void validatePassword() {
LoginReqDto loginReqDto = new LoginReqDto("abcd1234", "a1234567!");
String salt = "2p7VxertGPCkNfnr";
String hashedPassword = "45a49cbdfe0e5b676579f409c96f58759b44cfc907e78d6c2c3fe38a9c67b0cf";

lenient().doThrow(BadRequestException.class)
.when(passwordEncryptor)
.match(loginReqDto.getPassword(), hashedPassword);
.match(loginReqDto.getPassword(), salt, hashedPassword);

BadRequestException badRequestException =
Assertions.assertThrows(BadRequestException.class, () ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class MemberSignUpValidatorTest {

@Test
@DisplayName("이메일이 정규식에 부합하는지 테스트 - 실패 케이스")
void emailRegexValidateFailTest() {
void validateEmailRegexFailTest() {
String case1 = "";
String case2 = "example";
String case3 = "@";
Expand All @@ -43,15 +43,15 @@ void emailRegexValidateFailTest() {

@Test
@DisplayName("이메일이 정규식에 부합하는지 테스트 - 성공 케이스")
void emailRegexValidateSuccessTest() {
void validateEmailRegexSuccessTest() {
String case1 = "[email protected]";

assertDoesNotThrow(() -> memberSignUpValidatorImpl.validateEmailRegex(case1));
}

@Test
@DisplayName("ID 중복 체크 - 중복인 경우")
void idDuplicateValidate() {
void validateDuplicatedId() {
String case1 = "abcd1234";
given(memberRepository.countByMemberId(case1)).willReturn(1);

Expand All @@ -64,7 +64,7 @@ void idDuplicateValidate() {

@Test
@DisplayName("ID가 정규식에 부합하는지 테스트 - 실패 케이스")
void idRegexValidate() {
void validateIdRegex() {
String case1 = "";
String case2 = "한글";
String case3 = "???";
Expand Down
Loading

0 comments on commit 90d6b4f

Please sign in to comment.