Skip to content

Commit

Permalink
Merge pull request #18 from ssu-Recipable/feat/11/oauth
Browse files Browse the repository at this point in the history
Feat: 카카오 소셜 로그인, 자체 회원가입 및 로그인, 메일 서비스 api 구현
  • Loading branch information
Jeongho427 authored May 10, 2024
2 parents 0adcdfe + 8aaf51e commit 6ccf680
Show file tree
Hide file tree
Showing 19 changed files with 409 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.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
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package capstone.recipable.domain.auth.jwt.dto;


import capstone.recipable.domain.user.entity.User;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
Expand All @@ -22,7 +21,7 @@ public Collection<? extends GrantedAuthority> getAuthorities() {
collection.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return user.getNickname();
return user.getLoginId();
}
});
return collection;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import capstone.recipable.domain.auth.oauth.dto.CreateOauthUserRequest;
import capstone.recipable.domain.auth.oauth.dto.CreateUserRequest;
import capstone.recipable.domain.auth.oauth.service.KakaoService;
import capstone.recipable.domain.login.service.LoginService;
import capstone.recipable.domain.user.entity.User;
import capstone.recipable.domain.user.repository.UserRepository;
import capstone.recipable.domain.user.service.UserService;
import com.fasterxml.jackson.core.JsonProcessingException;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
Expand All @@ -16,12 +19,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,11 +38,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.findByLoginId(request.getEmail());
//User user = userOptional.get();
User user = userRepository.findByLoginId(request.getEmail());
HttpHeaders headers = kakaoService.getLoginHeader(user);

return ResponseEntity.ok().headers(headers).body("login");
Expand All @@ -51,7 +53,7 @@ public ResponseEntity<Object> kakaoLogin(@RequestParam String code) throws JsonP
@Operation(summary = "사용자 등록", description = """
사용자 정보 등록을 진행합니다.
거주지까지 입력을 마친 정보로 DB에 사용자 정보를 등록합니다.
DB에 사용자 정보를 등록합니다.
Jwt 토큰을 헤더에 넣어서 "OK" 메세지와 함께 반환합니다.
""")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@
public class CreateOauthUserRequest {
private String name;
private String email;
private String profileImg;
private String gender;
private String birthyear;
private String profileImage;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,12 @@ public class CreateUserRequest {
private String name;

private String email;

private String imageUrl;

private String gender;

private String birthyear;

private String location;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
Expand Down Expand Up @@ -36,7 +37,6 @@ public class KakaoService {
private long ACCESS_TOKEN_EXPIRE_TIME;

private final JwtProvider jwtProvider;
private final UserService userService;

//카카오 엑세스 토큰으로 사용자 정보 받아오기
public CreateOauthUserRequest getKakaoInfo(String accessToken) throws JsonProcessingException {
Expand All @@ -56,14 +56,14 @@ public CreateOauthUserRequest getKakaoInfo(String accessToken) throws JsonProces

// 필수 정보
String name = jsonNode.path("properties").path("nickname").asText();
/*String profileImage = jsonNode.path("properties").path("profile_image").asText("");
String gender = jsonNode.path("kakao_account").path("gender").asText();*/
String profileImage = jsonNode.path("properties").path("profile_image").asText("");
String gender = jsonNode.path("kakao_account").path("gender").asText();
String birthyear = jsonNode.path("kakao_account").path("birthyear").asText();
String email = jsonNode.path("kakao_account").path("email").asText("");

System.out.println("= = = " + email + " " + name);
System.out.println("= = = " + email + " " + gender + " " + name + " " + birthyear + " " + profileImage);

return new CreateOauthUserRequest(name, email, birthyear);
return new CreateOauthUserRequest(name, email, gender, birthyear, profileImage);
}
return null;
}
Expand Down Expand Up @@ -112,12 +112,4 @@ public HttpHeaders getLoginHeader(User user) {
return headers;
}

public boolean userExists(String email) {
/*Optional<User> userOptional*/User user = userService.findByLoginId(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 capstone.recipable.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 capstone.recipable.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 capstone.recipable.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 capstone.recipable.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 capstone.recipable.domain.login.controller;


import capstone.recipable.domain.auth.oauth.service.KakaoService;
import capstone.recipable.domain.login.dto.LoginRequest;
import capstone.recipable.domain.login.service.LoginService;
import capstone.recipable.domain.user.entity.User;
import capstone.recipable.domain.user.repository.UserRepository;
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.findByLoginId(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 capstone.recipable.domain.login.dto;

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

@AllArgsConstructor
@NoArgsConstructor
@Getter
public class LoginRequest {

private String email;
private String password;
}
Loading

0 comments on commit 6ccf680

Please sign in to comment.