Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
sejoon00 committed Aug 30, 2024
2 parents b64ab11 + 825d038 commit bfb5c0c
Show file tree
Hide file tree
Showing 31 changed files with 489 additions and 35 deletions.
3 changes: 3 additions & 0 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ services:
- GOOGLE_LOGIN_CLIENT_ID=${GOOGLE_LOGIN_CLIENT_ID}
- GOOGLE_LOGIN_CLIENT_SECRET=${GOOGLE_LOGIN_CLIENT_SECRET}
- GOOGLE_LOGIN_REDIRECT_URI=${GOOGLE_LOGIN_REDIRECT_URI}
- KAKAO_LOGIN_CLIENT_ID=${KAKAO_LOGIN_CLIENT_ID}
- KAKAO_LOGIN_CLIENT_SECRET=${KAKAO_LOGIN_CLIENT_SECRET}
- KAKAO_LOGIN_REDIRECT_URI=${KAKAO_LOGIN_REDIRECT_URI}
- SENTRY_DSN=${SENTRY_DSN}
depends_on:
- mysql
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.server.bbo_gak.domain.auth.controller;

import com.server.bbo_gak.domain.auth.dto.request.LoginRequest;
import com.server.bbo_gak.domain.auth.dto.response.LoginResponse;
import com.server.bbo_gak.domain.auth.dto.request.RefreshTokenRequest;
import com.server.bbo_gak.domain.auth.dto.response.LoginResponse;
import com.server.bbo_gak.domain.auth.service.AuthService;
import com.server.bbo_gak.domain.auth.service.oauth.GoogleService;
import com.server.bbo_gak.domain.auth.service.oauth.KakaoService;
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.global.annotation.AuthUser;
import com.server.bbo_gak.global.security.jwt.dto.TokenDto;
Expand All @@ -23,10 +24,10 @@
@RequiredArgsConstructor
public class AuthController {

private static final String SOCIAL_TOKEN_NAME = "SOCIAL-AUTH-TOKEN";
private final AuthService authService;
private final GoogleService googleService;

private static final String SOCIAL_TOKEN_NAME = "SOCIAL-AUTH-TOKEN";
private final KakaoService kakaoService;

@PostMapping("/social-login")
public ResponseEntity<LoginResponse> socialLogin(
Expand All @@ -41,9 +42,14 @@ public ResponseEntity<LoginResponse> socialLogin(
/**
* 프론트 연결 끝나면 지우기
*/
@PostMapping("/test/access-token")
@PostMapping("/test/google/access-token")
public String googleTest(@RequestParam("code") String code) {
return googleService.getToken(code);
return googleService.getGoogleToken(code);
}

@PostMapping("/test/kakao/access-token")
public String kakaoTest(@RequestParam("code") String code) {
return kakaoService.getKakaoToken(code);
}

@PostMapping("/test/login")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.server.bbo_gak.domain.auth.dto.response.oauth;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Map;

public record KakaoOAuthUserInfoResponse(
Long id,
LocalDateTime connected_at,
Map<String, Object> properties,
KakaoAccount kakao_account
) {

public static KakaoOAuthUserInfoResponse from(Map<String, Object> attributes) {
return new KakaoOAuthUserInfoResponse(
Long.valueOf(String.valueOf(attributes.get("id"))),
LocalDateTime.parse(
String.valueOf(attributes.get("connected_at")),
DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault())
),
(Map<String, Object>) attributes.get("properties"),
KakaoAccount.from((Map<String, Object>) attributes.get("kakao_account"))
);
}

public String email() {
return this.kakao_account().email();
}

public String nickname() {
return this.kakao_account().nickname();
}

public record KakaoAccount(
Boolean profileNicknameNeedsAgreement,
Profile profile,
Boolean hasEmail,
Boolean emailNeedsAgreement,
Boolean isEmailValid,
Boolean isEmailVerified,
String email
) {

public static KakaoAccount from(Map<String, Object> attributes) {
return new KakaoAccount(
Boolean.valueOf(String.valueOf(attributes.get("profile_nickname_needs_agreement"))),
Profile.from((Map<String, Object>) attributes.get("profile")),
Boolean.valueOf(String.valueOf(attributes.get("has_email"))),
Boolean.valueOf(String.valueOf(attributes.get("email_needs_agreement"))),
Boolean.valueOf(String.valueOf(attributes.get("is_email_valid"))),
Boolean.valueOf(String.valueOf(attributes.get("is_email_verified"))),
String.valueOf(attributes.get("email"))
);
}

public String nickname() {
return this.profile().nickname();
}

public record Profile(String nickname) {

public static Profile from(Map<String, Object> attributes) {
return new Profile(String.valueOf(attributes.get("nickname")));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.server.bbo_gak.domain.auth.service;

import com.server.bbo_gak.domain.auth.dto.request.LoginRequest;
import com.server.bbo_gak.domain.auth.dto.request.RefreshTokenRequest;
import com.server.bbo_gak.domain.auth.dto.response.LoginResponse;
import com.server.bbo_gak.domain.auth.dto.response.oauth.OauthUserInfoResponse;
import com.server.bbo_gak.domain.auth.dto.request.RefreshTokenRequest;
import com.server.bbo_gak.domain.auth.entity.AuthTestUser;
import com.server.bbo_gak.domain.auth.entity.AuthTestUserRepository;
import com.server.bbo_gak.domain.auth.service.oauth.GoogleService;
import com.server.bbo_gak.domain.auth.service.oauth.KakaoService;
import com.server.bbo_gak.domain.user.entity.Job;
import com.server.bbo_gak.domain.user.entity.OauthProvider;
import com.server.bbo_gak.domain.user.entity.User;
Expand All @@ -32,6 +33,7 @@ public class AuthServiceImpl implements AuthService {
private final RefreshTokenRepository refreshTokenRepository;
private final JwtTokenService jwtTokenService;
private final GoogleService googleService;
private final KakaoService kakaoService;
private final UserService userService;
private final UserRepository userRepository;

Expand All @@ -44,8 +46,7 @@ public LoginResponse socialLogin(String socialAccessToken, String provider) {

// DB에서 회원 찾기
User user = userRepository.findUserByOauthInfo(oauthUserInfo.toEntity())
.orElseGet(() -> userService.createUser(oauthUserInfo)); //DB에 회원이 없으면 회원가입

.orElseGet(() -> userService.createUser(oauthUserInfo)); //DB에 회원이 없으면 회원가입

if (refreshTokenRepository.existsRefreshTokenByMemberId(user.getId())) {
refreshTokenRepository.deleteById(user.getId()); //기존 토큰 삭제
Expand Down Expand Up @@ -104,6 +105,7 @@ public void logout(User user) {
private OauthUserInfoResponse getMemberInfo(String socialAccessToken, OauthProvider provider) {
return switch (provider) {
case GOOGLE -> googleService.getOauthUserInfo(socialAccessToken);
case KAKAO -> kakaoService.getOauthUserInfo(socialAccessToken);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,33 @@ public OauthUserInfoResponse getOauthUserInfo(String accessToken) {
return new OauthUserInfoResponse(response.id(), response.email(), response.name(), GOOGLE);
}

private GoogleOauthUserInfoResponse getGoogleOauthUserInfo(String accessToken){
private GoogleOauthUserInfoResponse getGoogleOauthUserInfo(String accessToken) {
try {
RestClient restClient = RestClient.create();
return restClient.get()
.uri(GOOGLE_USER_INFO_URI)
.header(AUTHORIZATION, TOKEN_PREFIX + accessToken)
.header("Content-type", "application/x-www-form-urlencoded;charset=utf-8")
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
(googleRequest, googleResponse) -> {
throw new BusinessException("Client error: " + googleResponse.getStatusCode(), AUTH_GET_USER_INFO_FAILED);
})
.onStatus(HttpStatusCode::is5xxServerError, (googleRequest, googleResponse) -> {
throw new BusinessException("Server error: " + googleResponse.getStatusCode(), AUTH_GET_USER_INFO_FAILED);
.uri(GOOGLE_USER_INFO_URI)
.header(AUTHORIZATION, TOKEN_PREFIX + accessToken)
.header("Content-type", "application/x-www-form-urlencoded;charset=utf-8")
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
(googleRequest, googleResponse) -> {
throw new BusinessException("Client error: " + googleResponse.getStatusCode(),
AUTH_GET_USER_INFO_FAILED);
})
.body(GoogleOauthUserInfoResponse.class);
} catch (RestClientException e) { // RestClient 관련 에러
.onStatus(HttpStatusCode::is5xxServerError, (googleRequest, googleResponse) -> {
throw new BusinessException("Server error: " + googleResponse.getStatusCode(),
AUTH_GET_USER_INFO_FAILED);
})
.body(GoogleOauthUserInfoResponse.class);
} catch (RestClientException e) { // RestClient 관련 에러
throw new BusinessException("RestClientException: " + e.getMessage(), AUTH_GET_USER_INFO_FAILED);
} catch (Exception e) { // 그 외 일반적인 예외
throw new BusinessException("Unexpected error: " + e.getMessage(), AUTH_GET_USER_INFO_FAILED);
}
}

// 프론트와 연결끝나면 지워도 됨.
public String getToken(String code) {
public String getGoogleToken(String code) {

Map<String, String> params = new LinkedHashMap<>();
params.put("client_id", googleOAuthConfig.getGoogleClientId());
Expand All @@ -79,4 +81,6 @@ public String getToken(String code) {
assert response != null;
return response.accessToken();
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.server.bbo_gak.domain.auth.service.oauth;

import static com.server.bbo_gak.domain.user.entity.OauthProvider.KAKAO;
import static com.server.bbo_gak.global.config.oauth.GoogleOAuthConfig.AUTHORIZATION;
import static com.server.bbo_gak.global.error.exception.ErrorCode.AUTH_GET_USER_INFO_FAILED;

import com.server.bbo_gak.domain.auth.dto.response.oauth.GoogleTokenServiceResponse;
import com.server.bbo_gak.domain.auth.dto.response.oauth.KakaoOAuthUserInfoResponse;
import com.server.bbo_gak.domain.auth.dto.response.oauth.OauthUserInfoResponse;
import com.server.bbo_gak.global.config.oauth.KakaoOAuthConfig;
import com.server.bbo_gak.global.error.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

@Slf4j
@RequiredArgsConstructor
@Service
public class KakaoService implements OauthService {

private final KakaoOAuthConfig kakaoOAuthConfig;

@Override
public OauthUserInfoResponse getOauthUserInfo(String accessToken) {
KakaoOAuthUserInfoResponse response = getKakaoOauthUserInfo(accessToken);
return new OauthUserInfoResponse(response.id().toString(), response.email(), response.nickname(), KAKAO);
}

private KakaoOAuthUserInfoResponse getKakaoOauthUserInfo(String accessToken) {
try {
RestClient restClient = RestClient.create();
return restClient.get()
.uri(KakaoOAuthConfig.KAKAO_USER_INFO_URI)
.header(AUTHORIZATION, KakaoOAuthConfig.TOKEN_PREFIX + accessToken)
.header("Content-type", "application/x-www-form-urlencoded;charset=utf-8")
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
(googleRequest, googleResponse) -> {
throw new BusinessException("Client error: " + googleResponse.getStatusCode(),
AUTH_GET_USER_INFO_FAILED);
})
.onStatus(HttpStatusCode::is5xxServerError, (googleRequest, googleResponse) -> {
throw new BusinessException("Server error: " + googleResponse.getStatusCode(),
AUTH_GET_USER_INFO_FAILED);
})
.body(KakaoOAuthUserInfoResponse.class);
} catch (RestClientException e) { // RestClient 관련 에러
throw new BusinessException("RestClientException: " + e.getMessage(), AUTH_GET_USER_INFO_FAILED);
} catch (Exception e) { // 그 외 일반적인 예외
throw new BusinessException("Unexpected error: " + e.getMessage(), AUTH_GET_USER_INFO_FAILED);
}


}

// 프론트와 연결끝나면 지워도 됨.
public String getKakaoToken(String code) {
RestTemplate restTemplate = new RestTemplate();

MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("client_id", kakaoOAuthConfig.getKakaoClientId());
params.add("client_secret", kakaoOAuthConfig.getKakaoClientSecret());
params.add("code", code);
params.add("grant_type", "authorization_code");
params.add("redirect_uri", kakaoOAuthConfig.getKakaoRedirectUri());

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// HttpEntity 생성
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);

// 요청 URL 설정
String url = KakaoOAuthConfig.KAKAO_TOKEN_URI;

// POST 요청 전송 및 응답 수신
ResponseEntity<GoogleTokenServiceResponse> responseEntity = restTemplate.postForEntity(url, requestEntity,
GoogleTokenServiceResponse.class);

// 응답 검증
if (responseEntity.getStatusCode().is2xxSuccessful()) {
GoogleTokenServiceResponse response = responseEntity.getBody();
assert response != null;
return response.accessToken();
} else {
// 오류 처리 로직 추가
throw new RuntimeException("Failed to retrieve token: " + responseEntity.getStatusCode());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
Expand All @@ -28,6 +29,15 @@ public ResponseEntity<List<TagGetResponse>> getAllTagList(
return ResponseEntity.ok(tagService.getAllTagList(user));
}

@GetMapping("/recruits/{recruit-id}/tags")
public ResponseEntity<List<TagGetResponse>> getAllRecruitsTagList(
@AuthUser User user,
@PathVariable("recruit-id") Long recruitId,
@RequestParam("type") String type
) {
return ResponseEntity.ok(tagService.getAllRecruitsTagList(user, recruitId, type));
}

@GetMapping("/cards/{card-id}/tags")
public ResponseEntity<List<TagGetResponse>> getcCardTagList(
@AuthUser User user,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

public interface TagRepository extends JpaRepository<Tag, Long> {

List<Tag> findAllByJob(Job job);
List<Tag> findAllByJobIsIn(List<Job> jobList);

List<Tag> findAllByIdIsNotIn(List<Long> idList);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
import com.server.bbo_gak.domain.card.entity.Card;
import com.server.bbo_gak.domain.card.entity.CardTag;
import com.server.bbo_gak.domain.card.entity.CardTypeValueGroup;
import com.server.bbo_gak.domain.recruit.entity.Recruit;
import com.server.bbo_gak.global.utils.BaseDateTimeFormatter;
import java.util.List;
import java.util.Optional;
import lombok.AccessLevel;
import lombok.Builder;

@Builder(access = AccessLevel.PRIVATE)
public record CardGetResponse(
String title,
String content,
String createdDate,
String updatedDate,
String recruitTitle,
String cardTypeValueGroup,
List<String> cardTypeValueList,
List<TagGetResponse> tagList
Expand All @@ -28,11 +32,17 @@ public static CardGetResponse of(Card card, List<CardTag> cardTagList) {
.map(cardType -> cardType.getCardTypeValue().getValue())
.toList();

String recruitTitle = Optional.ofNullable(card.getRecruit())
.map(Recruit::getTitle)
.orElse(null);

return CardGetResponse.builder()
.title(card.getTitle())
.content(card.getContent())
.recruitTitle(recruitTitle)
.cardTypeValueGroup(
CardTypeValueGroup.findByCardTypeValue(card.getCardTypeList().getFirst().getCardTypeValue()).getValue())
.createdDate(card.getCreatedDate().format(BaseDateTimeFormatter.getLocalDateTimeFormatter()))
.updatedDate(card.getUpdatedDate().format(BaseDateTimeFormatter.getLocalDateTimeFormatter()))
.cardTypeValueList(cardTypeValueList)
.tagList(tagGetResponseList)
Expand Down
Loading

0 comments on commit bfb5c0c

Please sign in to comment.