barded

[어디가게] @AuthenticationPrincipal 및 Custom Annotation을 사용하여 로그인한 사용자 정보 가져오기 본문

프로젝트/어디가게

[어디가게] @AuthenticationPrincipal 및 Custom Annotation을 사용하여 로그인한 사용자 정보 가져오기

barded 2023. 12. 12. 17:25

로그인한 사용자의 정보를 파라미터로 가져오고 싶을때 `Java`의 표준 `Principal` 객체를 받아와 사용할 수 있지만 `Principal` 객체는 `name` 정보만 참조할 수 있다.  이때 `@AuthenticationPrincipal` 애노테이션을 사용하면 `UserDetailService`에서 return하는 객체를 파라미터로 직접 받아 사용할 수 잇다.

 

//ava 표준 Principal 객체를 받아서 사용
@GetMapping("/")
public String index(Model model, Principal principal) {

    if (principal == null) {
        model.addAttribute("message", "Spring security");
    } else {
        model.addAttribute("message", "Hello, " + principal.getName());
    }
    
    return "hello";
}

//@AuthenticationPrincipal 어노테이션을 사용
@GetMapping("/")
public String index(Model model, @AuthenticationPrincipal User user) {

    if (user == null) {
        model.addAttribute("message", "Spring security");
    } else {
        model.addAttribute("message", "Hello, " + user.getName());
    }
    
    return "hello";
}

 

 

그렇다면 현재 로그인한 사용자의 정보를 참조하고 싶을때 도메인을 나타내는 객체(여기서는 `Account`)를 직접 사용하고 싶은경우에는??

 

Adapter 클래스를 사용하기.

`UserDetailService`에서 반환값을 변경하면 `Controller`에서 `@AuthenticationPrincipal`로 받아올 수 있는 객체가 변경된다.

이때 1. `Account` 객체 직접 반환 하는 방식과, 2. `Account`를 객체를 감싸는 `Adapter` 클래스를 사용할 수 있다. 

 

도메인 객체는 특정 기술에 종속되지 않도록 개발하는 것이 좋으므로 `Adapter클래스`를 사용하자

 

AccountAdapter.java

@Getter
public class AccountAdapter extends User {

    private Account account;

    public AccountAdapter(Account account) {
        super(account.getUserId(), account.getPassword(), authorities(account.getRoles()));
        this.account = account;
    }

    private static Collection<? extends GrantedAuthority> authorities(Set<AccountRole> roles) {
        return roles.stream()
                .map(r -> new SimpleGrantedAuthority(r.getRole().authority()))
                .collect(Collectors.toSet());
    }
}
  • User 클래스를 상속받는다.
  • AccountAdapter의 멤버는 오로지 Account 객체만 존재한다.
  • 생성자 내부에서 User 클래스의 생성자를 호출하여 username, password, role을 설정한다.

CustomUserDetailsService.java

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
	Account account = accountRepository.findByUserId(username);
    if (account == null) {
    	throw new UsernameNotFoundException("user not found");
     }
     return new AccountAdapter(account);
}

 

 

AccountAdapter사용하기

@GetMapping("/")
public String index(Model model, @AuthenticationPrincipal AccountAdapter accountAdapter) {
    if (accountAdapter == null) {
        model.addAttribute("message", "Spring security");
    } else {
        model.addAttribute("message", "Hello, " + accountAdapter.getAccount().getUserId());
    }
    return "index";
}

 

@AuthenticationPrincipal은 SpEL을 지원하므로 Account 객체를 직접 가져올 수 있다. 

하지만 너무 기므로 커스텀 애노테이션으로 해결하자!

 

CurrentUser.java

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
public @interface CurrentUser {
}

 

@GetMapping("/")
public String index(Model model, @CurrentUser Account account) {

    if (account == null) {
        model.addAttribute("message", "Spring security");
    } else {
        model.addAttribute("message", "Hello, " + account.getUserId());
    }
    
    return "index";
}

 

 

 

 

  • @AuthenticationPrincipal을 사용하여 UserDetailsService에서 리턴한 객체를 컨트롤러의 파라미터로 직접 참조할 수 있다.
  • 만약 도메인의 User를 표현하는 클래스(Account)를 직접 참조하고 싶다면 Adapter 클래스를 사용하자