Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f5668e8
feat : 커스텀 jwtProvider 구현
jsoonworld Jul 13, 2025
1b33d0e
feat : 타입 일치
jsoonworld Jul 13, 2025
5622913
chore : jwt 예외 클래스 패키지 이동
jsoonworld Jul 13, 2025
c8f6f4a
feat : token dto 구현
jsoonworld Jul 13, 2025
1da8ed9
feat : @AuthenticationPrincipal을 대체할 @Login 어노테이션 및 LoginUserArgument…
jsoonworld Jul 13, 2025
74c4cbb
feat : 비활성화 상태로 추가
jsoonworld Jul 13, 2025
13c57b6
refactor(auth): Optional을 사용하여 LoginCheckInterceptor 인증 로직 개선
jsoonworld Jul 13, 2025
b46596b
feat(auth): LoginUserArgumentResolver에서 원시 타입 long 지원
jsoonworld Jul 13, 2025
b8d8059
refactor(auth): JWT SecretKey 생성 로직 개선 및 책임 일원화
jsoonworld Jul 13, 2025
dcbbc0a
Merge branch 'feat/#278' into feat/#280
jsoonworld Jul 13, 2025
55a59c1
refactor : AuthService 로직 통합 및 리팩토링
jsoonworld Jul 13, 2025
ac8507c
refactor : AuthService 로직 통합 및 리팩토링에 따른 변경
jsoonworld Jul 13, 2025
321eab7
refactor : 누락된 authId 추가
jsoonworld Jul 13, 2025
ec99d47
fix(auth): AuthService의 signUp 로직 순서 오류 수정 및 개선
jsoonworld Jul 13, 2025
8f74931
feat(user): UserSignedUpEvent를 record로 전환하고 fcmToken 추가
jsoonworld Jul 13, 2025
2651e5f
refactor(webhook): UserSignedUpEvent 레코드 변경에 따른 핸들러 수정
jsoonworld Jul 13, 2025
8695ead
chore : 스프링 시큐리티 의존성 제거
jsoonworld Jul 14, 2025
2220283
del : 스프링 시큐리티에 사용된 jwt 관련 로직 제거
jsoonworld Jul 14, 2025
d97c7cd
refactor : 스프링 시큐리티 어노테이션 적욕을 커스텀 어노테이션으로 전환
jsoonworld Jul 14, 2025
8536478
refactor(auth): LoginCheckInterceptor 인터셉터 토큰 이중 처리 로직 개선
jsoonworld Jul 14, 2025
b702053
del : 사용하지 않는 dto 제거
jsoonworld Jul 14, 2025
7fabe93
mrege : 커스텀 JWT Provider 구현
jsoonworld Jul 14, 2025
7b1ace6
merge : Interceptor 및 ArgumentResolver 기반 인증 매커니즘 구현
jsoonworld Jul 14, 2025
7ecbf15
merge : AuthService 로직 통합 및 리팩토링
jsoonworld Jul 14, 2025
169d31b
merge : 스프링 시큐리티 의존성 제거 및 커스텀 인증 전면 적용
jsoonworld Jul 14, 2025
97a1c00
feat: JWT 시크릿 키 Base64 인코딩 방식 복원
jsoonworld Jul 15, 2025
85a5c5a
refactor: JwtProvider를 단일 키 처리 방식으로 단순화
jsoonworld Jul 15, 2025
e0889a3
refactor : 에러 메시지 수정
jsoonworld Jul 15, 2025
e05c6b8
merge : JWT 키 생성 및 검증 로직을 이전 방식으로 롤백
jsoonworld Jul 15, 2025
0a22f01
merge : Spring Security 제거 및 커스텀 인증 체계 도입
jsoonworld Jul 15, 2025
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
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

// Lombok
Expand Down Expand Up @@ -101,4 +100,4 @@ configurations {

tasks.named('test') {
useJUnitPlatform()
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package org.terning.terningserver.auth.api;

import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.terning.terningserver.auth.application.AuthService;
import org.terning.terningserver.auth.config.Login;
import org.terning.terningserver.auth.dto.request.FcmTokenSyncRequest;
import org.terning.terningserver.auth.dto.request.SignInRequest;
import org.terning.terningserver.auth.dto.request.SignUpFilterRequestDto;
import org.terning.terningserver.auth.dto.request.SignUpRequestDto;
import org.terning.terningserver.auth.dto.response.AccessTokenGetResponseDto;
import org.terning.terningserver.auth.dto.request.SignUpFilterRequest;
import org.terning.terningserver.auth.dto.request.SignUpRequest;
import org.terning.terningserver.auth.dto.response.SignInResponse;
import org.terning.terningserver.auth.dto.response.SignUpResponseDto;
import org.terning.terningserver.auth.dto.response.SignUpResponse;
import org.terning.terningserver.auth.dto.response.TokenReissueResponse;
import org.terning.terningserver.common.exception.dto.SuccessResponse;

import static org.terning.terningserver.auth.common.success.AuthSuccessCode.SUCCESS_SIGN_IN;
Expand All @@ -33,48 +32,48 @@ public class AuthController implements AuthSwagger {

@PostMapping("/sign-in")
public ResponseEntity<SuccessResponse<SignInResponse>> signIn(
@RequestHeader("Authorization") String authAccessToken,
@RequestHeader("Authorization") String socialAccessToken,
@RequestBody SignInRequest request
) {
return ResponseEntity.ok(SuccessResponse.of(SUCCESS_SIGN_IN, authService.signIn(authAccessToken, request)));
SignInResponse response = authService.signIn(socialAccessToken, request);
return ResponseEntity.ok(SuccessResponse.of(SUCCESS_SIGN_IN, response));
}

@PostMapping("/token-reissue")
public ResponseEntity<SuccessResponse<AccessTokenGetResponseDto>> reissueToken(
@RequestHeader("Authorization") String refreshToken
public ResponseEntity<SuccessResponse<TokenReissueResponse>> reissueToken(
@RequestHeader("Authorization") String authorizationHeader
) {
val response = authService.reissueToken(refreshToken);

TokenReissueResponse response = authService.reissueAccessToken(authorizationHeader);
return ResponseEntity.ok(SuccessResponse.of(SUCCESS_REISSUE_TOKEN, response));
}

@PostMapping("/sign-up")
public ResponseEntity<SuccessResponse<SignUpResponseDto>> signUp(
public ResponseEntity<SuccessResponse<SignUpResponse>> signUp(
@RequestHeader("Authorization") String authId,
@RequestBody SignUpRequestDto request
@RequestBody SignUpRequest request
) {
SignUpResponseDto signUpResponseDto = authService.signUp(authId, request);
return ResponseEntity.ok(SuccessResponse.of(SUCCESS_SIGN_UP, signUpResponseDto));
SignUpResponse signUpResponse = authService.signUp(authId, request);
return ResponseEntity.ok(SuccessResponse.of(SUCCESS_SIGN_UP, signUpResponse));
}

@PostMapping("/sign-up/filter")
public ResponseEntity<SuccessResponse> registerUserFilter(
@RequestHeader("User-Id") Long userId,
@RequestBody SignUpFilterRequestDto request
@RequestBody SignUpFilterRequest request
) {
authService.registerFilterWithUser(userId, request);
authService.registerUserFilter(userId, request);
return ResponseEntity.ok(SuccessResponse.of(SUCCESS_SIGN_UP_FILTER));
}

@PostMapping("/logout")
public ResponseEntity<SuccessResponse> signOut(@AuthenticationPrincipal Long userId) {
public ResponseEntity<SuccessResponse> signOut(@Login Long userId) {

authService.signOut(userId);

return ResponseEntity.ok(SuccessResponse.of(SUCCESS_SIGN_OUT));
}
@DeleteMapping("/withdraw")
public ResponseEntity<SuccessResponse> withdraw(@AuthenticationPrincipal Long userId) {
public ResponseEntity<SuccessResponse> withdraw(@Login Long userId) {

authService.withdraw(userId);

Expand All @@ -83,7 +82,7 @@ public ResponseEntity<SuccessResponse> withdraw(@AuthenticationPrincipal Long us

@PostMapping("/sync-user")
public ResponseEntity<SuccessResponse> syncUser(
@AuthenticationPrincipal Long userId,
@Login Long userId,
@RequestBody FcmTokenSyncRequest request
) {
authService.syncUser(userId, request);
Expand Down
24 changes: 12 additions & 12 deletions src/main/java/org/terning/terningserver/auth/api/AuthSwagger.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.terning.terningserver.auth.config.Login;
import org.terning.terningserver.auth.dto.request.FcmTokenSyncRequest;
import org.terning.terningserver.auth.dto.request.SignInRequest;
import org.terning.terningserver.auth.dto.request.SignUpFilterRequestDto;
import org.terning.terningserver.auth.dto.request.SignUpRequestDto;
import org.terning.terningserver.auth.dto.response.AccessTokenGetResponseDto;
import org.terning.terningserver.auth.dto.request.SignUpFilterRequest;
import org.terning.terningserver.auth.dto.request.SignUpRequest;
import org.terning.terningserver.auth.dto.response.SignInResponse;
import org.terning.terningserver.auth.dto.response.SignUpResponseDto;
import org.terning.terningserver.auth.dto.response.SignUpResponse;
import org.terning.terningserver.auth.dto.response.TokenReissueResponse;
import org.terning.terningserver.common.exception.dto.SuccessResponse;

@Tag(name = "Auth", description = "소셜 로그인 및 회원가입 API")
Expand All @@ -27,7 +27,7 @@ ResponseEntity<SuccessResponse<SignInResponse>> signIn(
);

@Operation(summary = "토큰 재발급", description = "토큰 재발급 API")
ResponseEntity<SuccessResponse<AccessTokenGetResponseDto>> reissueToken(
ResponseEntity<SuccessResponse<TokenReissueResponse>> reissueToken(
@Parameter(name = "Authorization", description = "", example = "refreshToken")
@RequestHeader("Authorization") String refreshToken
);
Expand All @@ -36,27 +36,27 @@ ResponseEntity<SuccessResponse<AccessTokenGetResponseDto>> reissueToken(
ResponseEntity<SuccessResponse> registerUserFilter(
@Parameter(name = "User-Id", description = "", example = "userId")
@RequestHeader("User-Id") Long userId,
@RequestBody SignUpFilterRequestDto request
@RequestBody SignUpFilterRequest request
);

@Operation(summary = "회원가입", description = "회원가입 API")
ResponseEntity<SuccessResponse<SignUpResponseDto>> signUp(
ResponseEntity<SuccessResponse<SignUpResponse>> signUp(
@Parameter(name = "Authorization", description = "", example = "authId")
@RequestHeader("authId") String authId,
@RequestBody SignUpRequestDto request
@RequestBody SignUpRequest request
);

@Operation(summary = "로그아웃", description = "로그아웃 API")
ResponseEntity<SuccessResponse> signOut(
@AuthenticationPrincipal Long userId);
@Parameter(hidden = true) @Login Long userId);

@Operation(summary = "계정탈퇴", description = "계정탈퇴 API")
ResponseEntity<SuccessResponse> withdraw(
@AuthenticationPrincipal Long userId);
@Parameter(hidden = true) @Login Long userId);

@Operation(summary = "유저동기화", description = "유저동기화 API")
ResponseEntity<SuccessResponse> syncUser(
@AuthenticationPrincipal Long userId,
@Parameter(hidden = true) @Login Long userId,
@RequestBody FcmTokenSyncRequest request
);
}
Original file line number Diff line number Diff line change
@@ -1,69 +1,132 @@
package org.terning.terningserver.auth.application;

import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.terning.terningserver.auth.application.reissue.AuthReissueService;
import org.terning.terningserver.auth.application.signin.AuthSignInService;
import org.terning.terningserver.auth.application.signout.AuthSignOutService;
import org.terning.terningserver.auth.application.signup.AuthSignUpService;
import org.terning.terningserver.auth.application.syncUser.AuthSyncUserService;
import org.terning.terningserver.auth.application.withdraw.AuthWithdrawService;
import org.terning.terningserver.auth.application.social.SocialAuthProvider;
import org.terning.terningserver.auth.application.social.SocialAuthServiceManager;
import org.terning.terningserver.auth.common.exception.AuthErrorCode;
import org.terning.terningserver.auth.common.exception.AuthException;
import org.terning.terningserver.auth.dto.Token;
import org.terning.terningserver.auth.dto.request.FcmTokenSyncRequest;
import org.terning.terningserver.auth.dto.request.SignInRequest;
import org.terning.terningserver.auth.dto.request.SignUpFilterRequestDto;
import org.terning.terningserver.auth.dto.request.SignUpRequestDto;
import org.terning.terningserver.auth.dto.response.AccessTokenGetResponseDto;
import org.terning.terningserver.auth.dto.request.SignUpFilterRequest;
import org.terning.terningserver.auth.dto.request.SignUpRequest;
import org.terning.terningserver.auth.dto.response.SignInResponse;
import org.terning.terningserver.auth.dto.response.SignUpResponseDto;
import org.terning.terningserver.auth.dto.response.SignUpResponse;
import org.terning.terningserver.auth.dto.response.TokenReissueResponse;
import org.terning.terningserver.auth.jwt.JwtProvider;
import org.terning.terningserver.auth.jwt.exception.JwtErrorCode;
import org.terning.terningserver.auth.jwt.exception.JwtException;
import org.terning.terningserver.external.pushNotification.notification.NotificationUserClient;
import org.terning.terningserver.filter.domain.Filter;
import org.terning.terningserver.filter.repository.FilterRepository;
import org.terning.terningserver.user.application.UserService;
import org.terning.terningserver.user.domain.User;
import org.terning.terningserver.user.event.UserSignedUpEvent;
import org.terning.terningserver.user.repository.UserRepository;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class AuthService {

private final AuthSignInService authSignInService;
private final AuthSignUpService authSignUpService;
private final AuthSignOutService authSignOutService;
private final AuthWithdrawService authWithdrawService;
private final AuthReissueService authReissueService;
private final AuthSyncUserService authSyncUserService;
private final UserService userService;
private final UserRepository userRepository;
private final JwtProvider jwtProvider;
private final SocialAuthServiceManager socialAuthServiceManager;
private final ApplicationEventPublisher eventPublisher;
private final NotificationUserClient notificationUserClient;
private final FilterRepository filterRepository;

@Transactional
public SignInResponse signIn(String authAccessToken, SignInRequest request) {
SignInResponse signInResponse = authSignInService.signIn(authAccessToken, request);
return signInResponse;
public SignInResponse signIn(String socialAccessToken, SignInRequest request) {
SocialAuthProvider provider = socialAuthServiceManager.getAuthService(request.authType());
String authId = provider.getAuthId(socialAccessToken);

User user = userRepository.findByAuthIdAndAuthType(authId, request.authType())
.orElse(null);

if (user == null) {
return SignInResponse.ofNewUser(authId, request.authType());
}

Token token = jwtProvider.generateTokens(user.getId());
user.updateRefreshToken(token.refreshToken());
userRepository.save(user);

return SignInResponse.ofExistingUser(token, authId, request.authType(), user.getId());
}

@Transactional
public SignUpResponseDto signUp(String authId, SignUpRequestDto request) {
SignUpResponseDto signUpResponseDto = authSignUpService.signUp(authId, request);
return signUpResponseDto;
public SignUpResponse signUp(String authId, SignUpRequest request) {
if (userRepository.existsByAuthIdAndAuthType(authId, request.authType())) {
throw new AuthException(AuthErrorCode.USER_ALREADY_EXIST);
}

User userToSave = User.from(authId, request);
userRepository.save(userToSave);

Token token = jwtProvider.generateTokens(userToSave.getId());
userToSave.updateRefreshToken(token.refreshToken());

eventPublisher.publishEvent(UserSignedUpEvent.of(userToSave, request.fcmToken()));

notificationUserClient.createUserOnNotificationServer(
userToSave.getId(),
userToSave.getName(),
userToSave.getAuthType(),
request.fcmToken()
);

return SignUpResponse.of(token, userToSave);
}

@Transactional
public void registerFilterWithUser(Long userId, SignUpFilterRequestDto request) {
authSignUpService.registerFilterWithUser(userId, request);
public void registerUserFilter(Long userId, SignUpFilterRequest request) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new AuthException(AuthErrorCode.USER_NOT_FOUND));

Filter newFilter = Filter.from(request);
filterRepository.save(newFilter);

user.assignFilter(newFilter);
}

@Transactional
public void signOut(long userId) {
authSignOutService.signOut(userId);
User user = userRepository.findById(userId).orElseThrow(() -> new AuthException(AuthErrorCode.USER_NOT_FOUND));
user.resetRefreshToken();
}

@Transactional
public void withdraw(long userId) {
authWithdrawService.withdraw(userId);
User user = userRepository.findById(userId).orElseThrow(() -> new AuthException(AuthErrorCode.USER_NOT_FOUND));
userService.deleteUser(user);
}

@Transactional
public AccessTokenGetResponseDto reissueToken(String refreshToken) {
AccessTokenGetResponseDto accessTokenGetResponseDto = authReissueService.reissueToken(refreshToken);
return accessTokenGetResponseDto;
public TokenReissueResponse reissueAccessToken(String authorizationHeader) {
Long userId = jwtProvider.getUserIdFrom(authorizationHeader);

User user = userRepository.findById(userId)
.orElseThrow(() -> new JwtException(JwtErrorCode.INVALID_TOKEN));

String providedToken = jwtProvider.resolveToken(authorizationHeader);
user.validateRefreshToken(providedToken);

Token accessToken = jwtProvider.generateAccessToken(userId);


return new TokenReissueResponse(accessToken.accessToken());
}

@Transactional
public void syncUser(long userId, FcmTokenSyncRequest request) {
authSyncUserService.syncUser(userId, request);
User user = userRepository.findById(userId)
.orElseThrow(() -> new AuthException(AuthErrorCode.USER_NOT_FOUND));

notificationUserClient.createOrUpdateUser(user, request.fcmToken());
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Loading