@AuthenticationPrincipal 어노테이션을 사용하면 JWT 토큰 정보에 필터링 된 유저정보를 Controller 에서 간단히 가져올 수 있습니다.
이번 포스팅에서 변경해볼 사항은 SecurityContextHolder (이전) -> @AuthenticationPrincipal(변경) 해보겠습니다.
Spring Security 에서는 인증된 사용자 정보를 SecurityContextHolder 내부의 SecurityContext 에 Authentication 객체로 저장을 하고 이후 필요할 때 꺼내어 사용합니다.
SecurityContextHolder 에 저장된 회원을 꺼내어 사용하는 방법을 알아보겠습니다.
먼저 해당 어노테이션을 몰랐을 때, 사용했던 방법은 아래와 같은 방법을 통해 authentication 을 가져와 getName() 메서드를 사용하는 것이었습니다.
getName() 으로 String name 을 가져와서 memberRepository에서 회원을 찾는 방법입니다.
private Member getMember() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return memberRepository.findByAccountId(authentication.getName()).orElseThrow(
() -> new BusinessException(NOT_FOUND_MEMBER));
}
위와 같은 로직으로 작성했을 경우, 많은 Service 클래스에 중복되는 코드가 발생한다는 점이 있습니다.
가장 큰 단점으로는 authentication 에서 사용할 수 있는 메서드가 getName() 만 있는 것입니다.
이유는 SecurityContextHolder 의 Principal 객체는 자바에 정의되어있는 Principal 객체를 바인딩하기 때문입니다.
@AuthenticationPrincipal 어노테이션을 사용해서 변경해보겠습니다.
예시는 게시글 작성API 입니다.
변경 전 Controller
// 게시글 작성
@PostMapping("/boards")
@PreAuthorize("hasAnyRole('REALTOR','LESSOR','LESSEE')")
@ResponseStatus(HttpStatus.CREATED)
public void boardWrite(@RequestBody BoardRequestDto boardRequestDto) {
boardService.save(boardRequestDto);
}
변경 후 Controller
// 게시글 작성
@PostMapping("/boards")
@PreAuthorize("hasAnyRole('REALTOR','LESSOR','LESSEE')")
@ResponseStatus(HttpStatus.CREATED)
public void boardWrite(@RequestBody BoardRequestDto boardRequestDto, @AuthenticationPrincipal User user) {
boardService.save(boardRequestDto, user);
}
변경 전 Service
// 게시글 작성
@Override
public void save(BoardRequestDto boardRequestDto) {
Member member = getMember();
Board board = new Board(member, boardRequestDto);
boardRepository.save(board);
}
private Member getMember() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
authentication.getName();
return memberRepository.findByAccountId(authentication.getName()).orElseThrow(
() -> new BusinessException(NOT_FOUND_MEMBER));
}
변경 후 Service
// 게시글 작성
@Override
public void save(BoardRequestDto boardRequestDto, User user) {
Member member = memberRepository.findByAccountId(user.getUsername())
.orElseThrow(() -> new BusinessException(NOT_FOUND_MEMBER));
Board board = new Board(member, boardRequestDto);
boardRepository.save(board);
}
변경 전 과 후의 차이는 코드량의 차이도 있지만, user 객체의 get 메서드를 통해 여러 정보를 가져와 활용할 수 있다는 점이라고 생각합니다.
+ @AuthenticationPrincipal(expression = "name") 을 사용할 수 있습니다.
name 에는 SpEL(스프링 표현언어) 를 사용합니다.
사용 방법은 아래 블로그에서 확인해 응용할 수 있습니다.
http://www.chidoo.me/index.php/2020/08/31/web-bearer-authorization-with-jwt-and-spring-security/
'Spring > Spring-Security' 카테고리의 다른 글
SpringSecurity Enum 타입으로 권한 설정하기 (0) | 2023.02.07 |
---|---|
Spring Security + JWT 로그인 구현하기 (Access Token) (0) | 2023.01.23 |
SpringSecurity 와 JWT 의 구조, 동작 과정 (0) | 2022.10.23 |
SpringSecurity DB 없이 테스트 방법 (0) | 2022.10.20 |
JWT (Json Web Token) 의 개념 (0) | 2022.10.19 |
댓글