-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat] 카카오 로그인 초기 세팅 #6
Merged
Changes from 11 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
13751fe
[FEAT] User -> Member로 추가 수정 (#1)
arinming 8db6adb
[FEAT] JWT를 통한 accessToken, refreshToken 생성 로직 추가 (#1)
arinming d248648
[FEAT] KaKao 정보 가져오는 로직 추가 및 카카오 Dto 생성 (#1)
arinming 37b2362
[FEAT] KaKao 정보를 DB에 저장하는 로직 추가 (#1)
arinming e0af9ec
[FEAT] MemberDto 생성 및 OauthService 로직 추가 (#1)
arinming 039096d
[FEAT] Refresh Token 갱신 로직 추가 (#1)
arinming 1fde5de
[FEAT] 토큰 관련 DTO 추가 (#1)
arinming a7d1a51
[FEAT] 카카오 로그인 컨트롤러 구현 (#1)
arinming 9d424b3
[FEAT] 토큰을 쿠키에 저장하는 로직 추가 (#1)
arinming ed76baf
[FEAT] SecurityConfig 추가 (#1)
arinming 591b448
[FEAT] JWT 필터 추가 (#1)
arinming 983494d
Merge remote-tracking branch 'origin/dev' into feature/8
arinming 57fb0e6
[FIX] 빌드 에러 해결 (#1)
arinming File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
src/main/java/com/ttubeog/domain/auth/application/JwtTokenService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package com.ttubeog.domain.auth.application; | ||
|
||
import io.jsonwebtoken.*; | ||
import io.jsonwebtoken.io.Decoders; | ||
import io.jsonwebtoken.io.Encoders; | ||
import io.jsonwebtoken.security.Keys; | ||
import jakarta.servlet.http.Cookie; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.springframework.beans.factory.InitializingBean; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.security.Key; | ||
import java.util.Date; | ||
import java.util.Random; | ||
|
||
@Service | ||
public class JwtTokenService implements InitializingBean { | ||
|
||
private final String secretKey; | ||
private final long accessTokenExpirationInSeconds; | ||
private final long refreshTokenExpirationInSeconds; | ||
private static Key key; | ||
|
||
|
||
public JwtTokenService( | ||
@Value("${jwt.secret-key}") String secretKey, | ||
@Value("${jwt.access-expired-time}") long accessTokenExpirationInSeconds, | ||
@Value("${jwt.refresh-expired-time}") long refreshTokenExpirationInSeconds | ||
) { | ||
this.secretKey = secretKey; | ||
this.accessTokenExpirationInSeconds = accessTokenExpirationInSeconds; | ||
this.refreshTokenExpirationInSeconds = refreshTokenExpirationInSeconds; | ||
} | ||
|
||
@Override | ||
public void afterPropertiesSet() throws Exception { | ||
key = getKeyFromBase64EncodedKey(encodeBase64SecretKey(secretKey)); | ||
} | ||
|
||
// JWT 토큰 생성 | ||
public String createToken(String payload, long expireLength) { | ||
// 토큰에 포함될 정보 | ||
Claims claims = Jwts.claims().setSubject(payload); | ||
// 현재 날짜와 시간 | ||
Date now = new Date(); | ||
// 유효기간 | ||
Date validity = new Date(now.getTime() + expireLength); | ||
|
||
return Jwts.builder() | ||
.setClaims(claims) | ||
.setIssuedAt(now) | ||
.setExpiration(validity) | ||
.signWith(key, SignatureAlgorithm.HS256) // 토큰 서명 | ||
.compact(); | ||
} | ||
|
||
// 액세스 토큰 생성 | ||
public String createAccessToken(String payload) { | ||
return createToken(payload, accessTokenExpirationInSeconds); | ||
} | ||
|
||
// 리프레시 토큰 생성 | ||
public String createRefreshToken() { | ||
byte[] array = new byte[7]; | ||
new Random().nextBytes(array); | ||
String newPayload = new String(array, StandardCharsets.UTF_8); | ||
|
||
return createToken(newPayload, refreshTokenExpirationInSeconds); | ||
} | ||
|
||
public String getPayload(String token) { | ||
try { | ||
return Jwts.parserBuilder() | ||
.setSigningKey(key) | ||
.build() | ||
.parseClaimsJws(token) | ||
.getBody() | ||
.getSubject(); | ||
} catch (ExpiredJwtException e) { | ||
return e.getClaims().getSubject(); | ||
} | ||
} | ||
|
||
public boolean validateToken(String token) { | ||
try { | ||
Jws<Claims> claimsJws = Jwts.parserBuilder() | ||
.setSigningKey(key) | ||
.build() | ||
.parseClaimsJws(token); | ||
return !claimsJws.getBody().getExpiration().before(new Date()); | ||
} catch (JwtException | IllegalArgumentException exception) { | ||
return false; | ||
} | ||
} | ||
|
||
|
||
private String encodeBase64SecretKey(String secretKey) { | ||
return Encoders.BASE64.encode(secretKey.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
private Key getKeyFromBase64EncodedKey(String encodedSecretKey) { | ||
byte[] keyBytes = Decoders.BASE64.decode(encodedSecretKey); | ||
|
||
Key key = Keys.hmacShaKeyFor(keyBytes); | ||
|
||
return key; | ||
} | ||
|
||
// 클라이언트 쿠키에 리프레시 토큰을 저장 | ||
public void addRefreshTokenToCookie(String refreshToken, HttpServletResponse response) { | ||
Long age = refreshTokenExpirationInSeconds; | ||
Cookie cookie = new Cookie("refresh_token", refreshToken); | ||
cookie.setPath("/"); | ||
cookie.setMaxAge(age.intValue()); | ||
cookie.setHttpOnly(true); | ||
response.addCookie(cookie); | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
src/main/java/com/ttubeog/domain/auth/application/KakaoOauthService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.ttubeog.domain.auth.application; | ||
|
||
|
||
import com.ttubeog.domain.auth.dto.KakaoInfoDto; | ||
import com.ttubeog.domain.member.application.MemberService; | ||
import com.ttubeog.domain.member.dto.MemberDto; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.core.ParameterizedTypeReference; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
|
||
import java.util.Map; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class KakaoOauthService { | ||
private final MemberService memberService; | ||
|
||
// 카카오 API를 호출해서 AccessToken으로 멤버 정보를 가져오는 로직 | ||
public Map<String, Object> getMemberInfoByToken(String accessToken) { | ||
return WebClient.create() | ||
.get() | ||
.uri("https://kapi.kakao.com/v2/user/me") | ||
.headers(httpHeaders -> httpHeaders.setBasicAuth(accessToken)) | ||
.retrieve() | ||
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() { | ||
}) | ||
.block(); | ||
} | ||
|
||
|
||
// 카카오 API에서 가져온 멤버 정보를 DB 저장, 업데이트 | ||
public MemberDto getMemberProfileByToken(String accessToken) { | ||
Map<String, Object> memberInfoByToken = getMemberInfoByToken(accessToken); | ||
KakaoInfoDto kakaoInfoDto = new KakaoInfoDto(memberInfoByToken); | ||
MemberDto memberDto = MemberDto.builder() | ||
.id(kakaoInfoDto.getId()) | ||
.email(kakaoInfoDto.getEmail()) | ||
.platform("kakao") | ||
.build(); | ||
|
||
if(memberService.findById(memberDto.getId()) != null) { | ||
memberService.update(memberDto); | ||
} else { | ||
memberService.save(memberDto); | ||
} | ||
return memberDto; | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
src/main/java/com/ttubeog/domain/auth/application/OauthService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.ttubeog.domain.auth.application; | ||
|
||
import com.ttubeog.domain.member.application.MemberService; | ||
import com.ttubeog.domain.member.dto.MemberDto; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class OauthService { | ||
private final MemberService memberService; | ||
private final JwtTokenService jwtTokenService; | ||
private final KakaoOauthService kakaoOauthService; | ||
|
||
// 카카오 로그인 | ||
public String loginWithKakao(String accessToken, HttpServletResponse response) { | ||
MemberDto memberDto = kakaoOauthService.getMemberProfileByToken(accessToken); | ||
return getTokens(memberDto.getId(), response); | ||
} | ||
|
||
// 액세스, 리프레시 토큰 생성 | ||
public String getTokens(Long id, HttpServletResponse response) { | ||
final String accessToken = jwtTokenService.createAccessToken(id.toString()); | ||
final String refreshToken = jwtTokenService.createRefreshToken(); | ||
|
||
MemberDto memberDto = memberService.findById(id); | ||
memberDto.setRefreshToken(refreshToken); | ||
memberService.updateRefreshToken(memberDto); | ||
|
||
return accessToken; | ||
} | ||
|
||
// 리프레시 토큰을 액세스 토큰으로 갱신 | ||
public String refreshToAccessToken(String refreshToken) { | ||
MemberDto memberDto = memberService.findByRefreshToken(refreshToken); | ||
|
||
if(memberDto == null) { | ||
|
||
} | ||
|
||
return jwtTokenService.createAccessToken(memberDto.getId().toString()); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분만 memberOptional이 아니고 userOptional인 건 이유가 있나요?!
궁금해서 질문 남깁니다 ,,!