Custom AuthenticationPrincipal를 사용한 인증

김건우·2023년 1월 30일
0

Spring Security

목록 보기
2/4
post-thumbnail

@AuthenticationPrincipal

  • 핸들러 매개변수로 현재 인증된 Principal을 참조할 수 있다.
  • Principal 은 인증 시 authentication에 들어있는 첫 번째 파라미터이다. 아래의 account.getNickname()에 해당.
  • 현재 아래의 메서드에서는 login시 NickName을 Principal로 넣어주고 있다.
public void login(Account account) {
    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( // 토큰 생성
            account.getNickname(),
            account.getPassword(),
            List.of(new SimpleGrantedAuthority("ROLE_USER")));
    // 로그인 처리
    SecurityContextHolder.getContext().setAuthentication(token);
}
  • @AuthenticationPrincipal은 SpEL을 사용해서 Principal 내부 정보에 접근할 수도 있다.
  • @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
    익명 인증인 경우에는 null로 설정하고, 아닌 경우에는 account 프로퍼티를 조회해서 설정하라는 뜻.
  • @AuthenticationPrincipal를 사용하면 UserDetailsService에서 return한 객체를 파라미터로 직접 받아 사용할 수 있다.

하지만 위의 에노테이션을 매개변수에 직접 쓴 다면 2가지의 문제점이있다.
하나는 직접 SqEl을 직접 모든 매개변수에 다 넣어 주어야 하는것이고, 또 하나는 PrinciPal에 nickName만 넣어야하는데 @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")와 같이 매번 파라미터에 붙이기는 번거롭기 때문에 @CurrentUser라는 커스텀 어노테이션을 생성할 것 이다.

커스텀 어노테이션 생성 @CurrentUser

// 런타임 까지 유지
@Retention(RetentionPolicy.RUNTIME)
// 타겟은 파라미터에만 붙이겠다.
@Target(ElementType.PARAMETER)
// 익명 사용자인 경우에는 null로, 익명 사용자가 아닌 경우에는 실제 account 객체로
// Principal 을 다이나믹 하게 꺼내기 위해 @CurrentUser 생성
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
public @interface CurrentUser {
}

@CurrentUser를 사용한 MainController

@Controller
public class MainController {
    @GetMapping("/")
    // 익명 사용자인 경우에는 null 로, 익명 사용자가 아닌 경우에는 실제 account 객체로
    // Principal 을 다이나믹 하게 꺼내기 위해 @CurrentUser 생성
    public String home(@CurrentUser Account account, Model model){
        if(account != null){ // null 이 아니면 인증을 한 사용자
            model.addAttribute(account);
        }
        return "index";
      }
}
  • 익명 사용자인 경우에는 null을, 익명 사용자가 아닌 경우에는 실제 account 객체를 가져온다.
  • 하지만 지금 로그인 할 때 사용한 Principal에는 Account라는 프로퍼티가 없다.
  • 따라서 Account라는 프로퍼티를 가지고 있는 중간 역할을 해줄 수 있는 객체가 필요하다.
  • 핸들러에서 현재 로그인한 유저의 정보가 필요할 때 위와 같이 사용할 수 있다.

UserAccount

스프링 시큐리티가 다루는 유저 정보와 우리의 도메인에서 다루는 유저 정보 사이의 갭을 매꿔주는 일종의 어댑터 역할

@Getter
public class UserAccount extends User { // 스프링 시큐리티의 User를 상속

    private Account account;
    // 스프링 시큐리티가 다루는 유저 정보를 우리가 가지고 있는 도메인의 유저 정보와 연동
    public UserAccount(Account account) {
        super(account.getNickname(), account.getPassword(), List.of(new SimpleGrantedAuthority("ROLE_USER")));
        this.account = account;
    }
}
  • 스프링 시큐리티의 User를 상속받는다.
  • UserAccount의 멤버는 Account 객체만이 존재한다.
  • 생성자의 내부에서 User 클래스의 생성자를 호출하여 username, password, role을 세팅한다.

login (AccountService)

AccountService.java 의 Principal에 위에서 생성한 AccountUser 객체를 넣는다.

  • 아래의 login service를 보면 Principal에 UserAccount를 넣어준다.🔽
public void login(Account account) {

        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( // 토큰 생성
                new UserAccount(account), // Principal 객체설정
                account.getPassword(),
                List.of(new SimpleGrantedAuthority("ROLE_USER")));
        // 로그인 처리
        SecurityContextHolder.getContext().setAuthentication(token);
    }

그럼 이제 로그인 단계에서 principal에 UserAccount를 넣어줘서 매개변수에 @CurrentUser를 넣어주면 해당 principal 정보를 꺼낼 수 있다.

profile
Live the moment for the moment.

0개의 댓글