-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BE] 회원가입 기능을 구현한다. #774
[BE] 회원가입 기능을 구현한다. #774
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수고하셨습니다 카피!
비밀번호 암호화 시 필요한 Salt
찾아보니 이메일로 salt를 생성하는 건 보안적으로 위험한 방법이라고 하더라고요
따라서 랜덤으로 생성한 뒤에 구분자를 통해서 비밀번호와 salt를 분리해서 저장하는 것이 좋을 것 같아요
이때 제가 생각하는 문제점은 password에 해당 구분자가 들어가는 경우가 있을 수 있어서 저희가 구분자로 사용하는 문자는 password에서 사용할 수 없도록 정책을 지정해야 될 것 같습니다
제약 조건
저는 PASSWORD랑 LoginType 유니크 제약 조건 좋습니다~
this.authService = authService; | ||
this.cookieProvider = cookieProvider; | ||
this.cookieResolver = cookieResolver; | ||
@PostMapping("/v1/register") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oauth와 구분되는 path를 사용하는 것이 좋을 것 같은데 /v1/local-auth/register
어떨까요?
혹은 다른 적절한 path가 있다면 좋을 것 같네요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반영했습니다!
public class Password { | ||
|
||
private static final Pattern PASSWORD_PATTERN = | ||
Pattern.compile("^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{6,}$"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용한 PASSWORD 패턴 전략도 본문에 적어 주시면 좋을 거서 같아요!
+ "WHERE u.email = :email " | ||
+ "AND u.loginType = :loginType " | ||
+ "AND u.deleted = false") | ||
boolean existsByEmailAndUserType(Email email, LoginType loginType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저희 쿼리문에서는 JPA 사용하지 않고 실제 쿼리를 작성하기로 했어서 value값으로 조회해야 될 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
확인해 봤는데 그렇게 작성하려면 nativeQuery 옵션을 켜야 되더라고요!
ENUM에 대해서는 따로 값을 꺼낼 필요는 없을 것 같아요~
실제 쿼리는 Id 같은 부분에 대해서만 적용하면 될 것 같습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Email email, LoginType loginType) 대신에 (String email, LoginType loginType)로 받으라는 것인가요??
일단 해담 메서드가 로직이 바뀜에 따라 사용되지 않아 삭제했습니다!
INSERT INTO users(name, email, user_type, login_type, created_at, modified_at, deleted) | ||
VALUES ('방방이', '[email protected]', 'USER', 'LOCAL', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false); | ||
INSERT INTO users(name, email, password, user_type, login_type, created_at, modified_at, deleted) | ||
VALUES ('방방이', '[email protected]', 'password1234', 'USER', 'LOCAL', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 VALUE 넣을 때 암호화된 값으로 넣어야 될 것 같아요!
|
||
//then | ||
User findUser = userRepository.findById(userId).orElseThrow(); | ||
assertThat(findUser.getPassword().getValue()).isNotEqualTo(password); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
서비스 테스트니까 암호화한 결과값이랑 같은지 보는 게 좋을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
내부에서 salt를 생성하기 때문에 암호화한 결과값을 동일하게 생성하는게 마땅치 않은 것 같네요
PasswordEncoder의 메서드 시그니처를 바꾸지 않는 이상 테스트 하기가 어려워 현재로써는 방법이 떠오르지 않아서 일단 유지했습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Salt를 주입받는 식으로 변경해서 값이 같은지에 대한 테스트로 변경했습니다!!!
import static org.assertj.core.api.Assertions.assertThatCode; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
class EmailTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트를 꼼꼽하게 해 주셨네요 👍
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
카피 전체적으로 잘 구현해주셨네요 👍
- 랜덤으로 Salt 생성하는게 안전할 것 같아요.
- 하나의 암호화된 문자열이라는 생각이 들어 굳이 따로 테이블을 빼지 않고 password와 같이 저장하는게 좋을 것 같아요.
- 안 그래도 필요했는데 유니크 제약조건 좋습니다!
public void validateEmail(String email) { | ||
if(!EMAIL_PATTERN.matcher(email).matches()) { | ||
throw new BangggoodException(ExceptionCode.EMAIL_INVALID_FORMAT); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public void validateEmail(String email) { | |
if(!EMAIL_PATTERN.matcher(email).matches()) { | |
throw new BangggoodException(ExceptionCode.EMAIL_INVALID_FORMAT); | |
} | |
} | |
public void validateEmailPattern(String email) { | |
if(!EMAIL_PATTERN.matcher(email).matches()) { | |
throw new BangggoodException(ExceptionCode.EMAIL_INVALID_FORMAT); | |
} | |
} |
아래 Password 포함해서 무엇을 검증하는지 이름으로 바로 알 수 있으면 좋겠어요!
return Base64.getEncoder().encodeToString(hash); | ||
} catch (NoSuchAlgorithmException | UnsupportedEncodingException | | ||
InvalidKeySpecException e) { | ||
throw new RuntimeException(e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기는 왜 커스텀 예외로 던지지 않나용?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해시 에러로 바꿔서 던졌습니다!
|
||
private static final Pattern PASSWORD_PATTERN = | ||
Pattern.compile("^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{6,}$"); | ||
private static final PasswordEncoder passwordEncoder = new PasswordEncoder(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PasswordEncoder가 이미 빈에 등록되어 있는데 계속 생성하도록 구현한 이유가 있나요??
PasswordEncoder.encode()값으로 Password 객체를 생성하는게 더 자연스러울 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PasswordEncoder를 static 클래스로 변경하였습니다!
private static final Pattern EMAIL_PATTERN = | ||
Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주석 사용을 지양하긴 하지만, 빠르게 이해하기 쉽게 주석으로 간단히 설명 적어주면 좋을 것 같아요~
# Conflicts: # backend/bang-ggood/src/test/java/com/bang_ggood/auth/service/AuthServiceTest.java
@Component | ||
public class PasswordEncoder { | ||
|
||
public String encode(String password) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
카피! 해당 부분 잘 바꿔주셨는데 추후 재사용성을 고려하여 encode 로직에서 salt를 받아야 될 것 같은데 어떻게 생각하시나요? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋은 방법인 것 같아요!
encode를 외부에서 salt를 받도록 변경했습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수고하셨습니다~~!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
沐雨櫛風(목우즐풍)하셨습니다!
|
||
public Password(String value) { | ||
validatePasswordPattern(value); | ||
this.value = passwordEncoder.encode(value, passwordEncoder.getSalt()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- static으로 만들어도 무방할 것 같은데, 추후에 bean이 필요한 상황을 생각하고 있다면 반영하지 않으셔도 됩니다.
- encode(String value) 메서드도 있으면 사용하기 편할 것 같아요~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 빈에 대한 판단은 로그인 PR에서 반영하는 게 좋을 것 같아 그때 생각해보겠습니다!
- encodeWithGeneralSalt(String password) 메서드를 만들어서 따로 처리했습니다!
password VARCHAR(255), | ||
user_type VARCHAR(255) NOT NULL, | ||
login_type VARCHAR(255) NOT NULL, | ||
created_at TIMESTAMP(6), | ||
modified_at TIMESTAMP(6), | ||
deleted BOOLEAN | ||
deleted BOOLEAN, | ||
CONSTRAINT unique_email_login_type UNIQUE (email, login_type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prod 환경에 배포할 때 데이터 정합성 일치하도록 주의해주세요!
❗ Issue
✨ 구현한 기능
📢 논의하고 싶은 내용
비밀번호 암호화 시 필요한 Salt 생성 방법
현재 비밀번호 암호화 시 Salt가 필요한데, 사용자마다 고유한 값이 필요해 이메일을 사용해 생성하고 있습니다.
그래서 PasswordEncoder의 encode() 호출시 email, password를 넘겨야합니다.
하지만 비밀번호 정책을 검증하는 책임이 Password 클래스에 있다보니, 순수한 비밀번호 값을 생성자에 넘겨야합니다.
그래서 Password 클래스가 PasswordEncoder를 필드로 가지고 있게 되는데, 암호화 시 이메일이 필요해서 Password 생성자에 email을 같이 넘겨야 되는 상황입니다.
개선 가능 방안은 다음과 같습니다.
추가적인 다른 의견 있으시면 논의하면 좋을 것 같습니다.
비밀번호 암호화 시 필요한 Salt 저장 방법
만약 이메일 대신 랜덤하고 유저마다 고유한 값으로 Salt를 생성한다면 저장할 방법이 필요합니다.
세가지 방안을 생각했습니다.
vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa
같이 저장됐을 시 처음 22개 문자는 salt값, 나머지는 암호화된 비밀번호입니다.Salt 생성 시 email을 사용하지 않도록 바뀐다면 고민해봐야할 것 같습니다.
회원가입 동시성 처리
현재 코드에서 회원 가입시 같은 이메일로 요청이 동시에 온다면 둘다 회원가입 되는 문제가 발생할 수 있습니다.
User 테이블의 email과 LoginType을 같이 unique 제약조건을 설정하면 해당 문제를 해결할 수 있습니다.
만약 제약 조건 추가시 코드는 다음과 같이 작성할 수 있을 것 같습니다.
현재 테이블에 유니크 제약조건이 안걸려 있기 때문에 제약조건 거는 것에 대해 의견 부탁드립니다.
🎸 기타