@AuthenticationPrincipal과 getAuthentication()에서 가져온 principal의 다른점은 있을까?
TL; DR
없다 같은것이기 때문에 크게 신경쓰지 말자 타입캐스팅도 자동으로 되기때문에 굳이 서비스 로직이아니라면 Parameter화시키는게 더 편하다.
왜 궁금했나?
@CreatedBy
나 @LastModifiedBy
를 사용하는 경우 Auditor를 구성하기위해 implements AuditorAware<User>
과 같은 Aware를 이용하게되는데, 개발시 스프링에서 제공하는 예시를 보더라도...
class SpringSecurityAuditorAware implements AuditorAware<User> {
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
return ((MyUserDetails) authentication.getPrincipal()).getUser();
}
}
getPrincipal을 빼서 쓰는데 사실 @AuthenticationPrincipal
를 사용하는 방식 자체가 대략 비슷해보이긴 했었다. 그 둘의 차이가 있는지 싶어서 @AuthenticationPrincipal
의 원리를 파악해서 같은건지 아니면 다른 영역의 것인지 알아 보려고 했다.
원리를 파고 들어가보자
일단 어노테이션이기 때문에, 해당하는 어노테이션이 어떤식으로 쓰이는지를 먼저 확인해야한다. → @AuthenticationPrincipal
를 먼저 찾아보자
![[스크린샷 2024-10-25 오후 2.56.17.png]]
를 확인해보면 AuthenticationPrincipalArgumentResolver
를 확인해볼 수 있다. 결국 어노테이션 밖에서 사용하기 위해서는 Controller의 argument를 인식하는 Resolver가 필요하기때문에...
그 해당하는 Resolver를 파악해보자.
해당하는 코드의 105번째줄에 다음과 같은 응답이 있다.
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
//우리가 유의해서 봐야할 부분은 다음이다.
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication == null) {
return null;
}
//그리고 이걸 바로 가져다 쓰는 구조다
Object principal = authentication.getPrincipal();
AuthenticationPrincipal annotation = findMethodAnnotation(AuthenticationPrincipal.class, parameter);
String expressionToParse = annotation.expression();
if (StringUtils.hasLength(expressionToParse)) {
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(principal);
context.setVariable("this", principal);
context.setBeanResolver(this.beanResolver);
Expression expression = this.parser.parseExpression(expressionToParse);
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());
}
return null;
}
return principal;
}
코드에서도 보이지만, 실제로 getAuthentication()
하는 부분과 완전히 동일하다. 혹시 다른부분이 있을까 했지만 딱히 그런 부분은 보이지 않았다. 물론, 이 아규먼트에서도 재밌는 부분은 있었다. 주석에 예시로 달려있는 부분들은 꽤 코드짤때 도움이 되보였다.
@AuthenticationPrincipal
를 커스터마이징시 어노테이션 자체에 붙여서 써버리면 CurrentUser 아님을 확인하는 과정이 없어도 문제는 없어보였다
다음처럼 사용하면
@Target({ ElementType. PARAMETER })
@Retention(RetentionPolicy. RUNTIME)
@AuthenticationPrincipal
public @interface CurrentUser {
}
/// (@CurrentUser CustomUser customUser) 이렇게 표현하기 좋다
이런형태로 쓰기 좋기 때문에 결론적으로는 해당하는 어노테이션을 만들어도 좋을거 같았다.
결론
완전 동일함을 확인했고, Holder를 뽑아서 사용하는 케이스가 아니라면 파라미터로 전달받아서 사용하는 게 좋아 보였다.
출처
- https://wildeveloperetrain.tistory.com/324 → 쓰려는 내용보다 더 깊게 들어갔다. 더 자세한 원리를 파고 싶다면 확인해보자
- https://codevang.tistory.com/273
'Spring' 카테고리의 다른 글
에러에 상태에 따라서 로깅 레벨을 조절해보자 (0) | 2024.11.10 |
---|---|
Transactional의 Self Injection이 올바른가 (0) | 2024.06.22 |
@JsonInclude란? (0) | 2024.02.04 |
부모 - 자식 관계에 있는 DTO를 효과적으로 표현하는 JsonTypeInfo Deduction기능을 알아보자. (1) | 2024.01.21 |
테스트 코드를 짜고는 싶은데, 테스트 실패시 빌드 실패가 걱정된다면? (0) | 2023.09.02 |