Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package com.jootalkpia.auth_server.client.dto;


import com.jootalkpia.auth_server.user.domain.SocialType;
import com.jootalkpia.auth_server.user.domain.Platform;

public record UserInfoResponse(
Long socialId,
SocialType socialType,
Platform platform,
String email,
String socialNickname
) {
public static UserInfoResponse of(
final Long socialId,
final SocialType socialType,
final Platform platform,
final String email,
final String socialNickname
) {
return new UserInfoResponse(socialId, socialType, email, socialNickname);
return new UserInfoResponse(socialId, platform, email, socialNickname);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.jootalkpia.auth_server.client.dto;

import com.jootalkpia.auth_server.user.domain.SocialType;
import com.jootalkpia.auth_server.user.domain.Platform;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record UserLoginRequest(
@NotNull(message = "소셜 로그인 종류가 입력되지 않았습니다.")
@Schema(description = "소셜로그인 타입", example = "KAKAO")
SocialType socialType,
Platform platform,

@NotBlank(message = "redirectUri가 입력되지 않았습니다.")
@Schema(description = "리다이텍트 uri 값", example = "http://localhost:5173/kakao/redirection")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import com.jootalkpia.auth_server.client.service.SocialService;
import com.jootalkpia.auth_server.exception.CustomException;
import com.jootalkpia.auth_server.response.ErrorCode;
import com.jootalkpia.auth_server.user.domain.SocialType;
import com.jootalkpia.auth_server.user.domain.Platform;
import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -41,7 +41,7 @@ public UserInfoResponse login(
throw new CustomException(ErrorCode.AUTHENTICATION_CODE_EXPIRED);
}
// Access Token으로 유저 정보 불러오기
return getLoginDto(loginRequest.socialType(), getUserInfo(accessToken));
return getLoginDto(loginRequest.platform(), getUserInfo(accessToken));
}

private String getOAuth2Authentication(
Expand All @@ -64,12 +64,12 @@ private KakaoUserResponse getUserInfo(
}

private UserInfoResponse getLoginDto(
final SocialType socialType,
final Platform platform,
final KakaoUserResponse userResponse
) {
return UserInfoResponse.of(
userResponse.id(),
socialType,
platform,
userResponse.kakaoAccount().email(),
userResponse.kakaoAccount().profile().nickname()
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.jootalkpia.auth_server.jwt;

import com.jootalkpia.auth_server.exception.CustomException;
import com.jootalkpia.auth_server.redis.Token;
import com.jootalkpia.auth_server.response.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -17,4 +19,12 @@ public void saveRefreshToken(final Long userId, final String refreshToken) {
Token.of(userId, refreshToken)
);
}
}

public Long findIdByRefreshToken(final String refreshToken) {
Token token = tokenRepository.findByRefreshToken(refreshToken)
.orElseThrow(
() -> new CustomException(ErrorCode.REFRESH_TOKEN_NOT_FOUND)
);
return token.getId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public enum ErrorCode {
MISSING_REQUIRED_HEADER("A40002", HttpStatus.BAD_REQUEST, "필수 헤더가 누락되었습니다."),
MISSING_REQUIRED_PARAMETER("A40003", HttpStatus.BAD_REQUEST, "필수 파라미터가 누락되었습니다."),
AUTHENTICATION_CODE_EXPIRED("A40004", HttpStatus.BAD_REQUEST, "인가 코드가 만료되었습니다."),
SOCIAL_TYPE_BAD_REQUEST("A40005", HttpStatus.BAD_REQUEST, "로그인 요청이 유효하지 않습니다."),
PLATFORM_BAD_REQUEST("A40005", HttpStatus.BAD_REQUEST, "로그인 요청이 유효하지 않습니다."),

// 401 Unauthorized
ACCESS_TOKEN_EXPIRED("A40100", HttpStatus.UNAUTHORIZED, "액세스 토큰이 만료되었습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.jootalkpia.auth_server.user.controller;

import com.jootalkpia.auth_server.client.dto.UserLoginRequest;
import com.jootalkpia.auth_server.response.ApiResponseDto;
import com.jootalkpia.auth_server.user.dto.AccessTokenGetSuccess;
import com.jootalkpia.auth_server.user.dto.LoginSuccessResponse;
import com.jootalkpia.auth_server.user.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
Expand All @@ -24,4 +27,12 @@ public ResponseEntity<LoginSuccessResponse> login(
) {
return ResponseEntity.ok().body(userService.create(authorizationCode, loginRequest));
}

@Override
@GetMapping("api/v1/user/token-refresh")
public ResponseEntity<AccessTokenGetSuccess> refreshToken(
@RequestParam final String token
) {
return ResponseEntity.ok().body(userService.refreshToken(token));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.jootalkpia.auth_server.client.dto.UserLoginRequest;
import com.jootalkpia.auth_server.user.dto.AccessTokenGetSuccess;
import com.jootalkpia.auth_server.user.dto.LoginSuccessResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand Down Expand Up @@ -29,4 +30,17 @@ ResponseEntity<LoginSuccessResponse> login(
@RequestParam final String authorizationCode,
@RequestBody final UserLoginRequest loginRequest
);
}

@Operation(summary = "액세스 토큰 재발급")
@ApiResponses(
value = {
@ApiResponse(responseCode = "20001", description = "액세스 토큰 재발급 성공"),
@ApiResponse(responseCode = "40102", description = "리프레시 토큰이 유효하지 않습니다."),
@ApiResponse(responseCode = "40104", description = "해당 유저의 리프레시 토큰이 존재하지 않습니다."),
@ApiResponse(responseCode = "50000", description = "서버 내부 오류입니다.")
}
)
ResponseEntity<AccessTokenGetSuccess> refreshToken(
@RequestParam final String refreshToken
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

@Getter
@AllArgsConstructor
public enum SocialType {
public enum Platform {
KAKAO("KAKAO"),
;
private final String type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,33 @@ public class User extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
private Long userId;

private Long socialId;

private String email;

@Enumerated(EnumType.STRING)
private SocialType socialType;
private Platform platform;

private String nickname;

private String profileImage;


public static User of(
final Long socialId,
final String email,
final SocialType socialType,
final String socialNickname
final Platform platform,
final String socialNickname,
final String profileImage
) {
return User.builder()
.socialId(socialId)
.email(email)
.socialType(socialType)
.platform(platform)
.nickname(socialNickname)
.profileImage(profileImage)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.jootalkpia.auth_server.user.dto;

public record AccessTokenGetSuccess(

String accessToken
) {
public static AccessTokenGetSuccess of(
final String accessToken
) {
return new AccessTokenGetSuccess(accessToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.jootalkpia.auth_server.exception.CustomException;
import com.jootalkpia.auth_server.response.ErrorCode;
import com.jootalkpia.auth_server.user.domain.SocialType;
import com.jootalkpia.auth_server.user.domain.Platform;
import com.jootalkpia.auth_server.user.domain.User;
import feign.Param;
import java.util.Optional;
Expand All @@ -11,15 +11,14 @@

public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {

@Query("SELECT u FROM User u WHERE u.socialId = :socialId AND u.socialType = :socialType")
Optional<User> findUserBySocialTypeAndSocialId(@Param("socialId") Long socialId,
@Param("socialType") SocialType socialType);
@Query("SELECT u FROM User u WHERE u.socialId = :socialId AND u.platform = :platform")
Optional<User> findUserByPlatformAndSocialId(@Param("socialId") Long socialId,
@Param("platform") Platform platform);

Optional<User> findUserById(Long id);
Optional<User> findByUserId(Long userId);

default User findUserByIdOrThrow(Long id) {
return findUserById(id)
default User findByUserIdOrThrow(Long id) {
return findByUserId(id)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
}
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.jootalkpia.auth_server.user.repository;

import com.jootalkpia.auth_server.user.domain.SocialType;
import com.jootalkpia.auth_server.user.domain.Platform;
import com.jootalkpia.auth_server.user.domain.User;
import java.util.Optional;

public interface UserRepositoryCustom {
Optional<User> findUserBySocialTypeAndSocialId(final Long socialId, final SocialType socialType);
Optional<User> findUserByPlatformAndSocialId(final Long socialId, final Platform platform);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.jootalkpia.auth_server.user.service;

import static com.jootalkpia.auth_server.jwt.JwtValidationType.EXPIRED_JWT_TOKEN;

import com.jootalkpia.auth_server.client.dto.UserInfoResponse;
import com.jootalkpia.auth_server.client.dto.UserLoginRequest;
import com.jootalkpia.auth_server.client.kakao.KakaoSocialService;
Expand All @@ -8,8 +10,9 @@
import com.jootalkpia.auth_server.jwt.TokenService;
import com.jootalkpia.auth_server.response.ErrorCode;
import com.jootalkpia.auth_server.security.UserAuthentication;
import com.jootalkpia.auth_server.user.domain.SocialType;
import com.jootalkpia.auth_server.user.domain.Platform;
import com.jootalkpia.auth_server.user.domain.User;
import com.jootalkpia.auth_server.user.dto.AccessTokenGetSuccess;
import com.jootalkpia.auth_server.user.dto.LoginSuccessResponse;
import com.jootalkpia.auth_server.user.dto.TokenDto;
import com.jootalkpia.auth_server.user.repository.UserRepository;
Expand All @@ -32,36 +35,37 @@ public LoginSuccessResponse create(

TokenDto tokenDto = getTokenDto(user);

return LoginSuccessResponse.of(user.getNickname(), user.getId(), tokenDto);
return LoginSuccessResponse.of(user.getNickname(), user.getUserId(), tokenDto);
}

public UserInfoResponse getUserInfoResponse(
final String authorizationCode,
final UserLoginRequest loginRequest
) {
switch (loginRequest.socialType()) {
switch (loginRequest.platform()) {
case KAKAO:
return kakaoSocialService.login(authorizationCode, loginRequest);
default:
throw new CustomException(ErrorCode.SOCIAL_TYPE_BAD_REQUEST);
throw new CustomException(ErrorCode.PLATFORM_BAD_REQUEST);
}
}

public User createUser(final UserInfoResponse userResponse) {
User user = User.of(
userResponse.socialId(),
userResponse.email(),
userResponse.socialType(),
userResponse.socialNickname()+"#"+userResponse.socialId()
userResponse.platform(),
userResponse.socialNickname()+"#"+userResponse.socialId(),
"https://github.com/user-attachments/assets/7e8fc602-6c3e-47bc-a8f4-6a84784a68da"
);
return userRepository.save(user);
}

public User getBySocialId(
final Long socialId,
final SocialType socialType
final Platform platform
) {
User user = userRepository.findUserBySocialTypeAndSocialId(socialId, socialType).orElseThrow(
User user = userRepository.findUserByPlatformAndSocialId(socialId, platform).orElseThrow(
() -> new CustomException(ErrorCode.USER_NOT_FOUND)
);
return user;
Expand All @@ -71,8 +75,7 @@ public TokenDto getTokenByUserId(
final Long id
) {
// 사용자 정보 가져오기
User user = userRepository.findById(id)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
userRepository.findByUserIdOrThrow(id);

UserAuthentication userAuthentication = new UserAuthentication(id.toString(), null);

Expand All @@ -84,24 +87,48 @@ public TokenDto getTokenByUserId(
);
}

public AccessTokenGetSuccess refreshToken(
final String refreshToken
) {
if (jwtTokenProvider.validateToken(refreshToken) == EXPIRED_JWT_TOKEN) {
// 리프레시 토큰이 만료된 경우
throw new CustomException(ErrorCode.REFRESH_TOKEN_EXPIRED);
}

Long userId = jwtTokenProvider.getUserFromJwt(refreshToken);
if (!userId.equals(tokenService.findIdByRefreshToken(refreshToken))) {
throw new CustomException(ErrorCode.TOKEN_INCORRECT_ERROR);
}

// 사용자 정보 가져오기
userRepository.findByUserIdOrThrow(userId);

UserAuthentication userAuthentication = new UserAuthentication(userId.toString(), null);

return AccessTokenGetSuccess.of(
jwtTokenProvider.issueAccessToken(userAuthentication)
);
}

private TokenDto getTokenDto(
final User user
) {
return getTokenByUserId(user.getId());
return getTokenByUserId(user.getUserId());
}

private User getUser(final UserInfoResponse userResponse) {
if (isExistingUser(userResponse.socialId(), userResponse.socialType())) {
return getBySocialId(userResponse.socialId(), userResponse.socialType());
if (isExistingUser(userResponse.socialId(), userResponse.platform())) {
return getBySocialId(userResponse.socialId(), userResponse.platform());
} else {
return createUser(userResponse);
}
}

private boolean isExistingUser(
final Long socialId,
final SocialType socialType
final Platform platform
) {
return userRepository.findUserBySocialTypeAndSocialId(socialId, socialType).isPresent();
return userRepository.findUserByPlatformAndSocialId(socialId, platform).isPresent();
}
}

Loading