Skip to content

Commit

Permalink
Merge pull request #54 from AECOFARM/email
Browse files Browse the repository at this point in the history
✨ [FEAT] SignUp시 Email인증 기능 추가
  • Loading branch information
YeahOut authored Aug 2, 2024
2 parents 2fdfc24 + 9ad1e82 commit c494cf9
Show file tree
Hide file tree
Showing 17 changed files with 289 additions and 49 deletions.
12 changes: 11 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,23 @@ dependencies {

// AWS S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'


// Mail
implementation 'com.sun.mail:jakarta.mail:2.0.1'
implementation 'javax.activation:activation:1.1.1'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation group: 'jakarta.mail', name: 'jakarta.mail-api', version: '2.1.3'


compileOnly 'org.projectlombok:lombok'
implementation 'org.postgresql:postgresql:42.7.3'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

implementation 'javax.xml.bind:jaxb-api:2.3.1'
implementation 'org.glassfish.jaxb:jaxb-runtime:2.3.1'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dgu.aecofarm.domain.email.service;

import dgu.aecofarm.entity.EmailMessage;

public interface EmailService {
void sendAuthCode(String email, String authCode);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dgu.aecofarm.domain.email.service;

import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class EmailServiceImpl implements EmailService {

private final JavaMailSender javaMailSender;

@Override
public void sendAuthCode(String email, String authCode) {
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, false, "UTF-8");
mimeMessageHelper.setTo(email);
mimeMessageHelper.setSubject("[AECOfarm] Email Verification Code");
mimeMessageHelper.setText("Your verification code is: " + authCode, false); // HTML이 아니라면 false로 설정
javaMailSender.send(mimeMessage);
log.info("Auth code sent to email: " + email);
} catch (jakarta.mail.MessagingException e) {
log.error("Failed to send auth code to email: " + email, e);
throw new RuntimeException("Failed to send auth code", e);
}
}
}
41 changes: 41 additions & 0 deletions src/main/java/dgu/aecofarm/domain/email/service/UserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dgu.aecofarm.domain.email.service;

import dgu.aecofarm.entity.Member;
import dgu.aecofarm.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
@RequiredArgsConstructor
public class UserService {
private final MemberRepository memberRepository;

// 임시 비밀번호를 설정하는 메서드
public void setTempPassword(String email, String tempPassword) {
Optional<Member> optionalMember = memberRepository.findMemberByEmail(email);
if (!optionalMember.isPresent()) {
throw new IllegalArgumentException("유효한 이메일이 아닙니다.");
}
Member member = optionalMember.get();
member.updatePassword(tempPassword);
memberRepository.save(member);
}

// 사용자 정보 조회 메서드
public Optional<Member> getMemberByEmail(String email) {
return memberRepository.findMemberByEmail(email);
}

// 사용자 비밀번호 재설정 메서드
public void resetPassword(String email, String newPassword) {
Optional<Member> optionalMember = memberRepository.findMemberByEmail(email);
if (!optionalMember.isPresent()) {
throw new IllegalArgumentException("유효한 이메일이 아닙니다.");
}
Member member = optionalMember.get();
member.updatePassword(newPassword);
memberRepository.save(member);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import dgu.aecofarm.domain.member.service.MemberService;
import dgu.aecofarm.dto.email.EmailResponseDto;
import dgu.aecofarm.dto.member.*;
import dgu.aecofarm.entity.Member;
import dgu.aecofarm.entity.Response;
Expand All @@ -28,12 +29,24 @@ public Response<?> signup(@RequestPart("signupData") SignupRequestDTO signupRequ
@RequestPart("file") MultipartFile file) {
try {
String imageUrl = memberService.uploadFile(file);
return Response.success(memberService.signup(signupRequestDTO, imageUrl));
SignupResponseDTO signupResponseDTO = memberService.initiateSignup(signupRequestDTO, imageUrl);
signupResponseDTO.getSignupRequestDTO().setImageUrl(imageUrl);
return Response.success(signupResponseDTO);
} catch (Exception e) {
return Response.failure(e);
}
}
@PostMapping("/signup/complete")
public Response<?> completeSignup(@RequestBody SignupCompleteDTO signupCompleteDTO) {
try {
return Response.success(memberService.completeSignup(signupCompleteDTO.getSignupRequestDTO(),
signupCompleteDTO.getAuthCode(),
signupCompleteDTO.getExpectedCode(),
signupCompleteDTO.getSignupRequestDTO().getImageUrl()));
} catch (Exception e) {
return Response.failure(e);
}
}

@PostMapping("/login")
public Response<?> login(@RequestBody LoginRequestDTO loginRequestDTO) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import java.util.Optional;

public interface MemberService {
String signup(SignupRequestDTO registerRequestDTO, String imageUrl);
SignupResponseDTO initiateSignup(SignupRequestDTO signupRequestDTO, String imageUrl);

String completeSignup(SignupRequestDTO signupRequestDTO, String authCode, String expectedCode, String imageUrl);

String uploadFile(MultipartFile file);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.fasterxml.jackson.databind.ObjectMapper;
import dgu.aecofarm.domain.email.service.EmailService;
import dgu.aecofarm.dto.member.*;
import dgu.aecofarm.entity.*;
import dgu.aecofarm.exception.InvalidUserIdException;
Expand Down Expand Up @@ -35,6 +36,7 @@
public class MemberServiceImpl implements MemberService {

private final MemberRepository memberRepository;
private final EmailService emailService;
private final ItemRepository itemRepository;
private final ObjectMapper objectMapper;
private final ContractRepository contractRepository;
Expand All @@ -45,24 +47,34 @@ public class MemberServiceImpl implements MemberService {
@Value("${cloud.aws.s3.bucket}")
private String bucketName;

public String signup(SignupRequestDTO signupRequestDTO, String imageUrl) {
String email = signupRequestDTO.getEmail();
String userName = signupRequestDTO.getUserName();
String password = toSHA256(signupRequestDTO.getPassword());
String phone = signupRequestDTO.getPhone();
int schoolNum = signupRequestDTO.getSchoolNum();

Member checkDuplicate = memberRepository.findMemberByEmail(email);
if (checkDuplicate != null) {
@Override
public SignupResponseDTO initiateSignup(SignupRequestDTO signupRequestDTO, String imageUrl) {
Optional<Member> checkDuplicate = memberRepository.findMemberByEmail(signupRequestDTO.getEmail());
if (checkDuplicate.isPresent()) {
throw new IllegalArgumentException("중복된 이메일입니다.");
}

String authCode = generateAuthCode();
emailService.sendAuthCode(signupRequestDTO.getEmail(), authCode);

return SignupResponseDTO.builder()
.signupRequestDTO(signupRequestDTO)
.expectedCode(authCode)
.build();
}

@Override
public String completeSignup(SignupRequestDTO signupRequestDTO, String authCode, String expectedCode, String imageUrl) {
if (!authCode.equals(expectedCode)) {
throw new IllegalArgumentException("인증 코드가 일치하지 않습니다.");
}

Member member = Member.builder()
.email(email)
.userName(userName)
.password(password)
.phone(phone)
.schoolNum(schoolNum)
.email(signupRequestDTO.getEmail())
.userName(signupRequestDTO.getUserName())
.password(toSHA256(signupRequestDTO.getPassword()))
.phone(signupRequestDTO.getPhone())
.schoolNum(signupRequestDTO.getSchoolNum())
.image(imageUrl)
.point(3000)
.build();
Expand All @@ -73,6 +85,7 @@ public String signup(SignupRequestDTO signupRequestDTO, String imageUrl) {
return "회원가입 성공";
}

@Override
public String uploadFile(MultipartFile file) {
if (file.isEmpty()) {
return null;
Expand All @@ -94,12 +107,14 @@ public String uploadFile(MultipartFile file) {
}
}

@Override
public LoginResponseDTO login(LoginRequestDTO loginRequestDTO) {
Member member = memberRepository.findMemberByEmail(loginRequestDTO.getEmail());
if (member == null) {
Optional<Member> optionalMember = memberRepository.findMemberByEmail(loginRequestDTO.getEmail());
if (!optionalMember.isPresent()) {
throw new IllegalArgumentException("유효한 이메일이 아닙니다.");
}

Member member = optionalMember.get();
String encode_password = toSHA256(loginRequestDTO.getPassword());

if (encode_password.equals(member.getPassword())) {
Expand All @@ -111,13 +126,13 @@ public LoginResponseDTO login(LoginRequestDTO loginRequestDTO) {
}
throw new IllegalArgumentException("비밀번호가 틀립니다.");
}

// Jwt Token에서 추출한 loginId로 Member 찾아오기
@Override
public Optional<Member> getLoginUserInfoByMemberId(String memberId) {
return memberRepository.findByMemberId(Long.valueOf(memberId));
}

// 로그인한 Member 조회
@Override
public Optional<JwtInfoResponseDTO> getLoginUserInfoByUserid(String memberId) {
return memberRepository.findByMemberId(Long.valueOf(memberId)).map(member ->
JwtInfoResponseDTO.builder()
Expand All @@ -126,28 +141,27 @@ public Optional<JwtInfoResponseDTO> getLoginUserInfoByUserid(String memberId) {
.build());
}

@Override
public String findPassword(FindPasswordRequestDTO findPasswordDTO) {
// memberId로 회원 정보 조회
Member member = memberRepository.findMemberByEmail(findPasswordDTO.getEmail());

if (member == null) {
Optional<Member> optionalMember = memberRepository.findMemberByEmail(findPasswordDTO.getEmail());
if (!optionalMember.isPresent()) {
throw new IllegalArgumentException("유효한 이메일이 아닙니다.");
}
}// 사용자 이름과 학번이 일치하는지 확인
Member member = optionalMember.get();

// 사용자 이름과 학번이 일치하는지 확인
if (!member.getUserName().equals(findPasswordDTO.getUserName()) || member.getSchoolNum() != findPasswordDTO.getSchoolNum()) {
throw new IllegalArgumentException("사용자 정보가 일치하지 않습니다.");
}

// 비밀번호 재설정
String newPassword = toSHA256(findPasswordDTO.getPassword());
member.updatePassword(newPassword);
memberRepository.save(member);

return "비밀번호 재설정에 성공하였습니다.";

}

@Override
public String signout(String memberId) {
Member member = memberRepository.findById(Long.valueOf(memberId))
.orElseThrow(() -> new IllegalArgumentException("유효한 사용자 ID가 아닙니다."));
Expand All @@ -160,24 +174,7 @@ public String signout(String memberId) {
return "회원 탈퇴에 성공하였습니다.";
}

private String toSHA256(String base) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(base.getBytes("UTF-8"));
StringBuilder hexString = new StringBuilder();

for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if(hex.length() == 1) hexString.append('0');
hexString.append(hex);
}

return hexString.toString();
} catch(NoSuchAlgorithmException | UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}

@Override
public RecommendResponseDTO getRecommand(String memberId) {
Member member = memberRepository.findById(Long.valueOf(memberId))
.orElseThrow(() -> new InvalidUserIdException("유효한 사용자 ID가 아닙니다."));
Expand Down Expand Up @@ -226,6 +223,7 @@ public RecommendResponseDTO getRecommand(String memberId) {
.build();
}

@Override
public SearchResponseDTO searchItems(SearchRequestDTO searchRequestDTO, String memberId) {
Member member = memberRepository.findById(Long.valueOf(memberId))
.orElseThrow(() -> new InvalidUserIdException("유효한 사용자 ID가 아닙니다."));
Expand Down Expand Up @@ -290,4 +288,26 @@ public SearchResponseDTO searchItems(SearchRequestDTO searchRequestDTO, String m
.borrowItems(borrowItems)
.build();
}

private String generateAuthCode() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
}

private String toSHA256(String base) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(base.getBytes("UTF-8"));
StringBuilder hexString = new StringBuilder();

for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}

return hexString.toString();
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
8 changes: 8 additions & 0 deletions src/main/java/dgu/aecofarm/dto/email/EmailPostDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dgu.aecofarm.dto.email;

import lombok.Getter;

@Getter
public class EmailPostDto {
private String email;
}
10 changes: 10 additions & 0 deletions src/main/java/dgu/aecofarm/dto/email/EmailResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dgu.aecofarm.dto.email;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class EmailResponseDto {
private String code;
}
12 changes: 12 additions & 0 deletions src/main/java/dgu/aecofarm/dto/member/SignupCompleteDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dgu.aecofarm.dto.member;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class SignupCompleteDTO {
private SignupRequestDTO signupRequestDTO;
private String authCode;
private String expectedCode;
}
Loading

0 comments on commit c494cf9

Please sign in to comment.