Spring Security Architecture

업데이트: Link

Spring 보안 아키텍처

이 가이드는 Spring Security에 대한 입문서로, 프레임워크의 설계와 기본 구성 요소에 대한 인사이트를 제공합니다. 애플리케이션 보안의 가장 기본적인 내용만 다룹니다. 하지만 이를 통해 Spring Security를 사용하는 개발자들이 겪는 혼란을 조금이나마 해소할 수 있습니다. 이를 위해 필터를 사용하고 더 일반적으로 메서드 주석을 사용하여 웹 애플리케이션에서 보안이 적용되는 방식을 살펴봅니다. 보안 애플리케이션이 어떻게 작동하는지, 어떻게 사용자 정의할 수 있는지, 또는 애플리케이션 보안에 대해 생각하는 방법을 배워야 하는 경우 이 가이드를 사용하세요.

이 가이드는 가장 기본적인 문제 이상의 문제를 해결하기 위한 매뉴얼이나 레시피는 아니지만(이러한 문제에 대한 다른 출처가 있음), 초보자와 전문가 모두에게 유용할 수 있습니다. 또한 보안 애플리케이션에 대한 몇 가지 기본 동작을 제공하고 전체 아키텍처에 어떻게 부합하는지 이해하는 데 유용할 수 있으므로 Spring Boot를 자주 참조합니다.

:book: Note

All of the principles apply equally well to applications that do not use Spring Boot.

인증 및 액세스 제어

애플리케이션 보안은 인증(사용자가 누구인가?)과 권한 부여(무엇을 할 수 있는가?)라는 다소 독립적인 두 가지 문제로 요약됩니다. 때때로 사람들은 “권한 부여” 대신 “접근 제어”라고 말해서 혼동할 수 있지만, “권한 부여”는 다른 곳에서 과부하가 걸리기 때문에 그렇게 생각하는 것이 도움이 될 수 있습니다. Spring Security는 인증과 권한 부여를 분리하도록 설계된 아키텍처를 가지고 있으며, 두 가지 모두에 대한 전략과 확장 지점을 가지고 있습니다.

인증

인증을 위한 주요 전략 인터페이스는 AuthenticationManager이며, 메서드는 하나뿐입니다:

public interface AuthenticationManager {

  Authentication authenticate(Authentication authentication)
    throws AuthenticationException;
}

AuthenticationManagerauthenticate() 메서드에서 다음 세 가지 중 하나를 수행할 수 있습니다:

  • 입력이 유효한 주체를 나타내는지 확인할 수 있는 경우 Authentication (일반적으로 authenticated=true과 함께)을 반환합니다.

  • 입력이 유효하지 않은 주체를 나타내는 것으로 판단되면 AuthenticationException을 던집니다.

  • 결정할 수 없으면 null을 반환합니다.

AuthenticationException은 런타임 예외입니다. 일반적으로 애플리케이션의 스타일이나 목적에 따라 애플리케이션에서 일반적인 방식으로 처리합니다. 즉, 사용자 코드는 일반적으로 이를 포착하여 처리할 것으로 예상되지 않습니다. 예를 들어, 웹 UI는 인증에 실패했다는 페이지를 렌더링할 수 있으며, 백엔드 HTTP 서비스는 컨텍스트에 따라 WWW-Authenticate 헤더를 포함하거나 포함하지 않고 401 응답을 보낼 수 있습니다.

AuthenticationManager의 가장 일반적으로 사용되는 구현은 AuthenticationProvider 인스턴스 체인에 위임하는 ProviderManager입니다. AuthenticationProviderAuthenticationManager와 비슷하지만 호출자가 주어진 Authentication 유형을 지원하는지 여부를 쿼리할 수 있는 추가 메서드가 있습니다:

public interface AuthenticationProvider {

	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;

	boolean supports(Class<?> authentication);
}

supports() 메서드의 Class<?> 인수는 실제로는 Class<? extends Authentication>입니다(authenticate() 메서드로 전달된 것을 지원하는지 여부만 묻습니다). ProviderManagerAuthenticationProvider의 체인에 위임하여 동일한 애플리케이션에서 여러 가지 인증 메커니즘을 지원할 수 있습니다. ProviderManager가 특정 Authentication 인스턴스 유형을 인식하지 못하면 건너뜁니다.

ProviderManager에는 선택적 부모가 있으며, 모든 프로바이더가 null을 반환하는 경우 참조할 수 있습니다. 부모를 사용할 수 없는 경우, null AuthenticationAuthenticationException을 발생시킵니다.

애플리케이션에 보호된 리소스의 논리적 그룹(예: /api/**와 같은 경로 패턴과 일치하는 모든 웹 리소스)이 있는 경우가 있으며, 각 그룹에는 고유한 전용 AuthenticationManager가 있을 수 있습니다. 종종 이들 각각은 ProviderManager이며, 이들은 하나의 부모를 공유합니다. 그러면 부모는 일종의 “글로벌” 리소스로서 모든 공급자에 대한 폴백 역할을 합니다.

ProviderManagers with a common parent

Figure 1. An AuthenticationManager hierarchy using ProviderManager

Customizing Authentication Managers

스프링 시큐리티는 애플리케이션에서 일반적인 인증 관리자 기능을 빠르게 설정할 수 있도록 몇 가지 구성 도우미를 제공합니다. 가장 일반적으로 사용되는 헬퍼는 인메모리, JDBC 또는 LDAP 사용자 세부 정보를 설정하거나 사용자 정의 UserDetailsService를 추가하는 데 유용한 AuthenticationManagerBuilder입니다. 다음 예제는 글로벌(부모) AuthenticationManager를 구성하는 애플리케이션을 보여줍니다:

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

   ... // web stuff here

  @Autowired
  public void initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
    builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
      .password("secret").roles("USER");
  }

}

이 예제는 웹 애플리케이션에 관한 것이지만, AuthenticationManagerBuilder의 사용법은 더 광범위하게 적용할 수 있습니다(웹 애플리케이션 보안 구현 방법에 대한 자세한 내용은 웹 보안을 참조하세요). AuthenticationManagerBuilder@Bean의 메서드에 @Autowired되어 있으며, 이것이 글로벌(부모) AuthenticationManager를 빌드하게 한다는 점에 유의하십시오. 반대로 다음 예제를 살펴보겠습니다:

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

  @Autowired
  DataSource dataSource;

   ... // web stuff here

  @Override
  public void configure(AuthenticationManagerBuilder builder) {
    builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
      .password("secret").roles("USER");
  }

}

컨피규레이터에서 메서드의 @Override를 사용했다면, AuthenticationManagerBuilder는 전역의 자식이 되는 “로컬” AuthenticationManager를 빌드하는 데만 사용되었을 것입니다. Spring Boot 애플리케이션에서는 글로벌 빈을 다른 빈에 @Autowired할 수 있지만, 로컬 빈은 명시적으로 노출하지 않는 한 그렇게 할 수 없습니다.

AuthenticationManager 유형의 자체 빈을 제공하여 선점하지 않는 한 Spring Boot는 기본 글로벌 AuthenticationManager(사용자 한 명만 있음)를 제공합니다. 기본값은 그 자체로 충분히 안전하므로 사용자 정의 글로벌 AuthenticationManager가 적극적으로 필요하지 않는 한 크게 걱정할 필요가 없습니다. AuthenticationManager를 빌드하는 구성을 수행하는 경우 보호하려는 리소스에 로컬로 수행하면 글로벌 기본값에 대해 걱정하지 않아도 됩니다.

권한 부여 또는 액세스 제어

인증이 성공하면 권한 부여로 넘어갈 수 있으며, 여기서 핵심 전략은 AccessDecisionManager입니다. 프레임워크에서 제공하는 세 가지 구현이 있으며, 세 가지 모두 ProviderManagerAuthenticationProviders를 위임하는 것과 비슷하게 AccessDecisionVoter 인스턴스 체인에 위임합니다.

AccessDecisionVoter는 (주체를 나타내는) AuthenticationConfigAttributes로 장식된 보안 Object를 고려합니다:

boolean supports(ConfigAttribute attribute);

boolean supports(Class<?> clazz);

int vote(
	Authentication authentication,
	S object,
	Collection<ConfigAttribute> attributes
);

ObjectAccessDecisionManagerAccessDecisionVoter의 시그니처에서 완전히 일반적입니다. 이는 사용자가 접근하고자 하는 모든 것을 나타냅니다(웹 리소스나 Java 클래스의 메서드가 가장 일반적인 두 가지 경우입니다). ConfigAttributes 역시 상당히 일반적인 것으로, 접근에 필요한 권한 수준을 결정하는 몇 가지 메타데이터로 보안 Object를 장식하는 것을 나타냅니다. ConfigAttributes는 인터페이스입니다. 메서드는 하나뿐이며(매우 일반적이고 String을 반환함), 이 문자열은 리소스 소유자의 의도를 어떤 식으로든 인코딩하여 누가 리소스에 액세스할 수 있는지에 대한 규칙을 표현합니다. 일반적인 ConfigAttribute는 사용자 역할의 이름(예: ROLE_ADMIN 또는 ROLE_AUDIT)이며, 종종 특수 형식(예: ROLE_ 접두사)을 갖거나 평가해야 하는 표현식을 나타내기도 합니다.

대부분의 사람들은 기본 ‘AccessDecisionManager’인 AffirmativeBased를 사용합니다(유권자가 긍정적으로 응답하면 액세스 권한이 부여됨). 모든 사용자 지정은 새로운 유권자를 추가하거나 기존 유권자의 작동 방식을 수정하여 유권자에서 발생하는 경향이 있습니다.

스프링 표현식 언어(SpEL) 표현식인 ConfigAttributes를 사용하는 것이 매우 일반적입니다(예: isFullyAuthenticated() && hasRole('user')). 이는 표현식을 처리하고 해당 표현식에 대한 컨텍스트를 생성할 수 있는 AccessDecisionVoter에 의해 지원됩니다. 처리할 수 있는 표현식의 범위를 확장하려면 SecurityExpressionRoot의 사용자 정의 구현이 필요하며 때로는 SecurityExpressionHandler도 필요합니다.

Web Security

웹 계층(UI 및 HTTP 백엔드용)의 Spring Security는 서블릿 Filters를 기반으로 하므로, 먼저 Filters의 역할을 전반적으로 살펴보는 것이 도움이 됩니다. 다음 그림은 단일 HTTP 요청에 대한 핸들러의 일반적인 계층화를 보여줍니다.

Filter chain delegating to a Servlet

클라이언트가 애플리케이션에 요청을 보내면 컨테이너는 요청 URI의 경로에 따라 어떤 필터와 어떤 서블릿을 적용할지 결정합니다. 최대 하나의 서블릿이 하나의 요청을 처리할 수 있지만 필터는 하나의 체인을 형성하므로 순서가 정해집니다. 실제로 필터는 요청 자체를 처리하려는 경우 나머지 체인을 거부할 수 있습니다. 또한 필터는 다운스트림 필터와 서블릿에서 사용되는 요청이나 응답을 수정할 수도 있습니다. 필터 체인의 순서는 매우 중요하며, Spring Boot는 두 가지 메커니즘을 통해 이를 관리합니다. Filter 유형의 @Bean@Order를 갖거나 Ordered를 구현할 수 있으며, 그 자체로 API의 일부로 순서를 갖는 FilterRegistrationBean의 일부가 될 수 있습니다. 일부 기성 필터는 자체 상수를 정의하여 서로에 대해 어떤 순서를 원하는지 알 수 있습니다(예를 들어, Spring Session의 SessionRepositoryFilterDEFAULT_ORDERInteger.MIN_VALUE + 50으로, 체인에서 앞쪽에 있는 것을 좋아하지만 그 앞에 오는 다른 필터를 배제하지 않습니다).

스프링 시큐리티는 체인에 하나의 Filter로 설치되며, 그 구체적인 유형은 곧 다루게 될 FilterChainProxy입니다. 스프링 부트 애플리케이션에서 보안 필터는 ApplicationContext@Bean이며, 모든 요청에 적용되도록 기본적으로 설치되어 있습니다. 보안 필터는 SecurityProperties.DEFAULT_FILTER_ORDER에 의해 정의된 위치에 설치되며, 이는 다시 FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER(Spring Boot 애플리케이션에서 필터가 요청을 래핑하여 동작을 수정할 경우 예상되는 최대 순서)에 의해 고정됩니다. 하지만 이보다 더 많은 것이 있습니다: 컨테이너의 관점에서 보면 Spring Security는 단일 필터이지만, 그 내부에는 각각 특별한 역할을 하는 추가 필터가 있습니다. 다음 이미지는 이러한 관계를 보여줍니다:

Spring Security Filter

Figure 2.Spring Security는 하나의 물리적 Filter이지만 내부 필터 체인에 처리를 위임합니다.

사실, 보안 필터에는 한 단계 더 간접적인 계층이 있습니다: 일반적으로 컨테이너에 DelegatingFilterProxy로 설치되며, 이는 Spring @Bean일 필요는 없습니다. 프록시는 항상 @BeanFilterChainProxy에 위임하며, 일반적으로 springSecurityFilterChain이라는 고정된 이름을 갖습니다. 내부적으로 필터 체인(또는 체인)으로 배열된 모든 보안 로직이 포함된 것은 FilterChainProxy입니다. 모든 필터는 동일한 API를 가지고 있으며(모두 서블릿 사양의 Filter 인터페이스를 구현함), 나머지 체인에 대해 거부권을 행사할 수 있습니다.

동일한 최상위 레벨인 FilterChainProxy에서 Spring Security가 관리하는 필터 체인은 여러 개가 있을 수 있으며 모두 컨테이너에 알려지지 않습니다. Spring Security 필터에는 필터 체인 목록이 포함되어 있으며 일치하는 첫 번째 체인에 요청을 디스패치합니다. 다음 그림은 요청 경로 일치에 따라 발생하는 디스패치를 보여줍니다(/foo/**/** 앞에 일치). 이는 매우 일반적이지만 요청을 일치시키는 유일한 방법은 아닙니다. 이 디스패치 프로세스의 가장 중요한 특징은 하나의 체인만 요청을 처리한다는 것입니다.

Security Filter Dispatch

Figure 3. 스프링 보안 FilterChainProxy는 일치하는 첫 번째 체인에 요청을 전송합니다.

사용자 정의 보안 구성이 없는 바닐라 Spring Boot 애플리케이션에는 여러 개의 필터 체인(보통 n=6)이 있습니다. 첫 번째(n-1) 체인은 /css/**/images/**와 같은 정적 리소스 패턴과 오류 보기를 무시하기 위해 존재합니다: /error. (경로는 사용자가 SecurityProperties 구성 빈의 security.ignored를 사용하여 제어할 수 있습니다.) 마지막 체인은 포괄 경로(/**)와 일치하며 인증, 권한 부여, 예외 처리, 세션 처리, 헤더 쓰기 등에 대한 로직을 포함하는 보다 활성적입니다. 이 체인에는 기본적으로 총 11개의 필터가 있지만 일반적으로 사용자가 어떤 필터가 언제 사용되는지 신경 쓸 필요는 없습니다.

Note

Spring Security 내부의 모든 필터가 컨테이너에 알려지지 않는다는 사실은 특히 기본적으로 Filter 유형의 모든 @Bean이 컨테이너에 자동으로 등록되는 Spring Boot 애플리케이션에서 중요합니다. 따라서 보안 체인에 사용자 정의 필터를 추가하려면 @Bean으로 만들지 않거나 컨테이너 등록을 명시적으로 비활성화하는 FilterRegistrationBean으로 감싸야 합니다.

필터 체인 생성 및 사용자 지정

Spring Boot 애플리케이션의 기본 대체 필터 체인(/** 요청 일치기가 있는 체인)의 사전 정의된 순서는 SecurityProperties.BASIC_AUTH_ORDER입니다. security.basic.enabled=false를 설정하여 완전히 해제하거나, 이를 대체로 사용하고 더 낮은 순서로 다른 규칙을 정의할 수 있습니다. 후자를 사용하려면 다음과 같이 WebSecurityConfigurerAdapter(또는 WebSecurityConfigurer) 유형의 @Bean을 추가하고 @Order로 클래스를 장식합니다:

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/match1/**")
     ...;
  }
}

이 빈을 사용하면 Spring Security가 새 필터 체인을 추가하고 폴백 전에 주문합니다.

많은 애플리케이션은 한 리소스 세트에 대해 다른 리소스 세트와 완전히 다른 액세스 규칙을 가지고 있습니다. 예를 들어, UI와 백킹 API를 호스팅하는 애플리케이션은 UI 부분에 대해서는 로그인 페이지로 리디렉션하는 쿠키 기반 인증을, API 부분에 대해서는 인증되지 않은 요청에 대해 401 응답을 보내는 토큰 기반 인증을 지원할 수 있습니다. 각 리소스 세트에는 고유한 순서와 고유한 요청 일치기를 가진 자체 WebSecurityConfigurerAdapter가 있습니다. 매칭 규칙이 겹치는 경우 가장 빠른 순서의 필터 체인이 승리합니다.

Dispatch 및 Authorization Request 매칭

보안 필터 체인(또는 이에 상응하는 WebSecurityConfigurerAdapter)에는 HTTP 요청에 적용할지 여부를 결정하는 데 사용되는 요청 일치기가 있습니다. 특정 필터 체인을 적용하기로 결정하면 다른 필터 체인은 적용되지 않습니다. 그러나 필터 체인 내에서 다음과 같이 HttpSecurity 구성자에 추가 매칭자를 설정하여 권한을 보다 세밀하게 제어할 수 있습니다:

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/match1/**")
      .authorizeRequests()
        .antMatchers("/match1/user").hasRole("USER")
        .antMatchers("/match1/spam").hasRole("SPAM")
        .anyRequest().isAuthenticated();
  }
}

Spring Security를 구성할 때 가장 쉽게 저지르기 쉬운 실수 중 하나는 이러한 매칭자가 서로 다른 프로세스에 적용된다는 사실을 잊어버리는 것입니다. 하나는 전체 필터 체인에 대한 요청 매칭자이고 다른 하나는 적용할 액세스 규칙만 선택하는 것입니다.

애플리케이션 보안 규칙과 액추에이터 규칙 결합

관리 엔드포인트에 Spring Boot 액추에이터를 사용하는 경우 보안이 유지되기를 원할 것이며, 기본적으로 보안이 유지됩니다. 실제로 보안 애플리케이션에 액추에이터를 추가하자마자 액추에이터 엔드포인트에만 적용되는 추가 필터 체인이 생깁니다. 이 필터는 액추에이터 엔드포인트만 일치시키는 요청 매칭으로 정의되며, 기본 SecurityProperties 폴백 필터보다 5개 적은 ManagementServerProperties.BASIC_AUTH_ORDER의 순서를 가지므로 폴백 전에 참조됩니다.

애플리케이션 보안 규칙을 액추에이터 엔드포인트에 적용하려면 액추에이터보다 먼저 정렬되고 모든 액추에이터 엔드포인트를 포함하는 요청 일치기가 있는 필터 체인을 추가할 수 있습니다. 액추에이터 엔드포인트에 대한 기본 보안 설정을 선호하는 경우 가장 쉬운 방법은 다음과 같이 액추에이터 필터보다 늦게, 그러나 폴백(예: ManagementServerProperties.BASIC_AUTH_ORDER + 1)보다 일찍 자체 필터를 추가하는 것입니다:

@Configuration
@Order(ManagementServerProperties.BASIC_AUTH_ORDER + 1)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/foo/**")
     ...;
  }
}

Note

웹 계층의 Spring Security는 현재 서블릿 API에 연결되어 있으므로, 임베디드 또는 기타 서블릿 컨테이너에서 애플리케이션을 실행할 때만 실제로 적용할 수 있습니다. 그러나 Spring MVC 또는 나머지 Spring 웹 스택에 묶여 있지 않으므로 모든 서블릿 애플리케이션(예: JAX-RS를 사용하는 애플리케이션)에서 사용할 수 있습니다.

Method Security

Spring Security는 웹 애플리케이션 보안을 지원할 뿐만 아니라 Java 메서드 실행에 액세스 규칙을 적용하는 기능도 지원합니다. Spring Security의 경우, 이것은 단지 다른 유형의 “보호된 리소스”입니다. 사용자에게는 접근 규칙이 동일한 형식의 ConfigAttribute 문자열(예: 역할 또는 표현식)을 사용하여 선언되지만 코드의 다른 위치에 있다는 것을 의미합니다. 첫 번째 단계는 예를 들어 애플리케이션의 최상위 구성에서 메서드 보안을 활성화하는 것입니다:

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SampleSecureApplication {
}

그런 다음 메서드 리소스를 직접 장식할 수 있습니다:

@Service
public class MyService {

  @Secured("ROLE_USER")
  public String secure() {
    return "Hello Security";
  }

}

이 예제는 보안 메서드가 있는 서비스입니다. Spring이 이 유형의 @Bean을 생성하면 프록시되고 호출자는 메서드가 실제로 실행되기 전에 보안 인터셉터를 거쳐야 합니다. 접근이 거부되면 호출자는 실제 메서드 결과 대신 AccessDeniedException을 얻습니다.

메서드에 보안 제약을 적용하기 위해 사용할 수 있는 다른 어노테이션이 있는데, 특히 @PreAuthorize@PostAuthorize는 각각 메서드 매개변수와 반환값에 대한 참조를 포함하는 표현식을 작성할 수 있게 해줍니다.

Tip

웹 보안과 메소드 보안을 결합하는 것은 드문 일이 아닙니다. 필터 체인은 인증, 로그인 페이지 리디렉션 등과 같은 사용자 경험 기능을 제공하고 메서드 보안은 보다 세분화된 수준에서 보호 기능을 제공합니다.

스레드로 작업하기

현재 인증된 주체를 다양한 다운스트림 소비자들이 사용할 수 있도록 해야 하기 때문에 Spring Security는 기본적으로 스레드에 구속됩니다. 기본 빌딩 블록은 SecurityContext이며, 여기에는 Authentication이 포함될 수 있습니다(사용자가 로그인한 경우 명시적으로 authenticated Authentication이 됩니다). SecurityContextHolder의 정적 편의 메서드를 통해 SecurityContext에 접근하고 조작할 수 있으며, 이 메서드들은 차례로 ThreadLocal을 조작합니다. 다음 예제는 이러한 배열을 보여줍니다:

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
assert(authentication.isAuthenticated);

사용자 애플리케이션 코드에서 이 작업을 수행하는 것은 일반적이지 않지만, 예를 들어 사용자 정의 인증 필터를 작성해야 하는 경우 유용할 수 있습니다(이 경우에도 Spring Security에는 SecurityContextHolder를 사용할 필요가 없도록 사용할 수 있는 기본 클래스가 있지만).

웹 엔드포인트에서 현재 인증된 사용자에 대한 액세스가 필요한 경우, 다음과 같이 @RequestMapping의 메서드 매개 변수를 사용할 수 있습니다:

@RequestMapping("/foo")
public String foo(@AuthenticationPrincipal User user) {
  ... // do stuff with user
}

이 어노테이션은 현재 AuthenticationSecurityContext에서 가져와서 getPrincipal() 메서드를 호출하여 메서드 파라미터를 산출합니다. AuthenticationPrincipal 유형은 인증의 유효성을 검사하는 데 사용되는 AuthenticationManager 에 따라 달라지므로, 이는 사용자 데이터에 대한 유형 안전 참조를 얻는 데 유용한 작은 트릭이 될 수 있습니다.

Spring Security를 사용 중인 경우, HttpServletRequestPrincipalAuthentication 유형이므로 이를 직접 사용할 수도 있습니다:

@RequestMapping("/foo")
public String foo(Principal principal) {
  Authentication authentication = (Authentication) principal;
  User = (User) authentication.getPrincipal();
  ... // do stuff with user
}

Spring Security를 사용하지 않을 때 작동하는 코드를 작성해야 할 때 유용할 수 있습니다( Authentication 클래스를 로드하는 것에 대해 좀 더 방어적이어야 함).

보안 메서드 비동기 처리

SecurityContext는 스레드에 바인딩되어 있으므로 보안 메서드를 호출하는 백그라운드 처리(예: @Async 사용)를 수행하려면 컨텍스트가 전파되는지 확인해야 합니다. 이는 백그라운드에서 실행되는 태스크(Runnable, Callable 등)로 SecurityContext를 래핑하는 것으로 요약됩니다. 스프링 시큐리티는 이 작업을 더 쉽게 하기 위해 RunnableCallable에 대한 래퍼와 같은 몇 가지 도우미를 제공합니다. SecurityContext@Async 메서드에 전파하려면 AsyncConfigurer를 제공하고 Executor의 유형이 올바른지 확인해야 합니다:

@Configuration
public class ApplicationConfiguration extends AsyncConfigurerSupport {

  @Override
  public Executor getAsyncExecutor() {
    return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
  }

}

댓글남기기