Skip to content

Commit e872d0a

Browse files
Merge pull request #237 from Podo-Store/develop
소셜 로그인 기능 구현 완료
2 parents 5a3e5f8 + 48fec85 commit e872d0a

File tree

15 files changed

+622
-143
lines changed

15 files changed

+622
-143
lines changed

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ dependencies {
5454

5555
// swagger
5656
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
57+
58+
// 소셜 로그인
59+
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
5760
}
5861

5962
tasks.named('test') {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package PodoeMarket.podoemarket.common.config;
2+
3+
import PodoeMarket.podoemarket.common.entity.type.SocialLoginType;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.core.convert.converter.Converter;
6+
7+
@Configuration
8+
public class SocialLoginTypeConverter implements Converter<String, SocialLoginType> {
9+
//대문자 값을 소문자로 mapping
10+
11+
@Override
12+
public SocialLoginType convert(String s) {
13+
return SocialLoginType.valueOf(s.toUpperCase());
14+
}
15+
}

src/main/java/PodoeMarket/podoemarket/common/entity/UserEntity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package PodoeMarket.podoemarket.common.entity;
22

3-
import PodoeMarket.podoemarket.common.entity.type.SignUpType;
3+
import PodoeMarket.podoemarket.common.entity.type.SocialLoginType;
44
import jakarta.persistence.*;
55
import lombok.*;
66
import org.hibernate.annotations.ColumnDefault;
@@ -39,7 +39,7 @@ public class UserEntity {
3939

4040
@Enumerated(EnumType.STRING)
4141
@Column
42-
private SignUpType signUpType;
42+
private SocialLoginType socialLoginType;
4343

4444
@Column(nullable = false, updatable = false)
4545
private LocalDateTime createdAt;

src/main/java/PodoeMarket/podoemarket/common/entity/type/SignUpType.java renamed to src/main/java/PodoeMarket/podoemarket/common/entity/type/SocialLoginType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package PodoeMarket.podoemarket.common.entity.type;
22

3-
public enum SignUpType {
3+
public enum SocialLoginType {
44
KAKAO,
55
GOOGLE,
66
NAVER

src/main/java/PodoeMarket/podoemarket/common/security/JwtAuthenticationFilter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
7676
securityContext.setAuthentication(authentication); // context에 인증 정보 설정
7777
SecurityContextHolder.setContext(securityContext); // SecurityContextHolder 저장
7878
}
79-
}else{
79+
} else {
8080
log.warn("Token is null");
8181
filterChain.doFilter(request, response);
8282
return;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package PodoeMarket.podoemarket.user.controller;
2+
3+
import PodoeMarket.podoemarket.common.entity.UserEntity;
4+
import PodoeMarket.podoemarket.common.entity.type.SocialLoginType;
5+
import PodoeMarket.podoemarket.common.security.TokenProvider;
6+
import PodoeMarket.podoemarket.dto.response.ResponseDTO;
7+
import PodoeMarket.podoemarket.user.dto.response.SignInResponseDTO;
8+
import PodoeMarket.podoemarket.user.service.OAuthService;
9+
import PodoeMarket.podoemarket.user.service.UserService;
10+
import lombok.RequiredArgsConstructor;
11+
import lombok.extern.slf4j.Slf4j;
12+
import org.springframework.http.ResponseEntity;
13+
import org.springframework.web.bind.annotation.*;
14+
15+
@RequiredArgsConstructor
16+
@RestController
17+
@Slf4j
18+
@RequestMapping("/auth")
19+
public class OauthController {
20+
private final OAuthService oauthService;
21+
private final UserService userService;
22+
private final TokenProvider tokenProvider;
23+
24+
@GetMapping(value = "/{socialLoginType}")
25+
public ResponseEntity<?> socialLoginType(@PathVariable(name = "socialLoginType") SocialLoginType socialLoginType) {
26+
String redirectURL = oauthService.request(socialLoginType);
27+
28+
return ResponseEntity.ok().body(redirectURL);
29+
}
30+
31+
@GetMapping(value = "/{socialLoginType}/callback")
32+
public ResponseEntity<?> callback(@PathVariable(name = "socialLoginType") SocialLoginType socialLoginType,
33+
@RequestParam(name = "code") String code) {
34+
try {
35+
final UserEntity user = oauthService.requestUser(socialLoginType, code);
36+
37+
if (userService.checkUserId(user.getUserId())) {
38+
final UserEntity signInUser = userService.getByUserId(user.getUserId());
39+
40+
final SignInResponseDTO resUserDTO = SignInResponseDTO.builder()
41+
.nickname(signInUser.getNickname())
42+
.auth(signInUser.isAuth())
43+
.accessToken(tokenProvider.createAccessToken(user))
44+
.refreshToken(tokenProvider.createRefreshToken(user))
45+
.build();
46+
47+
return ResponseEntity.ok().body(resUserDTO);
48+
} else {
49+
// 새 사용자는 저장
50+
oauthService.create(user);
51+
52+
return ResponseEntity.ok().body(true);
53+
}
54+
} catch(Exception e) {
55+
ResponseDTO resDTO = ResponseDTO.builder().error(e.getMessage()).build();
56+
return ResponseEntity.badRequest().body(resDTO);
57+
}
58+
}
59+
}

src/main/java/PodoeMarket/podoemarket/user/controller/UserController.java

Lines changed: 4 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import PodoeMarket.podoemarket.Utils.ValidCheck;
44
import PodoeMarket.podoemarket.common.config.jwt.JwtProperties;
5-
import PodoeMarket.podoemarket.common.entity.type.SignUpType;
65
import PodoeMarket.podoemarket.dto.EmailCheckDTO;
76
import PodoeMarket.podoemarket.dto.EmailRequestDTO;
87
import PodoeMarket.podoemarket.dto.response.ResponseDTO;
@@ -12,7 +11,8 @@
1211
import PodoeMarket.podoemarket.mail.MailSendService;
1312
import PodoeMarket.podoemarket.service.RedisUtil;
1413
import PodoeMarket.podoemarket.user.dto.request.SignInRequestDTO;
15-
import PodoeMarket.podoemarket.user.dto.response.KakaoUserInfoResponseDTO;
14+
import PodoeMarket.podoemarket.user.dto.response.SignInResponseDTO;
15+
import PodoeMarket.podoemarket.user.dto.response.TokenResponseDTO;
1616
import PodoeMarket.podoemarket.user.service.UserService;
1717
import io.jsonwebtoken.Claims;
1818
import io.jsonwebtoken.Jwts;
@@ -196,11 +196,7 @@ public ResponseEntity<?> authenticate(@RequestBody SignInRequestDTO dto) {
196196
UserEntity user = userService.getByCredentials(dto.getUserId(), dto.getPassword(), pwdEncoder);
197197

198198
if(user.getId() != null) {
199-
final UserDTO resUserDTO = UserDTO.builder()
200-
.id(user.getId())
201-
.userId(user.getUserId())
202-
.email(user.getEmail())
203-
.password(user.getPassword())
199+
final SignInResponseDTO resUserDTO = SignInResponseDTO.builder()
204200
.nickname(user.getNickname())
205201
.auth(user.isAuth())
206202
.accessToken(tokenProvider.createAccessToken(user))
@@ -222,48 +218,6 @@ public ResponseEntity<?> authenticate(@RequestBody SignInRequestDTO dto) {
222218
}
223219
}
224220

225-
@GetMapping("/kakao")
226-
public ResponseEntity<?> getKakao(@RequestParam("code") String code) {
227-
try {
228-
String accessToken = userService.getAccessTokenFromKakao(code);
229-
//
230-
KakaoUserInfoResponseDTO userInfo = userService.getUserInfo(accessToken);
231-
// KakaoUserInfoResponseDTO userInfo = userService.getUserInfo(code);
232-
233-
// 회원가입 여부 확인
234-
if (userService.checkEmail(userInfo.kakaoAccount.email)) {
235-
UserEntity user = userService.getByUserId(String.valueOf(userInfo.id));
236-
237-
final UserDTO resUserDTO = UserDTO.builder()
238-
.id(user.getId())
239-
.userId(user.getUserId())
240-
.email(user.getEmail())
241-
.password(user.getPassword())
242-
.nickname(user.getNickname())
243-
.auth(user.isAuth())
244-
.accessToken(tokenProvider.createAccessToken(user))
245-
.refreshToken(tokenProvider.createRefreshToken(user))
246-
.build();
247-
248-
return ResponseEntity.ok().body(resUserDTO);
249-
} else {
250-
final UserEntity user = UserEntity.builder()
251-
.userId(String.valueOf(userInfo.id))
252-
.email(userInfo.kakaoAccount.email)
253-
.nickname(userInfo.kakaoAccount.profile.nickName)
254-
.signUpType(SignUpType.KAKAO)
255-
.build();
256-
257-
userService.createSocialUser(user);
258-
259-
return ResponseEntity.ok().body(true);
260-
}
261-
} catch(Exception e) {
262-
ResponseDTO resDTO = ResponseDTO.builder().error(e.getMessage()).build();
263-
return ResponseEntity.badRequest().body(resDTO);
264-
}
265-
}
266-
267221
@PostMapping("/find/mailSend")
268222
public ResponseEntity<?> findMailSend(@RequestBody EmailRequestDTO dto) {
269223
try {
@@ -408,7 +362,7 @@ public ResponseEntity<?> createNewToken(HttpServletRequest request){
408362
UUID id = UUID.fromString(claims.getSubject());
409363

410364
UserEntity user = userService.getById(id);
411-
final UserDTO resUserDTO = UserDTO.builder()
365+
final TokenResponseDTO resUserDTO = TokenResponseDTO.builder()
412366
.userId(user.getUserId())
413367
.nickname(user.getNickname())
414368
.email(user.getEmail())
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package PodoeMarket.podoemarket.user.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class SignInResponseDTO {
13+
private String nickname;
14+
private boolean auth;
15+
private String accessToken; // jwt 저장공간
16+
private String refreshToken; // jwt 저장공간
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package PodoeMarket.podoemarket.user.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class TokenResponseDTO {
13+
private String userId;
14+
private String nickname;
15+
private String email;
16+
private String accessToken;
17+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package PodoeMarket.podoemarket.user.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.springframework.http.HttpStatus;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.stereotype.Component;
8+
import org.springframework.web.client.RestTemplate;
9+
import org.springframework.beans.factory.annotation.Value;
10+
11+
import java.util.HashMap;
12+
import java.util.Map;
13+
import java.util.stream.Collectors;
14+
15+
@Slf4j
16+
@Component
17+
@RequiredArgsConstructor
18+
public class GoogleOauth implements SocialOauth {
19+
@Value("${sns.google.url}")
20+
private String GOOGLE_SNS_BASE_URL;
21+
@Value("${sns.google.client-id}")
22+
private String GOOGLE_SNS_CLIENT_ID;
23+
@Value("${sns.google.callback-url}")
24+
private String GOOGLE_SNS_CALLBACK_URL;
25+
@Value("${sns.google.client-secret}")
26+
private String GOOGLE_SNS_CLIENT_SECRET;
27+
@Value("${sns.google.token-url}")
28+
private String GOOGLE_SNS_TOKEN_BASE_URL;
29+
30+
@Override
31+
public String getOauthRedirectURL() {
32+
Map<String, Object> params = new HashMap<>();
33+
params.put("scope", "email profile");
34+
params.put("response_type", "code");
35+
params.put("client_id", GOOGLE_SNS_CLIENT_ID);
36+
params.put("redirect_uri", GOOGLE_SNS_CALLBACK_URL);
37+
38+
String parameterString = params.entrySet().stream()
39+
.map(x -> x.getKey() + "=" + x.getValue())
40+
.collect(Collectors.joining("&"));
41+
42+
return GOOGLE_SNS_BASE_URL + "?" + parameterString;
43+
}
44+
45+
@Override
46+
public String requestAccessToken(String code) {
47+
RestTemplate restTemplate = new RestTemplate();
48+
49+
// 파라미터 설정
50+
Map<String, Object> params = new HashMap<>();
51+
params.put("code", code);
52+
params.put("client_id", GOOGLE_SNS_CLIENT_ID);
53+
params.put("client_secret", GOOGLE_SNS_CLIENT_SECRET);
54+
params.put("redirect_uri", GOOGLE_SNS_CALLBACK_URL);
55+
params.put("grant_type", "authorization_code");
56+
57+
// POST 요청 전송
58+
ResponseEntity<String> responseEntity = restTemplate.postForEntity(GOOGLE_SNS_TOKEN_BASE_URL, params, String.class);
59+
60+
if (responseEntity.getStatusCode() == HttpStatus.OK)
61+
return responseEntity.getBody();
62+
63+
return "구글 로그인 요청 처리 실패";
64+
}
65+
}

0 commit comments

Comments
 (0)