Skip to content

Commit

Permalink
[feat] : 비밀번호 찾기, refresh 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
BlackBean99 committed Sep 3, 2023
1 parent 6a9ca9e commit ec905e2
Show file tree
Hide file tree
Showing 31 changed files with 139 additions and 76 deletions.
Binary file modified .gradle/7.6.1/executionHistory/executionHistory.bin
Binary file not shown.
Binary file modified .gradle/7.6.1/executionHistory/executionHistory.lock
Binary file not shown.
Binary file modified .gradle/7.6.1/fileHashes/fileHashes.bin
Binary file not shown.
Binary file modified .gradle/7.6.1/fileHashes/fileHashes.lock
Binary file not shown.
Binary file modified .gradle/7.6.1/fileHashes/resourceHashesCache.bin
Binary file not shown.
Binary file modified .gradle/buildOutputCleanup/buildOutputCleanup.lock
Binary file not shown.
Binary file modified .gradle/buildOutputCleanup/outputFiles.bin
Binary file not shown.
Binary file modified .gradle/file-system.probe
Binary file not shown.
Binary file modified server/.gradle/7.6.1/executionHistory/executionHistory.bin
Binary file not shown.
Binary file modified server/.gradle/7.6.1/executionHistory/executionHistory.lock
Binary file not shown.
Binary file modified server/.gradle/7.6.1/fileHashes/fileHashes.bin
Binary file not shown.
Binary file modified server/.gradle/7.6.1/fileHashes/fileHashes.lock
Binary file not shown.
Binary file modified server/.gradle/7.6.1/fileHashes/resourceHashesCache.bin
Binary file not shown.
Binary file modified server/.gradle/buildOutputCleanup/buildOutputCleanup.lock
Binary file not shown.
Binary file modified server/.gradle/file-system.probe
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import com.econovation.recruitdomain.domains.card.domain.Card;
import com.econovation.recruitinfrastructure.mail.EmailSenderService;
import com.econovation.recruitinfrastructure.mail.GoogleMailProperties;
import com.econovation.recruitinfrastructure.ses.AwsSesUtils;
import com.econovation.recruitinfrastructure.ses.SendRawEmailDto;
import com.econovation.recruitinfrastructure.slack.SlackMessageProvider;
import com.econovation.recruitinfrastructure.slack.config.SlackProperties;
import javax.mail.MessagingException;
Expand All @@ -27,6 +29,7 @@
public class ApplicantRegisterEventConfirmEmailHandler {
private final GoogleMailProperties googleMailProperties;
private final EmailSenderService emailSenderService;
private final AwsSesUtils awsSesUtils;
@Async
@TransactionalEventListener(
classes = ApplicantRegisterEvent.class,
Expand All @@ -35,13 +38,12 @@ public class ApplicantRegisterEventConfirmEmailHandler {
public void handle(ApplicantRegisterEvent applicantRegistEvent) {
log.info("%s님의 지원서가 접수되었습니다.", applicantRegistEvent.getUserName());
try{
MimeMessage mimeMessage = emailSenderService.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, "utf-8");
helper.setFrom(googleMailProperties.getUsername());
helper.setTo(applicantRegistEvent.getEmail());
helper.setSubject("[Econovation] 에코노베이션 지원서 접수 확인 메일");
helper.setText(generateConfirmRegisterEmailBody(applicantRegistEvent.getEmail(), applicantRegistEvent.getUserName()), true);
emailSenderService.sendEmail(mimeMessage);
SendRawEmailDto sendData = SendRawEmailDto.builder()
.bodyHtml(generateConfirmRegisterEmailBody(applicantRegistEvent.getEmail(), applicantRegistEvent.getUserName()))
.recipient(applicantRegistEvent.getEmail())
.subject("[Econovation] 에코노베이션 지원서 접수 확인 메일")
.build();
awsSesUtils.sendRawEmails(sendData);
} catch (MessagingException e) {
log.error("메일 content 생성에 실패하였습니다.. {}", e.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.econovation.recruit.api.user.controller;

import static com.econovation.recruitcommon.consts.RecruitStatic.INTERVIEWER_SUCCESS_SIGNUP_MESSAGE;
import static com.econovation.recruitcommon.consts.RecruitStatic.PASSWORD_SUCCESS_CHANGE_MESSAGE;

import com.econovation.recruit.api.interviewer.docs.InterviewerExceptionDocs;
import com.econovation.recruit.api.user.usecase.UserLoginUseCase;
import com.econovation.recruit.api.user.usecase.UserRegisterUseCase;
import com.econovation.recruitcommon.annotation.ApiErrorExceptionsExample;
import com.econovation.recruitcommon.annotation.DevelopOnlyApi;
import com.econovation.recruitcommon.annotation.PasswordValidate;
import com.econovation.recruitcommon.dto.TokenResponse;
import com.econovation.recruitcommon.jwt.JwtTokenProvider;
import com.econovation.recruitdomain.domains.dto.LoginRequestDto;
Expand All @@ -19,10 +21,12 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
Expand Down Expand Up @@ -59,8 +63,21 @@ public ResponseEntity<TokenResponse> login(LoginRequestDto loginRequestDto) {
@Operation(summary = "회원가입합니다.", description = "회원가입합니다.")
@ApiErrorExceptionsExample(InterviewerExceptionDocs.class)
@PostMapping("/signup")
public ResponseEntity<String> signUp(@Valid @RequestBody SignUpRequestDto signUpRequestDto) {
public ResponseEntity<String> signUp(@RequestBody @Valid SignUpRequestDto signUpRequestDto) {
userRegisterUseCase.signUp(signUpRequestDto);
return new ResponseEntity<>(INTERVIEWER_SUCCESS_SIGNUP_MESSAGE, HttpStatus.OK);
}
@Operation(summary = "토큰 재발행", description = "refreshToken을 이용하여 accessToken을 재발행합니다.")
@PostMapping("/token/refresh")
public ResponseEntity<TokenResponse> refresh(@RequestParam String refreshToken) {
TokenResponse tokenResponse = userLoginUseCase.refresh(refreshToken);
return new ResponseEntity<>(tokenResponse, HttpStatus.OK);
}

@Operation(summary = "비밀번호 수정", description = "비밀번호를 수정합니다.")
@PostMapping("/password")
public ResponseEntity<String> changePassword(@RequestParam @Valid @PasswordValidate String password) {
userRegisterUseCase.changePassword(password);
return new ResponseEntity<>(PASSWORD_SUCCESS_CHANGE_MESSAGE, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.econovation.recruit.api.user.service;

import com.econovation.recruit.api.config.security.SecurityUtils;
import com.econovation.recruit.api.user.usecase.UserLoginUseCase;
import com.econovation.recruit.api.user.usecase.UserRegisterUseCase;
import com.econovation.recruitcommon.dto.TokenResponse;
Expand Down Expand Up @@ -33,6 +34,13 @@ public TokenResponse execute(LoginRequestDto loginRequestDto) {
return jwtTokenProvider.createToken(account.getId(), account.getRole().name());
}

@Override
public TokenResponse refresh(String refreshToken) {
Long idpId = jwtTokenProvider.parseRefreshToken(refreshToken);
Interviewer account = interviewerLoadPort.loadInterviewById(idpId);
return jwtTokenProvider.createToken(account.getId(), account.getRole().name());
}

private void checkPassword(String password, String encodePassword) {
boolean isMatch = passwordEncoder.matches(password, encodePassword);
if (!isMatch) throw InterviewerNotMatchException.EXCEPTION;
Expand All @@ -41,7 +49,7 @@ private void checkPassword(String password, String encodePassword) {
@Override
@Transactional
public void signUp(SignUpRequestDto signUpRequestDto) {
if (interviewerLoadPort.loadOptionalInterviewerByEmail(signUpRequestDto.getEmail()) != null)
if (interviewerLoadPort.loadOptionalInterviewerByEmail(signUpRequestDto.getEmail()).isPresent())
throw InterviewerAlreadySubmitException.EXCEPTION;
String encededPassword = passwordEncoder.encode(signUpRequestDto.getPassword());
Interviewer interviewer =
Expand All @@ -53,4 +61,12 @@ public void signUp(SignUpRequestDto signUpRequestDto) {
.build();
interviewerRecordPort.save(interviewer);
}

@Override
@Transactional
public void changePassword(String password) {
Long userId = SecurityUtils.getCurrentUserId();
String encededPassword = passwordEncoder.encode(password);
interviewerLoadPort.loadInterviewById(userId).changePassword(encededPassword);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
@UseCase
public interface UserLoginUseCase {
TokenResponse execute(LoginRequestDto loginRequestDto);

TokenResponse refresh(String refreshToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
public interface UserRegisterUseCase {

void signUp(SignUpRequestDto signUpRequestDto);

void changePassword(String password);
}

This file was deleted.

3 changes: 3 additions & 0 deletions server/Recruit-Common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'

//validate
implementation 'org.springframework.boot:spring-boot-starter-validation'

implementation 'com.fasterxml.jackson.core:jackson-annotations:2.10.1'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.1'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
package com.econovation.recruitcommon.annotation;

import com.econovation.recruitcommon.aspect.PasswordValidateAspect;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Target(ElementType.FIELD)
@Documented
@Constraint(validatedBy = PasswordValidateAspect.class)
@Target({
// ElementType.METHOD,
ElementType.FIELD,
ElementType.PARAMETER,
})
@Retention(RetentionPolicy.RUNTIME)
public @interface PasswordValidate {}
public @interface PasswordValidate {
String message() default "Invalid password";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.econovation.recruitcommon.aspect;

import com.econovation.recruitcommon.annotation.PasswordValidate;
import com.econovation.recruitcommon.exception.InvalidPasswordException;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PasswordValidateAspect implements ConstraintValidator<PasswordValidate, String> {

private boolean isValidPassword(String password) {
if (password == null || password.length() < 10) {
return false; // 비밀번호 길이가 10글자 미만인 경우 검증 실패
}

boolean hasDigit = false;
boolean hasSpecialChar = false;

for (char ch : password.toCharArray()) {
if (Character.isDigit(ch)) {
hasDigit = true;
} else if (isSpecialCharacter(ch)) {
hasSpecialChar = true;
}
}

return hasDigit && hasSpecialChar;
}

private boolean isSpecialCharacter(char ch) {
// 특수 문자 여부를 확인하는 로직을 구현
// 예를 들어, 일부 특수 문자를 확인할 수 있습니다.
return ch == '@' || ch == '#' || ch == '!' || ch == '$' || ch == '%' || ch == '^'
|| ch == '&' || ch == '*';
}

@Override
public void initialize(PasswordValidate constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (!isValidPassword(value)) {
throw InvalidPasswordException.EXCEPTION;
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class RecruitStatic {
public static final String BOARD_SUCCESS_UPDATE_MESSAGE = "성공적으로 업무카드가 수정됐습니다";
public static final String RECORD_SUCCESS_UPDATE_MESSAGE = "성공적으로 지원자의 면접 기록이 수정됐습니다";
public static final String INTERVIEWER_SUCCESS_SIGNUP_MESSAGE = "성공적으로 면접관이 등록됐습니다";
public static final String PASSWORD_SUCCESS_CHANGE_MESSAGE = "성공적으로 비밀번호가 변경됐습니다";
public static final List<String> TIMETABLE_APPLICANT_FIELD = List.of("field", "name");

public static final String[] SwaggerPatterns = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public enum GlobalErrorCode implements BaseErrorCode {
BAD_FILE_EXTENSION(BAD_REQUEST, "FILE_400_1", "파일 확장자가 잘못 되었습니다."),
OUT_OF_INDEX(BAD_REQUEST, "GLOBAL_400_2", "인덱스 범위를 벗어났습니다."),
TOO_MANY_REQUEST(TOO_MANY_REQUESTS, "GLOBAL_429_1", "과도한 요청을 보내셨습니다. 잠시 기다려 주세요."),
INVALID_PASSWORD(BAD_REQUEST, "GLOBAL_400_4", "비밀번호는 10자 이상 숫자, 특수문자를 포함해야 합니다."),
;
private Integer status;
private String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.econovation.recruitcommon.exception;

public class InvalidPasswordException extends RecruitCodeException {
public static final RecruitCodeException EXCEPTION = new InvalidPasswordException();

private InvalidPasswordException() {
super(GlobalErrorCode.INVALID_PASSWORD);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ public static Interviewer from(InterviewerResponse interviewerResponse) {
.year(interviewerResponse.getYear())
.build();
}

public void changePassword(String encededPassword) {
this.password = encededPassword;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public SesClient sesClient() {
AwsBasicCredentials.create(this.accessKey, this.secretKey);
return SesClient.builder()
.credentialsProvider(StaticCredentialsProvider.create(awsBasicCredentials))
.region(Region.US_WEST_2)
.region(Region.AP_NORTHEAST_2)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@
public class SendRawEmailDto {
// Replace [email protected] with your "From" address.
// This address must be verified with Amazon SES.
private final String sender = "공연 정산관리팀 <support@dudoong.com>";
private final String sender = "에코노베이션 Recruit팀 <recruit@econovation.com>";
// Replace [email protected] with a "To" address. If your account
// is still in the sandbox, this address must be verified.
private final String recipient;
// The subject line for the email.
private final String subject;

private final String bodyHtml;

private final List<RawEmailAttachmentDto> rawEmailAttachments = new ArrayList<>();

@Builder
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
aws:
access-key: ${AWS_ACCESS_KEY:testKey}
secret-key: ${AWS_SECRET_KEY:secretKey}
ses:
region: ${AWS_SES_REGION:ap-northeast-2}
verified-email: ${AWS_SES_VERIFIED_EMAIL:}
s3:
bucket: ${AWS_S3_BUCKET:bucket}
private-bucket : ${AWS_S3_BUCKET_PRIVATE:bucket-private}
Expand Down

0 comments on commit ec905e2

Please sign in to comment.