diff --git a/src/main/java/com/rest/api/controller/common/SocialController.java b/src/main/java/com/rest/api/controller/common/SocialController.java index 4919864..2a400ea 100644 --- a/src/main/java/com/rest/api/controller/common/SocialController.java +++ b/src/main/java/com/rest/api/controller/common/SocialController.java @@ -1,14 +1,11 @@ package com.rest.api.controller.common; import com.google.gson.Gson; -import com.rest.api.model.social.RetKakaoAuth; +import com.rest.api.service.social.KakaoService; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; -import org.springframework.http.*; import org.springframework.stereotype.Controller; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -21,10 +18,9 @@ import org.springframework.web.servlet.ModelAndView; public class SocialController { private final Environment env; - private final RestTemplate restTemplate; - private final Gson gson; + private final KakaoService kakaoService; @Value("${spring.url.base}") private String baseUrl; @@ -57,22 +53,7 @@ public class SocialController { */ @GetMapping(value = "/kakao") public ModelAndView redirectKakao(ModelAndView mav, @RequestParam String code) { - // Set header : Content-type: application/x-www-form-urlencoded - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - // Set parameter - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("grant_type", "authorization_code"); - params.add("client_id", kakaoClientId); - params.add("redirect_uri", baseUrl + kakaoRedirect); - params.add("code", code); - // Set http entity - HttpEntity> request = new HttpEntity<>(params, headers); - ResponseEntity response = restTemplate.postForEntity(env.getProperty("spring.social.kakao.url.token"), request, String.class); - if (response.getStatusCode() == HttpStatus.OK) { - RetKakaoAuth authInfo = gson.fromJson(response.getBody(), RetKakaoAuth.class); - mav.addObject("authInfo", authInfo); - } + mav.addObject("authInfo", kakaoService.getKakaoTokenInfo(code)); mav.setViewName("social/redirectKakao"); return mav; } diff --git a/src/main/java/com/rest/api/controller/v1/SignController.java b/src/main/java/com/rest/api/controller/v1/SignController.java index a26724d..b77949c 100644 --- a/src/main/java/com/rest/api/controller/v1/SignController.java +++ b/src/main/java/com/rest/api/controller/v1/SignController.java @@ -1,6 +1,5 @@ package com.rest.api.controller.v1; -import com.google.gson.Gson; import com.rest.api.advice.exception.CEmailSigninFailedException; import com.rest.api.advice.exception.CUserExistException; import com.rest.api.advice.exception.CUserNotFoundException; @@ -11,15 +10,13 @@ import com.rest.api.model.response.SingleResult; import com.rest.api.model.social.KakaoProfile; import com.rest.api.repo.UserJpaRepo; import com.rest.api.service.ResponseService; -import com.rest.api.service.user.UserService; +import com.rest.api.service.social.KakaoService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; -import org.springframework.core.env.Environment; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.RestTemplate; import java.util.Collections; import java.util.Optional; @@ -34,10 +31,7 @@ public class SignController { private final JwtTokenProvider jwtTokenProvider; private final ResponseService responseService; private final PasswordEncoder passwordEncoder; - private final RestTemplate restTemplate; - private final Environment env; - private final Gson gson; - private final UserService userService; + private final KakaoService kakaoService; @ApiOperation(value = "로그인", notes = "이메일 회원 로그인을 한다.") @PostMapping(value = "/signin") @@ -57,7 +51,7 @@ public class SignController { @ApiParam(value = "서비스 제공자 provider", required = true, defaultValue = "kakao") @PathVariable String provider, @ApiParam(value = "소셜 access_token", required = true) @RequestParam String accessToken) { - KakaoProfile profile = userService.getKakaoProfile(accessToken); + KakaoProfile profile = kakaoService.getKakaoProfile(accessToken); User user = userJpaRepo.findByUidAndProvider(String.valueOf(profile.getId()), provider).orElseThrow(CUserNotFoundException::new); return responseService.getSingleResult(jwtTokenProvider.createToken(String.valueOf(user.getMsrl()), user.getRoles())); } @@ -80,12 +74,12 @@ public class SignController { @ApiOperation(value = "소셜 계정 가입", notes = "소셜 계정 회원가입을 한다.") @PostMapping(value = "/signup/{provider}") public CommonResult signupProvider(@ApiParam(value = "서비스 제공자 provider", required = true, defaultValue = "kakao") @PathVariable String provider, - @ApiParam(value = "소셜 access_token", required = true) @RequestParam String accessToken, - @ApiParam(value = "이름", required = true) @RequestParam String name) { + @ApiParam(value = "소셜 access_token", required = true) @RequestParam String accessToken, + @ApiParam(value = "이름", required = true) @RequestParam String name) { - KakaoProfile profile = userService.getKakaoProfile(accessToken); + KakaoProfile profile = kakaoService.getKakaoProfile(accessToken); Optional user = userJpaRepo.findByUidAndProvider(String.valueOf(profile.getId()), provider); - if(user.isPresent()) + if (user.isPresent()) throw new CUserExistException(); User inUser = User.builder() diff --git a/src/main/java/com/rest/api/model/social/KakaoProfile.java b/src/main/java/com/rest/api/model/social/KakaoProfile.java index 7fa45b1..91594f0 100644 --- a/src/main/java/com/rest/api/model/social/KakaoProfile.java +++ b/src/main/java/com/rest/api/model/social/KakaoProfile.java @@ -2,18 +2,21 @@ package com.rest.api.model.social; import lombok.Getter; import lombok.Setter; +import lombok.ToString; @Getter @Setter +@ToString public class KakaoProfile { - private Long id; - private Properties properties; + private Long id; + private Properties properties; - @Getter - @Setter - private static class Properties { - private String nickname; - private String thumbnail_image; - private String profile_image; - } + @Getter + @Setter + @ToString + private static class Properties { + private String nickname; + private String thumbnail_image; + private String profile_image; + } } diff --git a/src/main/java/com/rest/api/service/social/KakaoService.java b/src/main/java/com/rest/api/service/social/KakaoService.java new file mode 100644 index 0000000..0290b87 --- /dev/null +++ b/src/main/java/com/rest/api/service/social/KakaoService.java @@ -0,0 +1,70 @@ +package com.rest.api.service.social; + +import com.google.gson.Gson; +import com.rest.api.advice.exception.CCommunicationException; +import com.rest.api.model.social.KakaoProfile; +import com.rest.api.model.social.RetKakaoAuth; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +@RequiredArgsConstructor +@Service +public class KakaoService { + + private final RestTemplate restTemplate; + private final Environment env; + private final Gson gson; + + @Value("${spring.url.base}") + private String baseUrl; + + @Value("${spring.social.kakao.client_id}") + private String kakaoClientId; + + @Value("${spring.social.kakao.redirect}") + private String kakaoRedirect; + + public KakaoProfile getKakaoProfile(String accessToken) { + // Set header : Content-type: application/x-www-form-urlencoded + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.set("Authorization", "Bearer " + accessToken); + + // Set http entity + HttpEntity> request = new HttpEntity<>(null, headers); + try { + // Request profile + ResponseEntity response = restTemplate.postForEntity(env.getProperty("spring.social.kakao.url.profile"), request, String.class); + if (response.getStatusCode() == HttpStatus.OK) + return gson.fromJson(response.getBody(), KakaoProfile.class); + } catch (Exception e) { + throw new CCommunicationException(); + } + throw new CCommunicationException(); + } + + public RetKakaoAuth getKakaoTokenInfo(String code) { + // Set header : Content-type: application/x-www-form-urlencoded + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + // Set parameter + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("grant_type", "authorization_code"); + params.add("client_id", kakaoClientId); + params.add("redirect_uri", baseUrl + kakaoRedirect); + params.add("code", code); + // Set http entity + HttpEntity> request = new HttpEntity<>(params, headers); + ResponseEntity response = restTemplate.postForEntity(env.getProperty("spring.social.kakao.url.token"), request, String.class); + if (response.getStatusCode() == HttpStatus.OK) { + return gson.fromJson(response.getBody(), RetKakaoAuth.class); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/rest/api/service/user/UserService.java b/src/main/java/com/rest/api/service/user/UserService.java deleted file mode 100644 index d431ea9..0000000 --- a/src/main/java/com/rest/api/service/user/UserService.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.rest.api.service.user; - -import com.google.gson.Gson; -import com.rest.api.advice.exception.CCommunicationException; -import com.rest.api.model.social.KakaoProfile; -import lombok.RequiredArgsConstructor; -import org.springframework.core.env.Environment; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -@RequiredArgsConstructor -@Service -public class UserService { - - private final RestTemplate restTemplate; - private final Environment env; - private final Gson gson; - - public KakaoProfile getKakaoProfile(String accessToken) { - // Set header : Content-type: application/x-www-form-urlencoded - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("Authorization", "Bearer " + accessToken); - - // Set http entity - HttpEntity> request = new HttpEntity<>(null, headers); - ResponseEntity response = restTemplate.postForEntity(env.getProperty("spring.social.kakao.url.profile"), request, String.class); - if (response.getStatusCode() == HttpStatus.OK) - return gson.fromJson(response.getBody(), KakaoProfile.class); - else - throw new CCommunicationException(); - } -} \ No newline at end of file diff --git a/src/main/resources/templates/social/login.ftl b/src/main/resources/templates/social/login.ftl index 439042f..bb2f64a 100644 --- a/src/main/resources/templates/social/login.ftl +++ b/src/main/resources/templates/social/login.ftl @@ -1,6 +1,6 @@ \ No newline at end of file diff --git a/src/test/java/com/rest/api/controller/v1/SignControllerTest.java b/src/test/java/com/rest/api/controller/v1/SignControllerTest.java index 094ebf3..c21b831 100644 --- a/src/test/java/com/rest/api/controller/v1/SignControllerTest.java +++ b/src/test/java/com/rest/api/controller/v1/SignControllerTest.java @@ -1,19 +1,16 @@ package com.rest.api.controller.v1; -import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.context.WebApplicationContext; import java.time.LocalDateTime; import java.time.ZoneId; @@ -86,4 +83,39 @@ public class SignControllerTest { .andExpect(jsonPath("$.success").value(false)) .andExpect(jsonPath("$.code").value(-9999)); } + + @Test + public void signInProviderFail() throws Exception { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("accessToken", "XXXXXXXX"); + mockMvc.perform(post("/v1/signin/kakao").params(params)) + .andDo(print()) + .andExpect(status().is5xxServerError()) + .andExpect(jsonPath("$.success").value(false)) + .andExpect(jsonPath("$.code").value(-1004)); + } + + @Test + public void signUpProvider() throws Exception { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("accessToken", "HizF3ir9522bMW3shkO0x0T9zBdXFCW1WsF56Qo9dVsAAAFqMwTqHw"); + params.add("name", "kakaoKing!"); + mockMvc.perform(post("/v1/signup/kakao").params(params)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.code").value(0)); + } + + @Test + public void signInProvider() throws Exception { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("accessToken", "HizF3ir9522bMW3shkO0x0T9zBdXFCW1WsF56Qo9dVsAAAFqMwTqHw"); + mockMvc.perform(post("/v1/signin/kakao").params(params)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.code").value(0)) + .andExpect(jsonPath("$.data").exists()); + } } \ No newline at end of file diff --git a/src/test/java/com/rest/api/service/social/KakaoServiceTest.java b/src/test/java/com/rest/api/service/social/KakaoServiceTest.java new file mode 100644 index 0000000..bd2f1da --- /dev/null +++ b/src/test/java/com/rest/api/service/social/KakaoServiceTest.java @@ -0,0 +1,29 @@ +package com.rest.api.service.social; + +import com.rest.api.model.social.KakaoProfile; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class KakaoServiceTest { + + @Autowired + private KakaoService kakaoService; + + @Test + public void whenGetKakaoProfile_thenReturnProfile() { + + String accessToken = "xjsMzpQtIr4w13FIQvL3R7BW7X4yvm1KmzXCTwopyWAAAAFqMxEcwA"; + // given + KakaoProfile profile = kakaoService.getKakaoProfile(accessToken); + // then + assertNotNull(profile); + assertEquals(profile.getId(), Long.valueOf(1066788171)); + } +} \ No newline at end of file