통합하기

enxnong·2024년 7월 24일
0

스프링 시큐리티

목록 보기
10/13
post-thumbnail

정수원님의 강의 스프링 시큐리티 완전 정복 [6.x 개정판] 보면서 공부한 내용입니다.

Servlet API 통합

SecurityContextHolderAwareRequestFilter

  • 스프링 시큐리티는 인증 관련 기능들을 필터가 아닌 서블릿 영역에서 처리할 수 있다
  • Servlet 3 버전 이상에서 통합을 위해서는 3개의 클래스가 사용되어 진다
    • SecurityContextHolderAwareRequestFilter
      • HTTP 요청이 처리될 때 HttpServletRequest 에 보안 관련 메소드를 추가적으로 제공하는 SecurityContextHolderAwareRequestWrapper 클래스를 적용한다
      • 요청 객체에 보안 관련 메소드를 추가적으로 제공하여 인증, 로그인, 로그아웃 등의 작업을 진행할 수 있다

💡 authenticatioManager, logoutHandlers, securityContextRepository 등의 api를 사용하고 있는데, 이는 인증 수행/인증 상태 유지/로그인/로그아웃 등의 인증 관련 기능들을 제공하고 있다

  • HttpServlet3RequestFactory
    • Servlet 3 API 와의 통합을 제공하기 위한 Servlet3SecurityContextHolderAwareRequestWrapper 객체를 생성한다
    • Servlet3SecurityContextHolderAwareRequestWrapper(실체 구현체)는 SecurityContextHolderAwareRequestWrapper를 상속받았다

💡 setLogoutHanders, setTrustResolver, setAuthenticationManger 등의 메소드에 SecurityContextHolderAwareRequestFilter가 전달해준 객체들을 넘겨받아 인증을 수행할 수 있도록 준비한다

  • Servlet3SecurityContextHolderAwareRequestWrapper
    • Servlet 3.0의 기능을 지원하면서 동시에 SecurityContextHolder(인증관련 기능)와의 통합을 제공한다

💡 logout, isAuthenticated, login 등의 보안 메소드를 통해 인증을 수행할 때 서블릿에서 사용할 수 있는 객체가 되어 서블릿에서 인증 관련 수행을 처리할 수 있다

Spring MVC 통합

@AuthenticationPrincipal

  • Spring Security에서는 entication.getPrincipal()을 자동으로 해결 할 수 있는 AuthenticationPrincipalArgumentResolver 를 제공한다

  • 그렇기에 Spring MVC에서 @AuthenticationPrincipal 을 메서드 인수에 선언하게 되면 Spring Security와 같은 기능을 독립적으로 사용할 수 있다

  • Spring Security 버전

@RequestMapping("/user")
public void findUser() {
Authentication authentication = SecurityContextHolder.getContextHolderStrategy().getContext().getAuthentication();
CustomUser custom = (CustomUser) authentication == null ? null : authentication.getPrincipal();
}
  • @AuthenticationPrincipal 버전
    • AuthenticationPrincipalArgumentResolver 클래스는 해당 어노테이션이 선언되면 authentication으로부터 principal을 꺼내와서 인자를 전달해주는 역할을 한다
    • 즉, 동일한 CustomUser 객체를 얻을 수 있다
@GetMapping("/user")
public User user(@AuthenticationPrincipal User user){
   return user;
	// User principal 자체를 의미한다
    // 그러므로 authentication 인증 객체 안에 
    // principal 속성에 User 객체가 저장되어 있어야 한다
}

@AuthenticationPrincipal(expression="표현식")

  • principal 객체 내부의 특정 필드나 메서드에 접근하고자 할 때 사용한다
@GetMapping("/user2")
public String user2(@AuthenticationPrincipal(expression = "username") String user){
	// User 객체 안에 들어있는 username 필드 가져오기
	return user;
}

@AuthenticationPrincipal 메타 주석

  • CurrentUser 어노테이션 생성 후 @AuthenticationPrincipal 어노테이션 추가 부여하여 자체적으로 메타 주석처리 할 수 있다
// CurrentUser 어노테이션 생성
@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {
}

// CurrentUser 어노테이션 사용
@GetMapping("/currentUsername")
public String currentUser(@CurrentUser String user){
   // CurrentUser 어노테이션으로 @AuthenticationPrincipal 어노테이션 메타 주석 처리 진행
   return user;
}

// CurrentUsername 어노테이션 생성 (표현식 사용)
@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : username")
// #this : principal 자체를 의미
// 익명이면 null 전달, 아니면 username 전달
public @interface CurrentUsername {
}

// CurrentUsername 어노테이션 사용
@GetMapping("/currentUsername")
public String currentUsername(@CurrentUsername String user){
	return user;
}

Spring MVC 비동기 통합

WebAsyncManagerIntegrationFilter

  • 일반적으로 부모 쓰레드의 SecurityContext는 자식 쓰레드의 SecurityContext에 저장이 안된다
    • 일반적인 비동기 쓰레드를 실행할 때 SecurityContext가 서로 공유가 안된 것을 확인할 수 있다 (별도의 작업을 진행해야지 공유 가능)
  • 하지만 Callable를 실행하면 별도의 쓰레드에서 비동기 실행이 되는데 Controller를 실행하는 쓰레드의 SecurityContext는 Callable를 실행하는 쓰레드에 공유가 가능해진다
  • 즉, 부모 쓰레드와 자식 쓰레드 간의 SecurityContext가 서로 접근이 가능해진다
    @GetMapping("/callable")
    public Callable<Authentication> call(){

        // 메인 부모 쓰레드의 SecurityContext가 전달됨
        SecurityContext context = SecurityContextHolder.getContextHolderStrategy().getContext();
        System.out.println("부모 쓰레드 " + Thread.currentThread().getName());
        System.out.println("context = " + context);

        // 비동기 자식 쓰레드
        // 부모의 SecurityContext에 접근 가능한지 확인
        return new Callable<Authentication>() {
            @Override
            public Authentication call() throws Exception {
                SecurityContext context = SecurityContextHolder.getContextHolderStrategy().getContext();
                System.out.println("자식 쓰레드 " + Thread.currentThread().getName());
                System.out.println("context = " + context);
                return context.getAuthentication();
            }
        };
    }

profile
높은 곳을 향해서

0개의 댓글