CurrentUserDtoArgumentResolver
를 구현하여 Entity -> DTO 로 변환AuthenticationPrincipalArgumentResolver
를 통해 Principal 의 subclass 을 주입한다.supportsParameter
AuthenticationPrincipal
타입 을 찾는다.public final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
// do something...
public boolean supportsParameter(MethodParameter parameter) {
return this.findMethodAnnotation(AuthenticationPrincipal.class, parameter) != null;
}
private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
T annotation = parameter.getParameterAnnotation(annotationClass);
if (annotation != null) {
return annotation;
} else {
Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
Annotation[] var5 = annotationsToSearch;
int var6 = annotationsToSearch.length;
for(int var7 = 0; var7 < var6; ++var7) {
Annotation toSearch = var5[var7];
annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass); // 여기!
if (annotation != null) {
return annotation;
}
}
return null;
}
}
resolveArgument
1. 해당 메서드의 파라미터에서 @AuthenticationPrincipal
annotation 이 있는지를 탐색한다.
2. StandardEvaluationContext 클래스는 해당 표현식(e.g. spEL) 을 평가할 개체를 지정한다. (by. reflection)
3. Expression 객체는 ExpressionParser 을 통해 표현식 문자열 구문 분석을 한 내용과 StandardEvaluationContext 에 평가할 개체를 토대로 원하는 객체를 호출한다.
public final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
// do something...
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
} else {
Object principal = authentication.getPrincipal();
AuthenticationPrincipal annotation = (AuthenticationPrincipal)this.findMethodAnnotation(AuthenticationPrincipal.class, parameter); //(1)
String expressionToParse = annotation.expression();
if (StringUtils.hasLength(expressionToParse)) {
StandardEvaluationContext context = new StandardEvaluationContext(); // (2)
context.setRootObject(principal);
context.setVariable("this", principal);
context.setBeanResolver(this.beanResolver);
Expression expression = this.parser.parseExpression(expressionToParse); // (3)
principal = expression.getValue(context);
}
if (principal != null && !ClassUtils.isAssignable(parameter.getParameterType(), principal.getClass())) {
if (annotation.errorOnInvalidType()) {
throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType());
} else {
return null;
}
} else {
return principal;
}
}
}
}
@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@AuthenticationPrincipal
public @interface CurrentUser {
}
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/mypages")
public class MyPageController {
private final MyPageQueryFacade myPageQueryFacade;
private final MyPageCommandFacade myPageCommandFacade;
@GetMapping("/me")
public ResponseEntity<BaseResponse<UserLookUpResponse>> searchMe(@CurrentUser CurrentUserDto currentUserDto) {
UserLookUpResponse userLookUpResponse = myPageQueryFacade.findUserWithDetailInfo(currentUserDto.id());
return ResponseEntity.ok(new BaseResponse<>(USER_LOOK_UP_SUCCESS, userLookUpResponse));
}
}
CurrenUserDtoArgumentResolver
를 작업하여 반영하여 A 등급으로 판정 변경코드 변경 이전 등급 판정
CurrentUserDtoArgumentResolver
public class CurrentUserDtoArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return findMethodAnnotation(CurrentUser.class, parameter) != null;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // LoginAuthenticationToken 타입 authentication
if (authentication == null) {
return null;
}
CustomUser customUser = (CustomUser) authentication.getPrincipal(); // principal을 CustomUser타입으로 변환
return new CurrentUserDto(customUser.getId(), customUser.getEmail()); // CustomUserDto 타입으로 반환
}
private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
T annotation = parameter.getParameterAnnotation(annotationClass);
if (annotation != null) {
return annotation;
}
Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
for (Annotation toSearch : annotationsToSearch) {
annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
if (annotation != null) {
return annotation;
}
}
return null;
}
}
코드 변경 등급 판정
@WithMockUser
는 Authentication 의 Pricipal 등록타입이 User 이다.MockCustomUserSecurityContextFactory
를 선언하고 @WithSecurityContext
를 할당@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = MockCustomUserSecurityContextFactory.class)
public @interface MockCustomUser {
String value() default "user";
String username() default "";
String[] roles() default {"USER"};
String password() default "password";
}
public class MockCustomUserSecurityContextFactory implements WithSecurityContextFactory<MockCustomUser> {
@Override
public SecurityContext createSecurityContext(MockCustomUser annotation) {
String username = StringUtils.hasLength(annotation.username()) ? annotation.username() : annotation.value();
Assert.notNull(username, () -> annotation + " cannot have null username on both username and value properties");
List<GrantedAuthority> authorities = settingRole(annotation);
CustomUser customUser = new CustomUser(username, annotation.password());
Authentication authentication = UsernamePasswordAuthenticationToken.authenticated(customUser, "", authorities);
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authentication);
return securityContext;
}
private List<GrantedAuthority> settingRole(MockCustomUser annotation) {
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (String role : annotation.roles()) {
Assert.isTrue(!role.startsWith("ROLE_"), () -> "roles cannot start with ROLE_ Got " + role);
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role));
}
return grantedAuthorities;
}
}