-
Notifications
You must be signed in to change notification settings - Fork 1
[FEATURE] 액세스 토큰 클레임 추가 (역할 아이디) #309
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
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
5f6f333
feat: accessToken claims 추가를 위한 레코드
m-a-king b7e5b22
feat(user): userId 기준 roleId 조회 기능 추가
m-a-king d26166e
feat(userIdentity): jwt accessToken에 roleId 추가를 위한 수정
m-a-king 6c8af34
test(jwt): 프로덕션 코드 수정에 따라서 테스트 코드 수정
m-a-king b1ab039
test: 유저 아이디 기준 봉사자, 기관 조회 테스트 추가
m-a-king e680a0f
feat(annotation): userId, roleId 추출 어노테이션 추가
m-a-king b4d2799
feat(annotation): userId, roleId 추출 예외 및 핸들링 기능 추가
m-a-king 3b0019f
feat(annotation): config 등록
m-a-king fc70c19
refactor: 개행 문제 해결
m-a-king acd8ca3
feat(userIdentity): 직렬화 가능하도록 수정
m-a-king feb62fd
refactor: 불필요한 파라미터 제거
m-a-king 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
31 changes: 31 additions & 0 deletions
31
src/main/java/com/somemore/center/service/NEWCenterQueryService.java
This file contains hidden or 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,31 @@ | ||
| package com.somemore.center.service; | ||
|
|
||
| import com.somemore.center.domain.NEWCenter; | ||
| import com.somemore.center.repository.NEWCenterRepository; | ||
| import com.somemore.center.usecase.NEWCenterQueryUseCase; | ||
| import com.somemore.global.exception.ExceptionMessage; | ||
| import com.somemore.global.exception.NoSuchElementException; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| @Transactional(readOnly = true) | ||
| public class NEWCenterQueryService implements NEWCenterQueryUseCase { | ||
|
|
||
| private final NEWCenterRepository centerRepository; | ||
|
|
||
| @Override | ||
| public NEWCenter getByUserId(UUID userId) { | ||
| return centerRepository.findByUserId(userId) | ||
| .orElseThrow(() -> new NoSuchElementException(ExceptionMessage.NOT_EXISTS_CENTER)); | ||
| } | ||
|
|
||
| @Override | ||
| public UUID getIdByUserId(UUID userId) { | ||
| return getByUserId(userId).getId(); | ||
| } | ||
| } |
12 changes: 12 additions & 0 deletions
12
src/main/java/com/somemore/center/usecase/NEWCenterQueryUseCase.java
This file contains hidden or 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,12 @@ | ||
| package com.somemore.center.usecase; | ||
|
|
||
| import com.somemore.center.domain.NEWCenter; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| public interface NEWCenterQueryUseCase { | ||
|
|
||
| NEWCenter getByUserId(UUID userId); | ||
|
|
||
| UUID getIdByUserId(UUID userId); | ||
| } |
11 changes: 11 additions & 0 deletions
11
src/main/java/com/somemore/global/auth/annotation/RoleId.java
This file contains hidden or 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,11 @@ | ||
| package com.somemore.global.auth.annotation; | ||
|
|
||
| import java.lang.annotation.ElementType; | ||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
| import java.lang.annotation.Target; | ||
|
|
||
| @Retention(RetentionPolicy.RUNTIME) | ||
| @Target(ElementType.PARAMETER) | ||
| public @interface RoleId { | ||
| } |
40 changes: 40 additions & 0 deletions
40
src/main/java/com/somemore/global/auth/annotation/RoleIdArgumentResolver.java
This file contains hidden or 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,40 @@ | ||
| package com.somemore.global.auth.annotation; | ||
|
|
||
| import com.somemore.global.auth.authentication.UserIdentity; | ||
| import com.somemore.global.exception.InvalidAuthenticationException; | ||
| import org.springframework.core.MethodParameter; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.bind.support.WebDataBinderFactory; | ||
| import org.springframework.web.context.request.NativeWebRequest; | ||
| import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
| import org.springframework.web.method.support.ModelAndViewContainer; | ||
|
|
||
| import static com.somemore.global.exception.ExceptionMessage.AUTHENTICATION_MISSING; | ||
| import static com.somemore.global.exception.ExceptionMessage.INVALID_PRINCIPAL_TYPE; | ||
|
|
||
| @Component | ||
| public class RoleIdArgumentResolver implements HandlerMethodArgumentResolver { | ||
|
|
||
| @Override | ||
| public boolean supportsParameter(MethodParameter parameter) { | ||
| return parameter.hasParameterAnnotation(RoleId.class); | ||
| } | ||
|
|
||
| @Override | ||
| public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, | ||
| NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { | ||
| Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
|
|
||
| if (authentication == null) { | ||
| throw new InvalidAuthenticationException(AUTHENTICATION_MISSING); | ||
| } | ||
|
|
||
| if (authentication.getPrincipal() instanceof UserIdentity principal) { | ||
| return principal.roleId(); | ||
| } | ||
|
|
||
| throw new InvalidAuthenticationException(INVALID_PRINCIPAL_TYPE); | ||
| } | ||
| } | ||
11 changes: 11 additions & 0 deletions
11
src/main/java/com/somemore/global/auth/annotation/UserId.java
This file contains hidden or 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,11 @@ | ||
| package com.somemore.global.auth.annotation; | ||
|
|
||
| import java.lang.annotation.ElementType; | ||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
| import java.lang.annotation.Target; | ||
|
|
||
| @Retention(RetentionPolicy.RUNTIME) | ||
| @Target(ElementType.PARAMETER) | ||
| public @interface UserId { | ||
| } |
40 changes: 40 additions & 0 deletions
40
src/main/java/com/somemore/global/auth/annotation/UserIdArgumentResolver.java
This file contains hidden or 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,40 @@ | ||
| package com.somemore.global.auth.annotation; | ||
|
|
||
| import com.somemore.global.auth.authentication.UserIdentity; | ||
| import com.somemore.global.exception.InvalidAuthenticationException; | ||
| import org.springframework.core.MethodParameter; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.bind.support.WebDataBinderFactory; | ||
| import org.springframework.web.context.request.NativeWebRequest; | ||
| import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
| import org.springframework.web.method.support.ModelAndViewContainer; | ||
|
|
||
| import static com.somemore.global.exception.ExceptionMessage.AUTHENTICATION_MISSING; | ||
| import static com.somemore.global.exception.ExceptionMessage.INVALID_PRINCIPAL_TYPE; | ||
|
|
||
| @Component | ||
| public class UserIdArgumentResolver implements HandlerMethodArgumentResolver { | ||
|
|
||
| @Override | ||
| public boolean supportsParameter(MethodParameter parameter) { | ||
| return parameter.hasParameterAnnotation(UserId.class); | ||
| } | ||
|
|
||
| @Override | ||
| public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, | ||
| NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { | ||
| Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
|
|
||
| if (authentication == null) { | ||
| throw new InvalidAuthenticationException(AUTHENTICATION_MISSING); | ||
| } | ||
|
|
||
| if (authentication.getPrincipal() instanceof UserIdentity principal) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분도 RoleId 에서 리뷰 남긴 부분이라 코멘트 달아두겠습니다! |
||
| return principal.userId(); | ||
| } | ||
|
|
||
| return new InvalidAuthenticationException(INVALID_PRINCIPAL_TYPE); | ||
| } | ||
| } | ||
This file contains hidden or 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
27 changes: 27 additions & 0 deletions
27
src/main/java/com/somemore/global/auth/authentication/UserIdentity.java
This file contains hidden or 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,27 @@ | ||
| package com.somemore.global.auth.authentication; | ||
|
|
||
| import com.somemore.user.domain.UserRole; | ||
| import io.jsonwebtoken.Claims; | ||
|
|
||
| import java.io.Serializable; | ||
| import java.util.UUID; | ||
|
|
||
| public record UserIdentity( | ||
| UUID userId, | ||
| UUID roleId, | ||
| UserRole role | ||
| ) implements Serializable { | ||
|
|
||
| public static UserIdentity of(UUID userId, UUID roleId, UserRole role) { | ||
| return new UserIdentity(userId, roleId, role); | ||
| } | ||
|
|
||
| public static UserIdentity from(Claims claims) { | ||
| UUID userId = UUID.fromString(claims.get("userId", String.class)); | ||
| UUID roleId = UUID.fromString(claims.get("roleId", String.class)); | ||
| UserRole role = UserRole.from( | ||
| claims.get("role", String.class)); | ||
|
|
||
| return new UserIdentity(userId, roleId, role); | ||
| } | ||
| } |
This file contains hidden or 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 hidden or 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 hidden or 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
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.
authentication == null를 통과하면
authentication.getPrincipal()도 통과가 가능한건지 여쭤보고 싶습니다
Principal이 비어 있는 경우는 없겠죠?
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.
authentication == null를 통과하면, authentication.getPrincipal()은 NPE 없이 호출 가능합니다. 또한, principal이 null이거나 비어 있는 값으로 설정될 가능성은 없습니다.
authentication은 Spring Security의 인증 과정에서 생성되며, principal은 생성자에서 초기화됩니다.
인증이 성공하면 authentication은 항상 principal, credentials, authorities를 포함한 상태로 SecurityContext에 저장됩니다.
따라서 authentication이 null이 아닌 경우, principal 또한 초기화된 상태입니다.
null instanceof UserIdentity
authentication.getPrincipal()이 null이라면, instanceof 연산에서 항상 false를 반환하므로, 안전하게 처리됩니다.
Spring Security는 필터 체인에서 인증 객체를 생성한 후 SecurityContext에 저장합니다.
컨트롤러 레벨에서 접근하는 경우, 이미 인증 객체가 완전히 구성된 상태입니다.
만약 authentication이 제대로 초기화되지 않았다면, 컨트롤러까지 요청이 도달하지 못합니다.
사실 상 로직에서는 NPE 방어를 진행했고, 인증 객체가 올바르게 구성되지 않는 상황이나 익명 유저(토큰X)는
@Secured로 필터에서 처리되어 예외가 발생할 것 같습니다~