Skip to content

Commit

Permalink
Merge pull request #28 from Kusitms-29th-Meetup-TeamE/feat/15/mail
Browse files Browse the repository at this point in the history
Feat: 메일 서비스 및 서비스 자체 회원가입, 로그인 api
  • Loading branch information
Jeongho427 authored May 9, 2024
2 parents ff6d5c7 + 87ab724 commit 1a11fc7
Show file tree
Hide file tree
Showing 22 changed files with 441 additions and 85 deletions.
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ dependencies {
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'


//mail
implementation 'org.springframework.boot:spring-boot-starter-mail'

//thymeleaf
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

//secrets manager
implementation 'io.awspring.cloud:spring-cloud-starter-aws-secrets-manager-config:2.4.4'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.csrf(c -> c.disable())
.authorizeHttpRequests(c -> c
.requestMatchers(
"/api/sign-up",
"/api/login",
"/sign-up",
"/login/kakao",
"/login/local",
"/send-email",
"/register",
"/users/main",
"/swagger-ui/**",
"/swagger-resources/**",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import com.meetup.teame.backend.domain.auth.oauth.dto.CreateOauthUserRequest;
import com.meetup.teame.backend.domain.auth.oauth.dto.CreateUserRequest;
import com.meetup.teame.backend.domain.auth.oauth.service.KakaoService;
import com.meetup.teame.backend.domain.login.service.LoginService;
import com.meetup.teame.backend.domain.user.entity.User;
import com.meetup.teame.backend.domain.user.repository.UserRepository;
import com.meetup.teame.backend.domain.user.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -16,12 +18,13 @@

@RequiredArgsConstructor
@RestController
@RequestMapping("/api")
@Tag(name = "oauth", description = "로그인 관련 api")
public class KakaoController {

private final UserRepository userRepository;
private final UserService userService;
private final KakaoService kakaoService;
private final LoginService loginService;

@Operation(summary = "카카오 로그인", description = """
카카오 소셜 로그인을 진행합니다.
Expand All @@ -34,10 +37,9 @@ public class KakaoController {
public ResponseEntity<Object> kakaoLogin(@RequestParam String code) throws JsonProcessingException {
String kakaoAccessToken = kakaoService.getKakaoAccessToken(code); //인가코드로 카카오 엑세스 토큰 받아오기
CreateOauthUserRequest request = kakaoService.getKakaoInfo(kakaoAccessToken); //엑세스 토큰으로 카카오 사용자 정보 받아오기
boolean checkExist = kakaoService.userExists(request.getEmail());
boolean checkExist = loginService.userExists(request.getEmail());
if(checkExist) { //이미 가입된 회원
/*Optional<User> userOptional*/User user = userService.findByEmail(request.getEmail());
//User user = userOptional.get();
User user = userRepository.findByEmail(request.getEmail());
HttpHeaders headers = kakaoService.getLoginHeader(user);

return ResponseEntity.ok().headers(headers).body("login");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
@NoArgsConstructor
@Getter
public class CreateOauthUserRequest {
private String name; // 필수 동의
private String email; // 선택 동의
private String gender; // 필수 동의
private String birthYear; // 필수 동의
private String profileImage; // 선택 동의
private String name;
private String email;
private String gender;
private String birthyear;
private String profileImage;
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,4 @@ public HttpHeaders getLoginHeader(User user) {
return headers;
}

public boolean userExists(String email) {
/*Optional<User> userOptional*/User user = userService.findByEmail(email);
if (/*userOptional.isPresent()*/user!=null) {
return true;
} else {
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.meetup.teame.backend.domain.email.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class BCryptConfig {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.meetup.teame.backend.domain.email.config;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;

import java.util.Properties;

@Configuration
public class MailConfig {

@Value("${spring.mail.host}")
private String MAIL_HOST;

@Value("${spring.mail.username}")
private String MAIL_ADDRESS;

@Value("${spring.mail.password}")
private String MAIL_PASSWORD;

@Bean
public JavaMailSender javaMailService() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();

// SMTP 서버 주소를 'smtp.gmail.com'으로 변경
javaMailSender.setHost(MAIL_HOST);
javaMailSender.setUsername(MAIL_ADDRESS); // 사용자 이메일 주소
javaMailSender.setPassword(MAIL_PASSWORD); // 앱 비밀번호 또는 사용자 비밀번호

// TLS를 사용하는 587 포트 설정
javaMailSender.setPort(587);

javaMailSender.setJavaMailProperties(getMailProperties());

return javaMailSender;
}

private Properties getMailProperties() {
Properties properties = new Properties();
properties.setProperty("mail.transport.protocol", "smtp");
properties.setProperty("mail.smtp.auth", "true");
properties.setProperty("mail.smtp.starttls.enable", "true"); // TLS 활성화
properties.setProperty("mail.debug", "true");

// 'smtp.gmail.com'으로 변경
properties.setProperty("mail.smtp.ssl.trust", "smtp.gmail.com");
return properties;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.meetup.teame.backend.domain.email.dto;

import lombok.Getter;

@Getter
public class EmailRequest {
String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.meetup.teame.backend.domain.email.service;

import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class EmailService {

@Value("${spring.mail.username}")
private String SENDER_EMAIL_ADDRESS;

private final JavaMailSender javaMailSender;
private static int authNumber;

public static void createNumber(){
authNumber = (int)(Math.random() * (90000)) + 100000;
}

// 파일에서 HTML 이메일 템플릿을 읽는 메소드
private String readEmailTemplate() throws IOException {
ClassPathResource resource = new ClassPathResource("/templates/email.html");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
}
}

// 이메일 생성 메소드
public MimeMessage CreateMail(String mail){
createNumber();
MimeMessage message = javaMailSender.createMimeMessage();

System.out.println("Sending email to: " + mail); // 로그 추가

try {

String htmlContent = readEmailTemplate();
htmlContent = htmlContent.replace("{authNumber}", Integer.toString(authNumber)); // 템플릿 내의 플레이스홀더를 인증번호로 교체


message.setFrom(SENDER_EMAIL_ADDRESS);
message.setRecipients(MimeMessage.RecipientType.TO, mail);
message.setSubject("또바 이메일 인증");
message.setText(htmlContent, "UTF-8", "html");

} catch (MessagingException | IOException e) {
e.printStackTrace();
}
return message;
}

public int sendMail(String mail){
MimeMessage message = CreateMail(mail);
javaMailSender.send(message);
return authNumber;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.meetup.teame.backend.domain.login.controller;

import com.meetup.teame.backend.domain.auth.oauth.service.KakaoService;
import com.meetup.teame.backend.domain.login.dto.LoginRequest;
import com.meetup.teame.backend.domain.login.service.LoginService;
import com.meetup.teame.backend.domain.user.entity.User;
import com.meetup.teame.backend.domain.user.repository.UserRepository;
import com.meetup.teame.backend.domain.user.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
@Tag(name = "local-login", description = "자체 로그인 관련 api")
public class LoginController {

private final UserRepository userRepository;
private final KakaoService kakaoService;
private final LoginService loginService;

@Operation(summary = "자체 로그인", description = """
이메일 주소, 비밀번호를 입력하고 로그인 시도를 합니다.
이메일이 맞는지 확인하고 틀리면 "등록되지 않은 이메일입니다." 메세지를 반환합니다.
비밀번호가 맞는지 확인하고 틀리면 "잘못된 비밀번호입니다." 메세지를 반환합나다.
로그인에 성공하면 "로그인 성공" 메세지를 반환합니다.
""")
@PostMapping("/login/local")
public ResponseEntity<String> localLogin(@RequestBody LoginRequest request) {
boolean existEmail = loginService.userExists(request.getEmail());
boolean validPassword = loginService.checkUserValid(request.getEmail(), request.getPassword());
if (existEmail) {
if (validPassword) {
User user = userRepository.findByEmail(request.getEmail());
HttpHeaders headers = kakaoService.getLoginHeader(user);
return ResponseEntity.ok().headers(headers).body("로그인 성공");
}
else {
return ResponseEntity.ok().body("잘못된 비밀번호입니다.");
}
}
else{
return ResponseEntity.ok().body("등록되지 않은 이메일입니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.meetup.teame.backend.domain.login.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Getter
public class LoginRequest {

private String email;
private String password;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.meetup.teame.backend.domain.login.service;

import com.meetup.teame.backend.domain.user.entity.User;
import com.meetup.teame.backend.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Service
@Transactional
public class LoginService {

private final BCryptPasswordEncoder encoder;
private final UserRepository userRepository;

public boolean userExists(String email) {
User user = userRepository.findByEmail(email);
if (user!=null) {
return true;
}
else {
return false;
}
}

public boolean checkUserValid(String email, String password) {
User user = userRepository.findByEmail(email);
if (user == null) {
return false;
}
return encoder.matches(password, user.getPassword());
}

}
Loading

0 comments on commit 1a11fc7

Please sign in to comment.