네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

'2022/07/11'에 해당되는 글 1건


반응형

@AuthenticationPrincipal

 

@GetMapping("/")
public String index(Model model, Principal principal) {

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

로그인한 사용자의 정보를 파라미터로 받아오고 싶을 때, Java 표준 Principal 객체를 받아서 사용한다. Java 표준 Principal 객체는 name 정보만 참조할 수 있다.

 

@AuthenticationPrincipal 어노테이션을 사용하면 UserDetailsService에서 Return한 객체를 파라미터로 직접 받아 사용할 수 있다.

@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 "index";
}

현재 로그인한 사용자의 정보를 참조하고 싶을 때 도메인의 User를 나타내는 객체 (Account)를 직접 사용하고 싶다는 요구사항이 있다면?

 

Adapter 클래스

- UserDetailsService에서 리턴하는 타입을 변경하면, Controller에서 @AuthenticationPrincipal로 받아올 수 있는 객체가 변경된다.

- 이때 사용할 수 있는 방법은 두가지이다.

1. Account 객체를 직접 리턴하기

2. Account 객체를 감싸는 Adapter 클래스를 사용하기

 

Account 객체를 직접 리턴하는 방법은 Account 객체가 UserDetails를 구현해야 한다.

도메인 객체는 특정 기술에 종속되지 않도록 개발하는 것이 좋다.

 

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을 세팅한다.

 

User 클래스를 상속받는 이유는?

- UserDetailsService에서 리턴하는 객체는 UserDetails 타입이어야 한다.

- UserDetails를 구현하는 User 클래스를 상속받는 방식으로 사용한다.

 

@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";
}

accountAdapter.getAccount()로 Account 객체를 참조하지 말고 Account 객체를 직접 사용하도록 하자.

 

Account 객체 직접 사용하기

@AuthenticationPrincipal은 SpEL을 지원한다.

SpEL을 사용해서 Adapter 클래스가 아닌 Account 객체를 직접 가져올 수 있다.

@GetMapping("/")
public String index(Model model, @AuthenticationPrincipal(expression = "#this == 'anonymousUser ? null : account") Account account) {

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

만약 현재 참조중인 객체가 AnonymousAuthenticaionFilter에 의해 생성된 Authentication인 경우 null을 반환하고, 아니라면 AccountAdapter 객체로 간주하고 Account 객체를 반환한다.

 

@CurrentUser

SpEL을 사용해서 직접 Account 객체를 가져오긴 했지만 해당 코드가 너무 길다.

- 커스텀 어노테이션을 생성하여 해결하자

@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 클래스를 사용하자

 

 출처 

https://ncucu.me/137

 

Spring Security - @AuthenticationPrincipal

Spring Security - @AuthenticationPrincipal @AuthenticationPrincipal 로그인한 사용자의 정보를 파라메터로 받고 싶을때 기존에는 다음과 같이 Principal 객체로 받아서 사용한다. 하지만 이 객체는 SecurityCo..

ncucu.me

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !