-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: OIDC 카카오 로그인 구현, CurrentUser 어노테이션 설정
* Feat: 엔티티 수정(User->Member), 양방향 매핑 삭제 * Fix: JoinColumn 오타 수정 * refactor: 파일 구조 변경 * feat: application-dev.yml 파일 update * feat: spring cloud/openfeign 라이브러리 추가 * fix: errorstatus 이름 변경 * feat: authorization code로 idToken 발급 로직 구현 * feat: yml 내용 추가 * fix: socialtype 수정 * feat: Update securityConfig * feat: _EMPTY_MEMBER -> _EMPTY_USER, 인증관련 에러코드 추가 * feat: Member 엔티티 implements UserDetails(Spring Security), payload 함수 추가 * feat: redis(elasticache 환경 설정) 추가 * feat: update gitignore * feat: RedisController, RedisService 구현 * feat: Kakao OIDC 공개키 캐싱 로직 구현 * feat: CurrentUser 어노테이션 구현 * feat: createdAt, updatedAt 자동 업데이트를 위한 @EnableJpaAuditing 어노테이션 추가 * feat: swagger JWT Bearer 인증버튼 설정 * feat: IdToken 검증 로직 및 로그인/회원가입 로직 구현 * fix: 기존 API CurrentUser 어노테이션으로 수정 * fix: toEntity, updateToken 함수 구현 * fix: findByEmail 구현(회원가입 여부 판별 시 사용) * fix: OIDC표준 OauthOIDCHelper * feat: yml update * fix: 테스트용 코드 제거 * feat: add swagger description * Feat: ApiResponse 적용
- Loading branch information
Showing
45 changed files
with
1,012 additions
and
82 deletions.
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
Submodule config
updated
from 36ae41 to d6baa8
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
30 changes: 30 additions & 0 deletions
30
src/main/java/com/example/tripy/domain/auth/AuthController.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,30 @@ | ||
package com.example.tripy.domain.auth; | ||
|
||
|
||
import com.example.tripy.domain.auth.dto.AuthResponseDto.LoginSimpleInfo; | ||
import com.example.tripy.global.response.ApiResponse; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
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; | ||
|
||
@RequiredArgsConstructor | ||
@RestController | ||
@RequestMapping("/api/auth") | ||
@Tag(name="Auth", description = "인증(로그인, 회원가입) 관련") | ||
public class AuthController { | ||
|
||
private final AuthService authService; | ||
|
||
@PostMapping("/login") | ||
@Operation(summary = "로그인/회원가입 api", description = "code : Authorization code / 회원가입, 로그인 구분 없이 동일한 API 사용") | ||
public ApiResponse<LoginSimpleInfo> getIdToken(@RequestParam String code){ | ||
String idToken = authService.getOauth2Authentication(code); | ||
return ApiResponse.onSuccess(authService.login(idToken)); | ||
} | ||
|
||
|
||
} |
100 changes: 100 additions & 0 deletions
100
src/main/java/com/example/tripy/domain/auth/AuthService.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,100 @@ | ||
package com.example.tripy.domain.auth; | ||
|
||
import com.example.tripy.domain.auth.dto.AuthResponseDto.KakaoAccessTokenResponse; | ||
import com.example.tripy.domain.auth.dto.AuthResponseDto.LoginSimpleInfo; | ||
import com.example.tripy.domain.auth.dto.AuthResponseDto.OIDCDecodePayload; | ||
import com.example.tripy.domain.auth.dto.AuthResponseDto.OIDCPublicKeysResponse; | ||
import com.example.tripy.domain.member.Member; | ||
import com.example.tripy.domain.member.MemberRepository; | ||
import com.example.tripy.domain.member.enums.SocialType; | ||
import com.example.tripy.global.security.JwtTokenProvider; | ||
import java.util.Optional; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.cache.annotation.Cacheable; | ||
import org.springframework.data.redis.core.RedisTemplate; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class AuthService { | ||
private final KakaoAuthApiClient kakaoAuthApiClient; | ||
private final RedisTemplate<String, Object> redisTemplate; | ||
private final KakaoOauthHelper kakaoOauthHelper; | ||
private final MemberRepository memberRepository; | ||
private final JwtTokenProvider jwtTokenProvider; | ||
|
||
|
||
@Value("${security.oauth2.client.kakao.authorization-grant-type}") | ||
private String grantType; | ||
@Value("${security.oauth2.client.kakao.client-id}") | ||
private String clientId; | ||
@Value("${security.oauth2.client.kakao.redirect-uri}") | ||
private String redirectUri; | ||
|
||
|
||
|
||
|
||
public String getOauth2Authentication(final String authorizationCode){ | ||
KakaoAccessTokenResponse tokenInfo = kakaoAuthApiClient.getOAuth2AccessToken( | ||
grantType, | ||
clientId, | ||
redirectUri, | ||
authorizationCode | ||
); | ||
return tokenInfo.idToken(); | ||
} | ||
|
||
@Transactional | ||
public LoginSimpleInfo login(String idToken){ | ||
OIDCDecodePayload oidcDecodePayload = kakaoOauthHelper.getOIDCDecodePayload(idToken); | ||
String email = oidcDecodePayload.email(); | ||
String nickName = oidcDecodePayload.nickName(); | ||
String picture = oidcDecodePayload.picture(); | ||
Optional<Member> optionalMember = memberRepository.findByEmail(email); | ||
|
||
String accessToken = null; | ||
String refreshToken = null; | ||
Member member = null; | ||
|
||
//회원가입 | ||
if(optionalMember.isEmpty()){ | ||
member = Member.toEntity(email, nickName, picture, SocialType.KAKAO); | ||
memberRepository.save(member); | ||
accessToken = jwtTokenProvider.createAccessToken(member.getPayload()); | ||
refreshToken = jwtTokenProvider.createRefreshToken(member.getId()); | ||
} | ||
if(optionalMember.isPresent()){ | ||
accessToken = jwtTokenProvider.createAccessToken(optionalMember.get().getPayload()); | ||
refreshToken = jwtTokenProvider.createRefreshToken(optionalMember.get().getId()); | ||
member = optionalMember.get(); | ||
|
||
} | ||
|
||
member.updateToken(accessToken, refreshToken); | ||
|
||
return LoginSimpleInfo.toDTO(accessToken, refreshToken); | ||
|
||
|
||
} | ||
|
||
@Cacheable(cacheNames = "KakaoOIDC", cacheManager = "oidcCacheManager") | ||
public OIDCPublicKeysResponse getKakaoOIDCOpenKeys() { | ||
return kakaoAuthApiClient.getKakaoOIDCOpenKeys(); | ||
} | ||
|
||
public void updateOpenKeyTestRedis() { | ||
OIDCPublicKeysResponse oidcPublicKeysResponse = getKakaoOIDCOpenKeys(); | ||
saveOIDCPublicKeysResponse(oidcPublicKeysResponse); | ||
} | ||
public void saveOIDCPublicKeysResponse(OIDCPublicKeysResponse response) { | ||
String key = "oidc:public_keys"; | ||
|
||
redisTemplate.opsForValue().set(key, response); | ||
} | ||
|
||
|
||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/com/example/tripy/domain/auth/KakaoAuthApiClient.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,33 @@ | ||
package com.example.tripy.domain.auth; | ||
|
||
import com.example.tripy.domain.auth.dto.AuthResponseDto.KakaoAccessTokenResponse; | ||
import com.example.tripy.domain.auth.dto.AuthResponseDto.OIDCPublicKeysResponse; | ||
import org.springframework.cloud.openfeign.FeignClient; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
|
||
|
||
@FeignClient(name= "kakaoApiClient", url="https://kauth.kakao.com") | ||
public interface KakaoAuthApiClient { | ||
@PostMapping(value = "/oauth/token", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) | ||
KakaoAccessTokenResponse getOAuth2AccessToken( | ||
@RequestParam("grant_type") String grantType, | ||
@RequestParam("client_id") String clientId, | ||
@RequestParam("redirect_uri") String redirectUri, | ||
@RequestParam("code") String code //인가코드 | ||
); | ||
|
||
|
||
//@Cacheable(cacheNames = "KakaoOIDC", cacheManager = "oidcCacheManager") | ||
@GetMapping("/.well-known/jwks.json") | ||
OIDCPublicKeysResponse getKakaoOIDCOpenKeys(); | ||
|
||
|
||
|
||
|
||
|
||
} | ||
|
||
|
30 changes: 30 additions & 0 deletions
30
src/main/java/com/example/tripy/domain/auth/KakaoOauthHelper.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,30 @@ | ||
package com.example.tripy.domain.auth; | ||
|
||
|
||
import com.example.tripy.domain.auth.dto.AuthResponseDto.OIDCDecodePayload; | ||
import com.example.tripy.domain.auth.dto.AuthResponseDto.OIDCPublicKeysResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class KakaoOauthHelper { | ||
private final KakaoAuthApiClient kakaoAuthApiClient; | ||
private final OauthOIDCHelper oauthOIDCHelper; | ||
@Value("${security.oauth2.client.kakao.client-id}") | ||
private String appId; | ||
|
||
@Value("${security.oauth2.client.kakao.iss}") | ||
private String iss; | ||
|
||
public OIDCDecodePayload getOIDCDecodePayload(String token){ | ||
OIDCPublicKeysResponse oidcPublicKeysResponse = kakaoAuthApiClient.getKakaoOIDCOpenKeys(); | ||
return oauthOIDCHelper.getPayloadFromIdToken( | ||
token, iss, appId, oidcPublicKeysResponse | ||
); | ||
} | ||
|
||
|
||
|
||
} |
35 changes: 35 additions & 0 deletions
35
src/main/java/com/example/tripy/domain/auth/OauthOIDCHelper.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,35 @@ | ||
package com.example.tripy.domain.auth; | ||
|
||
import com.example.tripy.domain.auth.dto.AuthResponseDto.OIDCDecodePayload; | ||
import com.example.tripy.domain.auth.dto.AuthResponseDto.OIDCPublicKey; | ||
import com.example.tripy.domain.auth.dto.AuthResponseDto.OIDCPublicKeysResponse; | ||
import com.example.tripy.global.security.JwtOIDCProvider; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class OauthOIDCHelper { | ||
|
||
private final JwtOIDCProvider jwtOIDCProvider; | ||
|
||
//토큰에서 kid 가져온다 -> 가져온 kid는 공개키 결정에 사용 | ||
private String getKidFromUnsignedIdToken(String token, String iss, String aud){ | ||
return jwtOIDCProvider.getKidFromUnsignedTokenHeader(token, iss, aud); | ||
} | ||
|
||
public OIDCDecodePayload getPayloadFromIdToken(String token, String iss, String aud, OIDCPublicKeysResponse oidcPublicKeysResponse){ | ||
String kid = getKidFromUnsignedIdToken(token, iss, aud); | ||
|
||
//같은 Kid인 공개키 불러와서 토큰 검증에 사용 | ||
OIDCPublicKey oidcPublicKey = oidcPublicKeysResponse.keys().stream() | ||
.filter(o-> o.kid().equals(kid)) | ||
.findFirst() | ||
.orElseThrow(); | ||
|
||
//검증 된 토큰에서 바디를 꺼내온다 | ||
return jwtOIDCProvider.getOIDCTokenBody(token, oidcPublicKey.n(), oidcPublicKey.e()); | ||
} | ||
|
||
} |
Oops, something went wrong.