스프링과 JPA 기반 웹 애플리케이션 개발 #21 현재 인증된 사용자 정보 참조
해당 내용은 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발의 강의 내용을 바탕으로 작성된 내용입니다.
강의를 학습하며 요약한 내용을 출처를 표기하고 블로깅 또는 문서로 공개하는 것을 허용합니다 라는 원칙 하에 요약 내용을 공개합니다. 출처는 위에 언급되어있듯, 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발입니다.
제가 학습한 소스코드는 https://github.com/n00nietzsche/jakestudy_webapp 에 지속적으로 업로드 됩니다. 매 커밋 메세지에 강의의 어디 부분까지 진행됐는지 기록해놓겠습니다.
@AuthenticationPrincipal
Principal
을 참조할 수 있다.Principal
을 어디에 넣었더라?public void login(Account account) {
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(
account.getNickname(),
account.getPassword(),
List.of(new SimpleGrantedAuthority("ROLE_USER"))
);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(token);
}
@AuthenticationPrincipal
은 SpEL
을 사용해서 Principal
내부 정보에 접근할 수 있다.@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
null
로 설정하고, 아닌 경우에는 account
프로퍼티를 조회해서 설정하라는 뜻
Principal
는 접근 주체를 말한다.
package com.jakestudy.account;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // RUNTIME까지 유지되는 애노테이션
@Target(ElementType.PARAMETER) // 파라미터 타입으로 들어가는 애노테이션
// 인증정보가 없을 때는 스프링 시큐리티에서 자동으로 `anonymousUser` 라는 문자열을 principal 에 설정하는 특성을 이용했다.
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
public @interface CurrentUser {
}
위와 같이 작성하면 된다. @CurrentUser
인터페이스를 작성하는 이유는 매번 @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : account")
와 같이 애노테이션을 넣어주어도 되지만, 상당히 귀찮고 코드의 가독성이 매우 저해되기 때문이다.
그래서 커스텀한 애노테이션을 만들어서 쉽게 현재 사용자를 가져올 수 있도록 만들었다. #this == 'anonymousUser'
라는 코드가 나온 이유는 스프링 시큐리티에서 인증되지 않은 사용자에게 기본으로 'anonymousUser'
라는 principal
을 제공하기 때문이다.
위는 디버그 중 SecurityContextHolder.getContext().getAuthentication()
메소드의 결과를 추적한 것인데, principal
에 "anonymousUser"
라는 값이 들어있는 것을 볼 수 있다.
@Getter
// 스프링 시큐리티의 userdetail에서 user를 가져오는 것을 잊지말자.
// 다른 라이브러리도 user라는 이름의 객체가 많아서 주의해야 한다.
public class UserAccount extends User {
private final Account account;
public UserAccount(Account account) {
super(account.getNickname(), account.getPassword(), List.of(new SimpleGrantedAuthority("ROLE_USER")));
this.account = account;
}
}
스프링 시큐리티 컨텍스트 내부에 인증 정보 중 현재 이용중인 principal
에 대한 값으로 단순히 문자열을 넣는 것이 아니라, Account
라는 객체 자체를 넣기 위해서 만든 클래스이다. 해당 클래스가 상속하는 User
클래스는 import org.springframework.security.core.userdetails.User
에 위치한 클래스이다.
또한 super(account.getNickname(), account.getPassword(), List.of(new SimpleGrantedAuthority("ROLE_USER")));
을 통해 스프링 시큐리티에서 제공하는 User
생성자를 호출하여, 스프링 시큐리티 authentication
에서 name
을 호출했을 때, password
를 호출했을 때 Account
객체에서 각각 어느정보를 가져올지에 대해 넣어놓았다.
위와 같이 기존 코드를 보면,
${#authentication.name}
등을 가져오는 부분이 있는데, 이 때account.getNickname()
을 가져오도록UserAccount
클래스의 소스에 명시해놓았다.
위는 스프링 시큐리티에서 제공하는
User
클래스의 내용 및 생성자이다.
public void login(Account account) {
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(
new UserAccount(account), // principal을 변경했음
account.getPassword(),
List.of(new SimpleGrantedAuthority("ROLE_USER"))
);
기존에 principal
로 그냥 account.getNickname()
을 호출했을 때 나오는 문자열을 줬다면, 이번에는 principal
로 객체를 주었다.
principal
의 자리에는 위에서 보다시피 어떤 오브젝트든 들어갈 수 있지만, User
를 상속하는 것이 이번 소스코드의 내용처럼 활용하기 좋을 것이다.
디버그로 값을 추적해보면 아래와 같이 객체가 들어있는 것을 확인할 수 있다.
@Controller
public class MainController {
@GetMapping("/")
public String home(@CurrentUser Account account, Model model) {
if(account != null) {
model.addAttribute(account);
}
return "index";
}
}
account
가 있는 경우에만 내려준다. 내려줄 모델의 문자열은 변수명과 같아서 생략했다.