diff --git a/.gitignore b/.gitignore index b8f534c54..a8ca8c3a2 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,5 @@ gradle-app.setting .idea/workspace.xml .idea/modules.xml .idea/jarRepositories.xml + +BE/src/main/resources/oauth.properties diff --git a/BE/src/main/java/com/mj_eno/sidedish/domain/user/User.java b/BE/src/main/java/com/mj_eno/sidedish/domain/user/User.java new file mode 100644 index 000000000..ee5f507bd --- /dev/null +++ b/BE/src/main/java/com/mj_eno/sidedish/domain/user/User.java @@ -0,0 +1,67 @@ +package com.mj_eno.sidedish.domain.user; + +import com.mj_eno.sidedish.web.dto.EmailDTO; +import com.mj_eno.sidedish.web.dto.TokenDTO; +import com.mj_eno.sidedish.web.dto.UserInfoDTO; +import org.springframework.data.annotation.Id; + +public class User { + + @Id + private Long id; + private String name; + private String email; + private String userId; + private String token; + + public User() {} + + public User(UserInfoDTO userInfoDTO, EmailDTO emailDTO, TokenDTO tokenDTO) { + this.name = userInfoDTO.getName(); + this.email = emailDTO.getEmail(); + this.userId = userInfoDTO.getLogin(); + this.token = tokenDTO.getAccess_token(); + } + + public void update(UserInfoDTO userInfoDTO, EmailDTO emailDTO, TokenDTO tokenDTO) { + this.name = userInfoDTO.getName(); + this.email = emailDTO.getEmail(); + this.userId = userInfoDTO.getLogin(); + this.token = tokenDTO.getAccess_token(); + } + + public void removeToken() { + this.token = null; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + + public String getUserId() { + return userId; + } + + public String getToken() { + return token; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", name='" + name + '\'' + + ", email='" + email + '\'' + + ", userId='" + userId + '\'' + + ", token='" + token + '\'' + + '}'; + } +} diff --git a/BE/src/main/java/com/mj_eno/sidedish/domain/user/UserRepository.java b/BE/src/main/java/com/mj_eno/sidedish/domain/user/UserRepository.java new file mode 100644 index 000000000..fe407758f --- /dev/null +++ b/BE/src/main/java/com/mj_eno/sidedish/domain/user/UserRepository.java @@ -0,0 +1,12 @@ +package com.mj_eno.sidedish.domain.user; + +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface UserRepository extends CrudRepository { + + Optional findByEmail(String email); + + Optional findByToken(String token); +} diff --git a/BE/src/main/java/com/mj_eno/sidedish/service/UserService.java b/BE/src/main/java/com/mj_eno/sidedish/service/UserService.java new file mode 100644 index 000000000..49058188b --- /dev/null +++ b/BE/src/main/java/com/mj_eno/sidedish/service/UserService.java @@ -0,0 +1,103 @@ +package com.mj_eno.sidedish.service; + +import com.mj_eno.sidedish.domain.user.User; +import com.mj_eno.sidedish.domain.user.UserRepository; +import com.mj_eno.sidedish.exception.EntityNotFoundException; +import com.mj_eno.sidedish.exception.ErrorMessage; +import com.mj_eno.sidedish.web.dto.EmailDTO; +import com.mj_eno.sidedish.web.dto.TokenDTO; +import com.mj_eno.sidedish.web.dto.UserInfoDTO; +import com.mj_eno.sidedish.web.dto.UserResponseDTO; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.env.Environment; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.List; + +@PropertySource("classpath:/oauth.properties") +@Service +public class UserService { + + private final UserRepository userRepository; + private final RestTemplate restTemplate; + private final Environment environment; + + public UserService(UserRepository userRepository, Environment environment, RestTemplateBuilder restTemplateBuilder) { + this.userRepository = userRepository; + this.restTemplate = restTemplateBuilder.build(); + this.environment = environment; + } + + public UserResponseDTO login(String code) { + TokenDTO tokenDTO = tokenRequestApi(code); + UserInfoDTO userInfoDTO = userInfoRequestApi(tokenDTO.getAccess_token()); + EmailDTO emailDTO = emailRequestApi(tokenDTO.getAccess_token()); + + if (verifyUser(emailDTO)) { + User user = findByEmail(emailDTO); + user.update(userInfoDTO, emailDTO, tokenDTO); + return new UserResponseDTO(userRepository.save(user)); + } + User user = new User(userInfoDTO, emailDTO, tokenDTO); + return new UserResponseDTO(userRepository.save(user)); + } + + public void logout(String token) { + User user = findByToken(token); + user.removeToken(); + userRepository.save(user); + } + + private TokenDTO tokenRequestApi(String code) { + String id = environment.getProperty("github.client.id"); + String secret = environment.getProperty("github.secret"); + String url = environment.getProperty("github.access.token.url"); + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url) + .queryParam("client_id", id) + .queryParam("client_secret", secret) + .queryParam("code", code); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); + httpHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + HttpEntity httpEntity = new HttpEntity<>(httpHeaders); + return restTemplate.exchange(builder.toUriString(), HttpMethod.POST, httpEntity, TokenDTO.class).getBody(); + } + + private UserInfoDTO userInfoRequestApi(String token) { + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://api.github.com/user"); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.set(HttpHeaders.AUTHORIZATION, "token " + token); + HttpEntity httpEntity = new HttpEntity<>(httpHeaders); + return restTemplate.exchange(builder.toUriString(), HttpMethod.GET, httpEntity, UserInfoDTO.class).getBody(); + } + + private EmailDTO emailRequestApi(String token) { + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://api.github.com/user/emails"); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.set(HttpHeaders.AUTHORIZATION, "token " + token); + HttpEntity httpEntity3 = new HttpEntity<>(httpHeaders); + List emailDTOList = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, httpEntity3, new ParameterizedTypeReference>() {}).getBody(); + return emailDTOList.get(0); + } + + private boolean verifyUser(EmailDTO emailDTO) { + return userRepository.findByEmail(emailDTO.getEmail()).isPresent(); + } + + private User findByEmail(EmailDTO emailDTO) { + return userRepository.findByEmail(emailDTO.getEmail()).orElseThrow( + () -> new EntityNotFoundException(ErrorMessage.ENTITY_NOT_FOUND) + ); + } + + private User findByToken(String token) { + return userRepository.findByToken(token).orElseThrow( + () -> new EntityNotFoundException(ErrorMessage.ENTITY_NOT_FOUND) + ); + } +} diff --git a/BE/src/main/java/com/mj_eno/sidedish/web/UserController.java b/BE/src/main/java/com/mj_eno/sidedish/web/UserController.java new file mode 100644 index 000000000..704126fdc --- /dev/null +++ b/BE/src/main/java/com/mj_eno/sidedish/web/UserController.java @@ -0,0 +1,30 @@ +package com.mj_eno.sidedish.web; + +import com.mj_eno.sidedish.service.UserService; +import com.mj_eno.sidedish.web.dto.UserResponseDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.*; + +@RestController +public class UserController { + + public final UserService userService; + private final Logger logger = LoggerFactory.getLogger(DishController.class); + + public UserController(UserService userService) { + this.userService = userService; + } + + @PostMapping("/login") + public UserResponseDTO login(@RequestParam String code) { + logger.info("로그인 요청"); + return userService.login(code); + } + + @GetMapping("/logout") + public void logout(@RequestHeader String token) { + logger.info("로그아웃 요청"); + userService.logout(token); + } +} diff --git a/BE/src/main/java/com/mj_eno/sidedish/web/dto/EmailDTO.java b/BE/src/main/java/com/mj_eno/sidedish/web/dto/EmailDTO.java new file mode 100644 index 000000000..12dd51f2a --- /dev/null +++ b/BE/src/main/java/com/mj_eno/sidedish/web/dto/EmailDTO.java @@ -0,0 +1,21 @@ +package com.mj_eno.sidedish.web.dto; + +public class EmailDTO { + + private String email; + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + @Override + public String toString() { + return "emailDTO{" + + "email='" + email + '\'' + + '}'; + } +} diff --git a/BE/src/main/java/com/mj_eno/sidedish/web/dto/TokenDTO.java b/BE/src/main/java/com/mj_eno/sidedish/web/dto/TokenDTO.java new file mode 100644 index 000000000..5feb7ef58 --- /dev/null +++ b/BE/src/main/java/com/mj_eno/sidedish/web/dto/TokenDTO.java @@ -0,0 +1,33 @@ +package com.mj_eno.sidedish.web.dto; + +public class TokenDTO { + + private String access_token; + private String token_type; + private String scope; + + public void setAccess_token(String access_token) { + this.access_token = access_token; + } + + public void setToken_type(String token_type) { + this.token_type = token_type; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getAccess_token() { + return access_token; + } + + @Override + public String toString() { + return "TokenDTO{" + + "access_token='" + access_token + '\'' + + ", token_type='" + token_type + '\'' + + ", scope='" + scope + '\'' + + '}'; + } +} diff --git a/BE/src/main/java/com/mj_eno/sidedish/web/dto/UserInfoDTO.java b/BE/src/main/java/com/mj_eno/sidedish/web/dto/UserInfoDTO.java new file mode 100644 index 000000000..33b529e21 --- /dev/null +++ b/BE/src/main/java/com/mj_eno/sidedish/web/dto/UserInfoDTO.java @@ -0,0 +1,31 @@ +package com.mj_eno.sidedish.web.dto; + +public class UserInfoDTO { + + private String login; + private String name; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "UserInfoDTO{" + + "login='" + login + '\'' + + ", name='" + name + '\'' + + '}'; + } +} diff --git a/BE/src/main/java/com/mj_eno/sidedish/web/dto/UserResponseDTO.java b/BE/src/main/java/com/mj_eno/sidedish/web/dto/UserResponseDTO.java new file mode 100644 index 000000000..ccc558c7e --- /dev/null +++ b/BE/src/main/java/com/mj_eno/sidedish/web/dto/UserResponseDTO.java @@ -0,0 +1,34 @@ +package com.mj_eno.sidedish.web.dto; + +import com.mj_eno.sidedish.domain.user.User; + +public class UserResponseDTO { + + private String name; + private String email; + private String userId; + private String token; + + public UserResponseDTO(User user) { + this.name = user.getName(); + this.email = user.getEmail(); + this.userId = user.getUserId(); + this.token = user.getToken(); + } + + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + + public String getUserId() { + return userId; + } + + public String getToken() { + return token; + } +} diff --git a/BE/src/main/resources/schema.sql b/BE/src/main/resources/schema.sql index 7980cdd49..efa08a788 100644 --- a/BE/src/main/resources/schema.sql +++ b/BE/src/main/resources/schema.sql @@ -14,6 +14,19 @@ SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,N CREATE SCHEMA IF NOT EXISTS `side_dish` DEFAULT CHARACTER SET utf8; USE `side_dish`; +-- ----------------------------------------------------- +-- Table `side_dish`.`user` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `side_dish`.`user`; +CREATE TABLE IF NOT EXISTS `side_dish`.`user` ( + `id` INT AUTO_INCREMENT, + `name` VARCHAR(45) NOT NULL, + `email` VARCHAR(45) NOT NULL, + `user_id` VARCHAR(45) NOT NULL, + `token` VARCHAR(255), + PRIMARY KEY (`id`)) + ENGINE = InnoDB; + -- ----------------------------------------------------- -- Table `side_dish`.`best_menu_category` -- -----------------------------------------------------