Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public SignUpResponse signUp(SignUpRequest request, HttpSession session){

emailSessionRepository.deleteById(sessionId);

verificationService.deleteSession(sessionId);
return new SignUpResponse(member.getId());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import es.princip.ringus.domain.exception.SignUpErrorCode;
import es.princip.ringus.domain.member.MemberRepository;
import es.princip.ringus.global.exception.CustomRuntimeException;
import es.princip.ringus.presentation.auth.dto.request.GenerateCodeRequest;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpSession;
import jakarta.transaction.Transactional;
Expand Down Expand Up @@ -35,14 +36,21 @@ public void init(){
}

@Transactional
public void generateVerificationCode(String email) {
if(memberRepository.existsByEmail(email)){
throw new CustomRuntimeException(SignUpErrorCode.DUPLICATE_EMAIL);
public void generateVerificationCode(GenerateCodeRequest request) {
if(request.isPasswordReset()) {
if (!memberRepository.existsByEmail(request.email())) {
throw new CustomRuntimeException(SignUpErrorCode.NOT_FOUND_MEMBER);
}
}
else {
if(memberRepository.existsByEmail(request.email())) {
throw new CustomRuntimeException(SignUpErrorCode.DUPLICATE_EMAIL);
}
}

EmailVerification verification = EmailVerification.of(email);
EmailVerification verification = EmailVerification.of(request.email());

emailSendService.sendMimeMessage(email, verification.getVerificationCode());
emailSendService.sendMimeMessage(request.email(), verification.getVerificationCode());

verificationRepository.save(verification);
}
Expand Down Expand Up @@ -91,6 +99,13 @@ public void verifySession(String email, HttpSession session){
throw new CustomRuntimeException(EmailErrorCode.SESSION_EMAIL_MISMATCH);
}

sessionRepository.delete(emailSession);
}

@Transactional
public void deleteSession(String email) {
if(!sessionRepository.existsById(email)) {
throw new CustomRuntimeException(EmailErrorCode.SESSION_NOT_FOUND);
}
sessionRepository.deleteById(email);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package es.princip.ringus.application.member.service;

import es.princip.ringus.application.auth.service.EmailVerificationService;
import es.princip.ringus.domain.exception.MemberErrorCode;
import es.princip.ringus.domain.exception.SignUpErrorCode;
import es.princip.ringus.domain.member.Member;
Expand All @@ -16,6 +17,8 @@
import es.princip.ringus.presentation.member.dto.MemberResponse;
import es.princip.ringus.presentation.member.dto.MenteeProfileResponse;
import es.princip.ringus.presentation.member.dto.MentorProfileResponse;
import es.princip.ringus.presentation.member.dto.PasswordUpdateRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
Expand All @@ -31,9 +34,10 @@
public class MemberService {
private final MemberRepository memberRepository;
private final MentorRepository mentorRepository;
private final MenteeRepository menteeRepository;
private final MenteeRepository menteeRepository;
private final PasswordEncoder passwordEncoder;
private final MentoringRepository mentoringRepository;
private final EmailVerificationService emailVerificationService;

/**
* 회원 저장 (이메일 인증 후 회원가입 진행)
Expand Down Expand Up @@ -77,6 +81,23 @@ public MemberResponse getMember(Long memberId) {
return MemberResponse.of(member);
}

@Transactional
public void updatePassword(PasswordUpdateRequest request, HttpSession session) {
emailVerificationService.verifySession(request.email(), session);

Member member = memberRepository.findByEmail(request.email())
.orElseThrow(() -> new CustomRuntimeException(SignUpErrorCode.NOT_FOUND_MEMBER));

if (passwordEncoder.matches(request.newPassword(), member.getPassword())) {
throw new CustomRuntimeException(MemberErrorCode.DUPLICATE_EXISTING_PASSWORD);
}

member.updatePassword(request.newPassword(), passwordEncoder);

emailVerificationService.deleteSession(request.email());

}

public boolean isUniqueNickname(String nickname) {
return !mentorRepository.existsByNickname(nickname) && !menteeRepository.existsByNickname(nickname);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

public enum EmailErrorCode implements ErrorCode {

SUCESSS_VALILDATION(HttpStatus.OK, "성공적으로 인증되었습니다"),
SUCESSS_VAILDATION(HttpStatus.OK, "성공적으로 인증되었습니다"),
TTL_EXPIRED(HttpStatus.FORBIDDEN, "TTL 만료"),
ERROR_EXCEEDED_ATTEMPTS(HttpStatus.FORBIDDEN, "인증번호 틀림 횟수 5회 이상, 새로운 인증번호를 발급 받아주세요"),
ERROR_INVALID_CODE(HttpStatus.BAD_REQUEST, "인증번호가 틀립니다"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ public enum MemberErrorCode implements ErrorCode {

SESSION_EXPIRED(HttpStatus.UNAUTHORIZED, "세션이 거부됨"),
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "멤버를 찾을 수 없음"),
MEMBER_TYPE_DIFFERENT(HttpStatus.BAD_REQUEST, "다른 멤버 타입");
MEMBER_TYPE_DIFFERENT(HttpStatus.BAD_REQUEST, "다른 멤버 타입"),
INVAILD_PASSWORD(HttpStatus.BAD_REQUEST, "비밀번호가 유효하지 않음"),
DUPLICATE_EXISTING_PASSWORD(HttpStatus.BAD_REQUEST, "이미 사용 중인 비밀번호입니다.");

MemberErrorCode(HttpStatus status, String message) {
this.status = status;
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/es/princip/ringus/domain/member/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,8 @@ public boolean isMentor() {
public boolean isMentee() {
return this.memberType == MemberType.ROLE_MENTEE;
}

public void updatePassword(String newPassword, PasswordEncoder passwordEncoder) {
this.password = passwordEncoder.encode(newPassword);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public ResponseEntity<Map<String, Object>> handleCustomRuntimeException(CustomRu
Map<String, Object> response = new LinkedHashMap<>();
response.put("status", ex.getStatus().value());
response.put("message", ex.getMessage());
response.put("code", ex.getCode());

return ResponseEntity.status(ex.getStatus()).body(response);
}
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/es/princip/ringus/global/util/PasswordVaildator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package es.princip.ringus.global.util;

import es.princip.ringus.domain.exception.MemberErrorCode;
import es.princip.ringus.global.exception.CustomRuntimeException;
import java.util.regex.Pattern;

public class PasswordVaildator {
private static final String PASSWORD_PATTERN = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*_+=/])[A-Za-z\\d!@#$%^&*_+=/]{8,20}$";

public static void validate(String password) {
if (!Pattern.matches(PASSWORD_PATTERN, password)) {
throw new CustomRuntimeException(MemberErrorCode.INVAILD_PASSWORD);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import es.princip.ringus.application.auth.service.EmailVerificationService;
import es.princip.ringus.global.util.ApiResponseWrapper;
import es.princip.ringus.global.util.CookieUtil;
import es.princip.ringus.global.util.PasswordVaildator;
import es.princip.ringus.presentation.auth.dto.request.EmailVerifyRequest;
import es.princip.ringus.presentation.auth.dto.request.GenerateCodeRequest;
import es.princip.ringus.presentation.auth.dto.request.LoginRequest;
Expand All @@ -14,6 +15,7 @@
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
Expand All @@ -23,8 +25,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.URI;

@Slf4j
@RestController
@RequiredArgsConstructor
Expand All @@ -38,6 +38,8 @@ public class AuthController implements AuthControllerDocs{
@PostMapping("/signup")
public ResponseEntity<?> signUp(@Valid @RequestBody SignUpRequest request, HttpSession session, HttpServletResponse httpResponse) {

PasswordVaildator.validate(request.password());

SignUpResponse response = authService.signUp(request, session);

CookieUtil.deleteCookie(httpResponse, "JSESSIONID");
Expand Down Expand Up @@ -76,7 +78,7 @@ public ResponseEntity<ApiResponseWrapper<Void>> logout(HttpSession session, Http

@PostMapping("/email/code")
public ResponseEntity<ApiResponseWrapper<Void>> requestCode(@Valid @RequestBody GenerateCodeRequest request) {
emailVerificationService.generateVerificationCode(request.email());
emailVerificationService.generateVerificationCode(request);

return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "인증번호가 발급되었습니다"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@

public record GenerateCodeRequest(
@Email @NotBlank
String email
String email,

@NotBlank
Boolean isPasswordReset
) { }
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ public record SignUpRequest(

@NotBlank(message = "비밀번호를 입력해주세욘.")
@Size(min = 8, max = 20, message = "비밀번호는 8~20자여야 합니다.")
@Pattern(
regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*_+=/])[A-Za-z\\d!@#$%^&*_+=/]{8,20}$",
message = "비밀번호는 8~20자의 대소문자, 숫자, 특수문자로 구성해야 합니다."
)
String password,

@NotNull Set<ServiceTermAgreementRequest> serviceTerms
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
import es.princip.ringus.global.annotation.SessionCheck;
import es.princip.ringus.global.annotation.SessionMemberId;
import es.princip.ringus.global.util.ApiResponseWrapper;
import es.princip.ringus.global.util.CookieUtil;
import es.princip.ringus.global.util.PasswordVaildator;
import es.princip.ringus.presentation.member.dto.MemberResponse;
import es.princip.ringus.presentation.member.dto.PasswordUpdateRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -39,4 +44,22 @@ public ResponseEntity<ApiResponseWrapper<Boolean>> isUniqueNickname(@RequestPara
return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, response));
}

@SessionCheck
@PatchMapping("/password")
public ResponseEntity<ApiResponseWrapper<Void>> updatePassword(
@RequestBody PasswordUpdateRequest request,
HttpSession session,
HttpServletResponse httpResponse
){
PasswordVaildator.validate(request.newPassword());

memberService.updatePassword(request, session);

CookieUtil.deleteCookie(httpResponse, "JSESSIONID");

session.invalidate();

return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "비밀번호가 변경되었습니다."));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package es.princip.ringus.presentation.member.dto;

public record PasswordUpdateRequest(
String email,
String newPassword
) { }