diff --git a/build.gradle b/build.gradle index e447f6eb..db2acbbf 100644 --- a/build.gradle +++ b/build.gradle @@ -89,6 +89,12 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.batch:spring-batch-test' implementation 'org.springframework.boot:spring-boot-starter-quartz' + + // Gmail + implementation 'com.google.api-client:google-api-client:2.0.0' + implementation 'com.google.oauth-client:google-oauth-client-jetty:1.34.1' + implementation 'com.google.apis:google-api-services-gmail:v1-rev20220404-2.0.0' + implementation 'javax.mail:mail:1.4.7' } spotless { diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml index a01e0752..d62e139d 100644 --- a/scripts/docker-compose.yml +++ b/scripts/docker-compose.yml @@ -31,7 +31,7 @@ services: - '8080:8080' env_file: .env volumes: - - "./client_secret.json:/client_secret.json" + - "./client_secret_442820160906-mpbltfh7036rl8jtlal40so1tfotkf24.apps.googleusercontent.com.json:/client_secret_442820160906-mpbltfh7036rl8jtlal40so1tfotkf24.apps.googleusercontent.com.json" - "./monghanyang-21fad-54f38c79a642.json:/monghanyang-21fad-54f38c79a642.json" - "./logs:/logs" labels: diff --git a/src/main/java/org/ioteatime/meonghanyangserver/auth/service/AuthService.java b/src/main/java/org/ioteatime/meonghanyangserver/auth/service/AuthService.java index 490db9d6..55d1022d 100644 --- a/src/main/java/org/ioteatime/meonghanyangserver/auth/service/AuthService.java +++ b/src/main/java/org/ioteatime/meonghanyangserver/auth/service/AuthService.java @@ -12,6 +12,7 @@ import org.ioteatime.meonghanyangserver.auth.dto.request.LoginRequest; import org.ioteatime.meonghanyangserver.auth.mapper.AuthEntityMapper; import org.ioteatime.meonghanyangserver.auth.mapper.AuthResponseMapper; +import org.ioteatime.meonghanyangserver.clients.google.GoogleMailClient; import org.ioteatime.meonghanyangserver.clients.ses.SesClient; import org.ioteatime.meonghanyangserver.common.exception.BadRequestException; import org.ioteatime.meonghanyangserver.common.exception.NotFoundException; @@ -36,6 +37,7 @@ public class AuthService { private final JwtUtils jwtUtils; private final SesClient sesClient; + private final GoogleMailClient googleMailClient; private final MemberRepository memberRepository; private final GroupMemberRepository deviceRepository; private final EmailCodeRepository emailCodeRepository; @@ -103,7 +105,8 @@ public void send(String email) {
%s
""" .formatted(code); - sesClient.sendEmail(email, mailSubject, mailContent); + googleMailClient.sendMail(email, mailSubject, mailContent); + // sesClient.sendEmail(email, mailSubject, mailContent); } private static String getCode() { @@ -157,6 +160,7 @@ public void issuePassword(IssuePasswordRequest issuePasswordRequest) {%s
""" .formatted(password); - sesClient.sendEmail(issuePasswordRequest.email(), mailSubject, mailContent); + googleMailClient.sendMail(issuePasswordRequest.email(), mailSubject, mailContent); + // sesClient.sendEmail(issuePasswordRequest.email(), mailSubject, mailContent); } } diff --git a/src/main/java/org/ioteatime/meonghanyangserver/clients/google/GoogleMailClient.java b/src/main/java/org/ioteatime/meonghanyangserver/clients/google/GoogleMailClient.java new file mode 100644 index 00000000..4ea05a45 --- /dev/null +++ b/src/main/java/org/ioteatime/meonghanyangserver/clients/google/GoogleMailClient.java @@ -0,0 +1,118 @@ +package org.ioteatime.meonghanyangserver.clients.google; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; +import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; +import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.googleapis.json.GoogleJsonError; +import com.google.api.client.googleapis.json.GoogleJsonResponseException; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.gson.GsonFactory; +import com.google.api.client.util.store.FileDataStoreFactory; +import com.google.api.services.gmail.Gmail; +import com.google.api.services.gmail.GmailScopes; +import com.google.api.services.gmail.model.Message; +import java.io.*; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.util.Properties; +import java.util.Set; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import org.apache.commons.codec.binary.Base64; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class GoogleMailClient { + @Value("${google.service-account}") + private String gcpServiceAccount; + + @Value("${google.official-gmail}") + private String fromEmailAddress; + + private Credential getCredentials( + final NetHttpTransport HTTP_TRANSPORT, GsonFactory jsonFactory) { + try { + // Load client secrets. + InputStream in = new FileInputStream(gcpServiceAccount); + GoogleClientSecrets clientSecrets = + GoogleClientSecrets.load(jsonFactory, new InputStreamReader(in)); + + // Build flow and trigger user authorization request. + GoogleAuthorizationCodeFlow flow = + new GoogleAuthorizationCodeFlow.Builder( + HTTP_TRANSPORT, + jsonFactory, + clientSecrets, + Set.of(GmailScopes.GMAIL_SEND)) + .setDataStoreFactory( + new FileDataStoreFactory(Paths.get("tokens").toFile())) + .setAccessType("offline") + .build(); + LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build(); + return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); + } catch (IOException e) { + throw new RuntimeException("Google Credential 획득 중 I/O 오류가 발생하였습니다."); + } + } + + public void sendMail(String toEmailAddress, String subject, String message) { + try { + NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); + GsonFactory jsonFactory = GsonFactory.getDefaultInstance(); + Gmail service = + new Gmail.Builder( + httpTransport, + jsonFactory, + getCredentials(httpTransport, jsonFactory)) + .setApplicationName("Test Mailer") + .build(); + + // Encode as MIME message + Properties props = new Properties(); + Session session = Session.getDefaultInstance(props, null); + MimeMessage email = new MimeMessage(session); + email.setFrom(new InternetAddress(fromEmailAddress)); + email.addRecipient( + javax.mail.Message.RecipientType.TO, new InternetAddress(toEmailAddress)); + email.setSubject(subject); + email.setContent(message, "text/html;charset=utf-8"); + + // Encode and wrap the MIME message into a gmail message + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + email.writeTo(buffer); + byte[] rawMessageBytes = buffer.toByteArray(); + String encodedEmail = Base64.encodeBase64URLSafeString(rawMessageBytes); + Message msg = new Message(); + msg.setRaw(encodedEmail); + + // Create send message + msg = service.users().messages().send("me", msg).execute(); + System.out.println("Message id: " + msg.getId()); + System.out.println(msg.toPrettyString()); + + } catch (GoogleJsonResponseException e) { + GoogleJsonError error = e.getDetails(); + if (error.getCode() == 403) { + System.err.println("Unable to send message: " + e.getDetails()); + } else { + throw new RuntimeException( + "Gmail Send Mail에서 GoogleJsonResponseException이 발생하였습니다."); + } + } catch (AddressException e) { + throw new RuntimeException("Gmail Send Mail에서 AddressException이 발생하였습니다."); + } catch (MessagingException e) { + throw new RuntimeException("Gmail Send Mail에서 MessagingException이 발생하였습니다."); + } catch (IOException e) { + throw new RuntimeException("Gmail Send Mail에서 IOException이 발생하였습니다."); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Gmail Client GeneralSecurityException이 발생하였습니다."); + } + } +} diff --git a/src/main/resources/application-key.yaml b/src/main/resources/application-key.yaml index a1b27c83..b8cd1efb 100644 --- a/src/main/resources/application-key.yaml +++ b/src/main/resources/application-key.yaml @@ -6,6 +6,7 @@ token: cctv-secret: ${JWT_CCTV_SECRET} google: + service-account: ${GCP_SERVICE_ACCOUNT} official-gmail: ${OFFICIAL_GMAIL} application-credentials: ${GOOGLE_APPLICATION_CREDENTIALS}