Skip to content

Commit

Permalink
�Feat: 비밀번호 유효성 검증 (#22)
Browse files Browse the repository at this point in the history
* Init: Add member controll dto

멤버 생성 컨트롤러 DTO를 추가함.
Github issue #3

* Feat: Change join parmeter to DTO

가입 서비스의 파라미터를 디티오로 변경함
Github issue #3

* Feat: Change return value of join method

join 번환값을 Long type으로 변경함
Github issue #3

* Init: Add member controller

createMember 컨트롤러를 생성함.
파라미터로 패스워드와 이메일 유효성 검증이 된 dto를 받음
Github issue #3

* Comment: Add comment to createMember method

멤버 생성 메서드를 설명하는 주석을 추가함
Github issue #3

* Comment: Add comment to CreateMemberDto

CreateMemberDto를 설명하는 주석을 추가함
Github issue #3

* Refactor: rename requestId to memberId

createMember에서 join의 반환값 변수명을 명확하게 수정함.
Github issue #3

* Feat: Add password pattern validation method

비밀번호 패턴 검사 시 정규식이 길어서 가독성을 위해 패턴 검증 메서드를 따로 추가하여, 생성 메서드에서 검증하도록 함.
Github issue #3

* Commend: Add comment about join return value

join의 반환값에 대한 설명을 주석에 추가함.
Github issue #3

* Test: 패스워드 검증 테스트

* Fix: password pattern validation method

숫자, 문자, 특수문자 조합 정규식을 수정함.
연속된 동일한 문자를 확인하는 로직은 match가 아닌 find 메서드를 이용하여 비밀번호 일부만 해당되어도 true를 반환하도록 수정함.
Github issue #3

* Test: Change expectable exception message

dto에서 발생하는 에러가 변경됨에따라 에러 메세지를 수정함
Github issue #3

* Refactor: remove annotation

인터페이스에 작성한 어노테이션을 삭제함
Github issue #3

---------

Co-authored-by: Seonghyeon Kim <[email protected]>
  • Loading branch information
honeyl3ee and kshshkim authored Oct 26, 2023
1 parent 39a7863 commit cdbb85f
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package beforespring.socialfeed.member.controller;

import beforespring.socialfeed.member.controller.dto.CreateMemberDto;
import beforespring.socialfeed.member.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequiredArgsConstructor
public class MemberController {

private final MemberService memberService;

/**
* 멤버 생성. 가입 요청과 가입 승인 서비스를 호출합니다.
*
* @param request 멤버 생성을 위한 dto
* @return 생성된 멤버의 아이디를 반환합니다.
*/
@PostMapping("/api/member/new")
public CreateMemberDto.Response createMember(@RequestBody @Valid CreateMemberDto.Request request) {
Long memberId = memberService.join(request);
return new CreateMemberDto.Response(memberId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package beforespring.socialfeed.member.controller.dto;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* 멤버 생성 DTO
*/
public class CreateMemberDto {

/**
* 멤버 생성 요청 dto. 이메일과 패스워드의 유효성을 검증함
*/
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
static public class Request {
@NotEmpty
private String username;
@NotEmpty
@Email(message = "이메일 형식이 올바르지 않습니다.")
private String email;
@NotEmpty
@Size(min = 10, message = "비밀번호는 최소 10자 이상이어야 합니다.")
private String password;

public Request(String username, String email, String password) {
this.username = username;
this.email = email;
validatePasswordPattern(password);
this.password = password;
}

/**
* 비밀번호 패턴 검증. 유효하지 않으면 에러를 던짐
*
* @param password 검증할 패스워드
*/
private void validatePasswordPattern(String password) {
if (isConsecutiveCharsPattern(password)) {
throw new IllegalArgumentException("동일한 문자를 3회 이상 연속으로 사용할 수 없습니다.");
}
if (!isComplexCharsPattern(password)) {
throw new IllegalArgumentException("숫자, 문자, 특수문자 중 2가지 이상을 포함해야 합니다.");
}
}

/**
* 비밀번호 패턴 검사 로직
*
* @param password 검증할 패스워드
* @return 숫자, 문자, 특수문자가 중 2개 이상을 포함하면 true, 아니면 false
*/
private boolean isComplexCharsPattern(String password) {
String complexCharsPattern = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[^A-Za-z\\d]).{10,}$|" +
"(?=.*[A-Za-z])(?=.*\\d).{10,}$|" +
"(?=.*[A-Za-z])(?=.*[^A-Za-z\\d]).{10,}$|" +
"(?=.*\\d)(?=.*[^A-Za-z\\d]).{10,}$";
Matcher matcher = Pattern.compile(complexCharsPattern).matcher(password);
return matcher.matches();
}

/**
* 비밀번호 패턴 검사 로직
*
* @param password 검증할 패스워드
* @return 동일한 문자가 3회 이상 연속되면 true, 아니면 false
*/
private boolean isConsecutiveCharsPattern(String password) {
String consecutiveCharsPattern = "(.)\\1\\1";
Matcher matcher = Pattern.compile(consecutiveCharsPattern).matcher(password);
return matcher.find();
}
}

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
static public class Response {
private Long id;

public Response(Long id) {
this.id = id;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package beforespring.socialfeed.member.service;

import beforespring.socialfeed.member.controller.dto.CreateMemberDto;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

public interface MemberService {

/**
* 가입 요청. 가입 요청시 6자리의 랜덤 코드를 이메일로 발송. (이메일 발송 생략에 대해서 논의 필요)
*
* @param username
* @param password
* @param email
* @param request 멤버 생성 요청 DTO
* @return member id
*/
void join(String username, String password, String email);
Long join(CreateMemberDto.Request request);

/**
* 가입 승인
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package beforespring.socialfeed.member.controller.dto;

import beforespring.socialfeed.member.controller.dto.CreateMemberDto.Request;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class CreateMemberDtoTest {
@Test
void password_validation_test() {
// given
String givenUsername = "givenUsername";
String givenEmail = "[email protected]";
String givenPassword = "passwdThatShou!dBe0kay";

// when then
assertThatCode(
() ->
new Request(
givenUsername,
givenEmail,
givenPassword
)
)
.describedAs("예외가 발생하지 않고 생성에 성공할것.")
.doesNotThrowAnyException();
}

@Test
@DisplayName("3회 이상 연속되는 문자는 사용이 불가능합니다.")
void password_validation_passwd_not_valid_consecutive_character() {
// given
String givenUsername = "givenUsername";
String givenEmail = "[email protected]";
String givenPassword = "paaaaswd1!!"; // a가 3번 반복되는 잘못된 패스워드

// when then
assertThatThrownBy(
() ->
new Request(
givenUsername,
givenEmail,
givenPassword
)
)
.describedAs("3회 이상 반복되는 문자에 대해서 예외가 발생해야함.")
.hasMessageContaining("동일한 문자를 3회 이상 연속으로 사용할 수 없습니다.");
}

@Test
@DisplayName("특수문자, 숫자, 문자 중 둘 이상은 포함해야함.")
void password_validation_passwd_not_valid_() {
// given
String givenUsername = "givenUsername";
String givenEmail = "[email protected]";
String givenPassword = "passwordNotOkay"; // 숫자와 특수문자가 없는 잘못된 패스워드
// when then
assertThatThrownBy(
() ->
new Request(
givenUsername,
givenEmail,
givenPassword
)
)
.describedAs("숫자, 문자, 특수문자 중 2가지 이상을 포함해야함.")
.hasMessageContaining("숫자, 문자, 특수문자 중 2가지 이상을 포함해야 합니다.");
}
}

0 comments on commit cdbb85f

Please sign in to comment.