Spring-Security-架構(gòu)初談

身份驗(yàn)證和訪問(wèn)控制

應(yīng)用程序安全性歸結(jié)為或多或少的兩個(gè)獨(dú)立問(wèn)題:

authentication/認(rèn)證(你是誰(shuí)?)和 authorization/授權(quán)(你可以做什么?)。

有時(shí)人們會(huì)說(shuō)“訪問(wèn)控制”而不是“授權(quán)”,這可能會(huì)造成混淆,但是以這種方式思考可能會(huì)有所幫助,因?yàn)椤笆跈?quán)”在其他地方可能有點(diǎn)言過(guò)其重。Spring Security 的體系結(jié)構(gòu)旨在將認(rèn)證與授權(quán)分開,并各自具有策略和擴(kuò)展點(diǎn)。

Authentication(身份驗(yàn)證/認(rèn)證)

身份驗(yàn)證的主要策略接口是 AuthenticationManager,它只有一個(gè)方法:

public interface AuthenticationManager {

  Authentication authenticate(Authentication authentication)
    throws AuthenticationException;

}

AuthenticationManager 可以在 authenticate() 方法中執(zhí)行以下三項(xiàng)操作之一:

  1. 如果它可以驗(yàn)證輸入是否代表有效的主體,則返回 Authentication(通常使用 authenticated = true)。
  2. 如果認(rèn)為輸入代表無(wú)效的主體,則拋出 AuthenticationException。
  3. 如果無(wú)法決策,則返回 null。

AuthenticationException 是運(yùn)行時(shí)異常。它通常由應(yīng)用程序以通用方式處理,具體取決于應(yīng)用程序的用例或目的。換句話說(shuō),通常不希望用戶代碼捕獲并處理它。例如,web 程序?qū)⒊尸F(xiàn)一個(gè)頁(yè)面,指出認(rèn)證失敗,后端 HTTP服務(wù)將發(fā)送401響應(yīng),取決于上下文,帶有或不帶有 WWW-Authenticate 標(biāo)頭。

AuthenticationManager 最常用的實(shí)現(xiàn)是 ProviderManager,它委派了 AuthenticationProvider 實(shí)例鏈。AuthenticationProvider 有點(diǎn)像 AuthenticationManager,但是它還有一個(gè)額外的方法,允許調(diào)用者查詢是否支持給定的 Authentication 類型:

public interface AuthenticationProvider {

    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;

    boolean supports(Class<?> authentication);

}

supports() 方法中的 Class <?> 參數(shù)實(shí)際上是 Class <? extends Authentication>(僅會(huì)詢問(wèn)它是否支持將內(nèi)容傳遞到 authenticate() 方法中)。通過(guò)委托給 AuthenticationProviders 鏈,ProviderManager 可以在同一應(yīng)用程序中支持多種不同的身份驗(yàn)證機(jī)制。如果 ProviderManager 無(wú)法識(shí)別特定的身份驗(yàn)證實(shí)例類型,則將跳過(guò)該類型。

ProviderManager 具有可選的父級(jí),如果所有提供程序都返回 null,則可以咨詢?cè)摳讣?jí)。如果父級(jí)不可用,則 null 身份驗(yàn)證將導(dǎo)致 AuthenticationException

有時(shí),應(yīng)用程序具有邏輯組的受保護(hù)資源(例如,與路徑模式 /api/** 匹配的所有 Web 資源),并且每個(gè)組可以具有自己的專用 AuthenticationManager。通常,每一個(gè)都是 ProviderManager,它們共享一個(gè)父級(jí)。因此,父級(jí)是一種“全局”資源,充當(dāng)所有提供程序的后備。

ProviderManagers with a common parent

圖1.使用 ProviderManagerAuthenticationManager 層次結(jié)構(gòu)

自定義身份驗(yàn)證管理器

Spring Security 提供了一些配置幫助類,可以在自己的應(yīng)用程序中便捷的得到通用身份驗(yàn)證管理器功能。最常用的幫助類是 AuthenticationManagerBuilder,它非常適合配置內(nèi)存中的 JDBC 或 LDAP 用戶詳情,或者用于添加自定義 UserDetailsService。這是配置全局(父)AuthenticationManager 的應(yīng)用程序的示例:

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

}

此示例與 Web 應(yīng)用程序有關(guān),但是 AuthenticationManagerBuilder 的用法更為廣泛(有關(guān)如何實(shí)現(xiàn) Web 應(yīng)用程序安全性的詳細(xì)信息,請(qǐng)參見下文)。請(qǐng)注意,AuthenticationManagerBuilder 使用 @Autowired 注入到@Bean 的方法中-這就是使它構(gòu)建全局(父)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 僅用于構(gòu)建“本地” AuthenticationManager,這是全局的一個(gè)子類。在 Spring Boot 應(yīng)用程序中,您可以將全局變量 @Autowired連接到另一個(gè) bean 中,但是除非你自己明確暴漏出它,否則不能對(duì)本地對(duì)象執(zhí)行此操作。

Spring Boot 提供了一個(gè)默認(rèn)的全局 AuthenticationManager(只有一個(gè)用戶),除非你通過(guò)提供自己的AuthenticationManager 類型的 bean 來(lái)?yè)屨妓?。除非你需要自定義全局 AuthenticationManager,否則默認(rèn)值本身就足夠安全,你不必?fù)?dān)心太多。

授權(quán)或訪問(wèn)控制

身份驗(yàn)證成功后,我們可以繼續(xù)進(jìn)行授權(quán),這里的核心策略是 AccessDecisionManager。框架提供了三種實(shí)現(xiàn)方式,所有這三種實(shí)現(xiàn)都委托給 AccessDecisionVoter 鏈,有點(diǎn)像 ProviderManager 委托給AuthenticationProviders

AccessDecisionVoter 使用 ConfigAttributes 修飾的身份驗(yàn)證主體和安全 Object 進(jìn)行投票:

boolean supports(ConfigAttribute attribute);

boolean supports(Class<?> clazz);

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

ObjectAccessDecisionManagerAccessDecisionVoter 的簽名中是完全通用的-它表示用戶可能要訪問(wèn)的任何內(nèi)容(Web 資源或 Java 類中的方法是兩種最常見的情況)。ConfigAttributes 也相當(dāng)通用,用一些元數(shù)據(jù)來(lái)表示安全 Object 的修飾,這些元數(shù)據(jù)確定訪問(wèn)它所需的權(quán)限級(jí)別。ConfigAttribute 是一個(gè)接口,但是它只有一個(gè)通用的方法并返回 String,這些字符串以某種方式編碼資源所有者的意圖,表明有關(guān)允許誰(shuí)訪問(wèn)它的規(guī)則。典型的 ConfigAttribute 是用戶角色的名稱(如 ROLE_ADMINROLE_AUDIT),并且它們通常具有特殊的格式(如 ROLE_ 前綴)或表示需要求值的表達(dá)式。

大多數(shù)人只使用默認(rèn)的 AccessDecisionManager,它是 AffirmativeBased 的(如果任何選民投票通過(guò),則允許授予訪問(wèn)權(quán)限)。任何定制都傾向于在選民中發(fā)生,要么添加新選民,要么修改現(xiàn)有選民的投票方式。

使用作為 Spring 表達(dá)式語(yǔ)言(SpEL)表達(dá)式的 ConfigAttributes 非常常見,例如 isFullyAuthenticated() && hasRole(‘FOO')AccessDecisionVoter 支持此功能,可以處理表達(dá)式并為其創(chuàng)建上下文。為了擴(kuò)展可以處理的表達(dá)式的范圍,需要 SecurityExpressionRoot 的自定義實(shí)現(xiàn),有時(shí)還需要 SecurityExpressionHandler。

網(wǎng)絡(luò)安全

Web 層(用于 UI 和 HTTP 后端)中的 Spring Security 基于 Servlet 過(guò)濾器,因此通常首先了解過(guò)濾器的作用會(huì)很有幫助。下圖顯示了單個(gè) HTTP 請(qǐng)求的處理程序的典型分層。

Filter chain delegating to a Servlet

客戶端向應(yīng)用程序發(fā)送請(qǐng)求,然后容器根據(jù)請(qǐng)求 URI 的路徑確定對(duì)它應(yīng)用哪些過(guò)濾器和哪個(gè) servlet。一個(gè) servlet 最多只能處理一個(gè)請(qǐng)求,但是過(guò)濾器形成一個(gè)鏈,因此它們是有序的,實(shí)際上,如果過(guò)濾器要處理請(qǐng)求本身,則可以否決鏈的其余部分。過(guò)濾器還可以修改下游過(guò)濾器和 Servlet 中使用的請(qǐng)求和/或響應(yīng)。過(guò)濾器鏈的順序非常重要,Spring Boot 通過(guò)兩種機(jī)制對(duì)其進(jìn)行管理:一種是 Filter 類型的 @Bean 可以具有 @Order 或?qū)崿F(xiàn) Ordered,另一個(gè)是它們可以成為 FilterRegistrationBean 的一部分,而 FilterRegistrationBean 本身也將順序作為其 API 的一部分。一些現(xiàn)成的過(guò)濾器定義了自己的常量,以幫助表示它們希望相對(duì)于彼此的順序(例如,Spring Session 中的 SessionRepositoryFilterDEFAULT_ORDERInteger.MIN_VALUE + 50,它告訴我們,它喜歡處于過(guò)濾器鏈的前端,但并不排除其他過(guò)濾器的出現(xiàn)。

Spring Security 作為鏈中的單個(gè) Filter 安裝,其具體類型為 FilterChainProxy,原因很快就會(huì)變得顯而易見。在 Spring Boot 應(yīng)用程序中,安全過(guò)濾器是 ApplicationContext 中的 @Bean,默認(rèn)情況下會(huì)安裝該過(guò)濾器,以便將其應(yīng)用于每個(gè)請(qǐng)求。它安裝在 SecurityProperties.DEFAULT_FILTER_ORDER 定義的位置,反過(guò)來(lái)由 FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER 錨定(Spring Boot 應(yīng)用程序希望過(guò)濾器包裝請(qǐng)求并修改其行為時(shí)期望的最大順序)。但是,還有更多的功能:從容器的角度來(lái)看,Spring Security 是一個(gè)過(guò)濾器,但是在內(nèi)部有其他過(guò)濾器,每個(gè)過(guò)濾器都扮演著特殊的角色。如圖:

Spring Security Filter

圖2. Spring Security 是單個(gè)真實(shí)的過(guò)濾器,但是將處理委托給一系列內(nèi)部過(guò)濾器。實(shí)際上,安全性過(guò)濾器中甚至還有一層間接層:通常作為 DelegatingFilterProxy 安裝在容器中,而不必是 Spring @Bean,該代理類是 Spring Web 提供的 servlet 過(guò)濾器,它將所有工作委托給根 ApplicationContext 中的 Spring bean。該代理委托給一個(gè)始終為 @BeanFilterChainProxy,通常使用固定名稱 springSecurityFilterChain。

public class DelegatingFilterProxy implements Filter {
    void doFilter(request, response, filterChain) {
        Filter delegate = applicationContet.getBean("springSecurityFilterChain")
            delegate.doFilter(request, response, filterChain);
    }
}

這個(gè)被委托的 Bean 就是 FilterChainProxy,它里面包含所有內(nèi)部安全性邏輯,這些安全性邏輯在內(nèi)部排列為一個(gè)或多個(gè)過(guò)濾器鏈,它們?nèi)坑?Spring Security 在同一頂級(jí) FilterChainProxy 中管理,而對(duì)于容器來(lái)說(shuō)都是未知的,該類的主要邏輯如下偽代碼所示:

public class FilterChainProxy implements Filter {
    void doFilter(request, response, filterChain) {
        // 查找此請(qǐng)求的所有過(guò)濾器
        List<Filter> delegates = lookupDelegates(request, response)
            // 除非委托過(guò)濾器類決定停止,否則調(diào)用每個(gè)篩選器
            for delegate in delegates {
                if continue processing 
                    delegate.doFilter(request, response, filterChain)
            }
        // 如果所有過(guò)濾器都認(rèn)為ok,則允許應(yīng)用程序的其余部分運(yùn)行
        if continue processing
            filterChain.doFilter(request, response)
    }
}

Spring Security 過(guò)濾器包含一個(gè)過(guò)濾器鏈列表,并向與其匹配的第一個(gè)鏈發(fā)送請(qǐng)求。下圖顯示了基于匹配請(qǐng)求路徑(/foo/**/** 之前匹配)發(fā)生的調(diào)度。這是很常見的,但不是匹配請(qǐng)求的唯一方法。此調(diào)度過(guò)程的最重要特征是,只有一個(gè)鏈處理過(guò)請(qǐng)求。

Security Filter Dispatch

圖3. Spring Security FilterChainProxy 將請(qǐng)求分派到匹配的第一個(gè)鏈。 沒有自定義安全配置的普通 Spring Boot 應(yīng)用程序具有多個(gè)(稱為n)過(guò)濾器鏈,通常 n = 6。前 (n-1) 個(gè)鏈只是忽略靜態(tài)資源匹配模式,像 /css/**/images/**,以及錯(cuò)誤視圖 /error(路徑可以由用戶通過(guò) SecurityProperties 配置 bean 中 security.ignored 控制)。最后一條鏈與捕獲所有路徑/** 相匹配,并且更活躍,包含用于身份驗(yàn)證,授權(quán),異常處理的邏輯,會(huì)話處理,請(qǐng)求寫入等。默認(rèn)情況下,此鏈中總共有11個(gè)過(guò)濾器,但通常用戶不必?fù)?dān)心使用什么過(guò)濾器以及何時(shí)使用。

容器不知道 Spring Security 內(nèi)部的所有過(guò)濾器這一事實(shí)非常重要,尤其是在 Spring Boo t應(yīng)用程序中,默認(rèn)情況下,所有 Filter 類型的 @Bean 都會(huì)自動(dòng)向容器注冊(cè)。因此,如果要向安全鏈中添加自定義過(guò)濾器,則無(wú)需將其設(shè)置為 @Bean 或?qū)⑵浒b在明確禁用容器注冊(cè)的 FilterRegistrationBean 中。

創(chuàng)建和定制過(guò)濾器鏈

Spring Boot 應(yīng)用程序(帶有 /** 請(qǐng)求匹配器的應(yīng)用程序)中的默認(rèn)后備過(guò)濾器鏈具有 SecurityProperties.BASIC_AUTH_ORDER 的預(yù)定義順序。您可以通過(guò)設(shè)置 security.basic.enabled = false 完全關(guān)閉它,也可以將其用作后備并僅以較低的順序定義其他規(guī)則。為此,只需添加類型為 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("/foo/**")
     ...;
  
}

這個(gè) bean 將導(dǎo)致 Spring Security 添加一個(gè)新的過(guò)濾器鏈并在回退之前對(duì)其進(jìn)行排序。

許多應(yīng)用程序?qū)商踪Y源的訪問(wèn)規(guī)則完全不同。例如,承載 UI 和支持 API 的應(yīng)用程序可能支持基于 cookie 的身份驗(yàn)證以及對(duì) UI 部件的登錄頁(yè)面的重定向,而基于令牌的身份驗(yàn)證則具有針對(duì) API 部件的未經(jīng)身份驗(yàn)證的請(qǐng)求的 401 響應(yīng)。 每組資源都有其自己的 WebSecurityConfigurerAdapter 以及唯一的順序和自己的請(qǐng)求匹配器。 如果匹配規(guī)則重疊,則最早的有序過(guò)濾器鏈將獲勝。

請(qǐng)求匹配以進(jìn)行調(diào)度和授權(quán)

安全過(guò)濾器鏈(或等效的 WebSecurityConfigurerAdapter)具有請(qǐng)求匹配器,該請(qǐng)求匹配器用于確定是否將其應(yīng)用于 HTTP 請(qǐng)求。一旦決定應(yīng)用特定的過(guò)濾器鏈,就不再應(yīng)用其他過(guò)濾器鏈。但是在過(guò)濾器鏈中,可以通過(guò)在HttpSecurity 配置器中設(shè)置其他匹配器來(lái)對(duì)授權(quán)進(jìn)行更細(xì)粒度的控制。例:

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

配置 Spring Security 時(shí)最容易犯的一個(gè)錯(cuò)誤是忘記這些匹配器適用于不同的流程,一個(gè)是整個(gè)過(guò)濾器鏈的請(qǐng)求匹配器,另一個(gè)是僅選擇要應(yīng)用的訪問(wèn)規(guī)則。

將應(yīng)用程序安全規(guī)則與執(zhí)行器規(guī)則相結(jié)合

如果您將 Spring Boot Actuator 用于管理端點(diǎn),則可能希望它們是安全的,默認(rèn)情況下它們將是安全的。實(shí)際上,將執(zhí)行器添加到安全應(yīng)用程序后,您會(huì)獲得一條僅適用于執(zhí)行器端點(diǎn)的附加過(guò)濾器鏈。它由僅匹配執(zhí)行器端點(diǎn)的請(qǐng)求匹配器定義,并且其順序?yàn)?ManagementServerProperties.BASIC_AUTH_ORDER,該順序比默認(rèn)的SecurityProperties 回退過(guò)濾器少 5 個(gè),因此在進(jìn)行回退處理之前會(huì)經(jīng)過(guò)它。

如果您希望將應(yīng)用程序安全規(guī)則應(yīng)用于執(zhí)行器端點(diǎn),則可以添加一個(gè)比執(zhí)行器順序更早順序的過(guò)濾器鏈,并帶有一個(gè)包括所有執(zhí)行器端點(diǎn)的請(qǐng)求匹配器。如果您喜歡執(zhí)行器端點(diǎn)的默認(rèn)安全性設(shè)置,那么最簡(jiǎn)單的方法是在執(zhí)行器端點(diǎn)之后但在回退之前(例如 ManagementServerProperties.BASIC_AUTH_ORDER + 1)添加自己的過(guò)濾器。例:

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

Web 層中的 Spring Security 當(dāng)前與 Servlet API 綁定在一起,因此,它僅在以嵌入式或其他方式在 Servlet 容器中運(yùn)行應(yīng)用程序時(shí)才真正適用。但是,它不依賴于 Spring MVC 或 Spring Web 堆棧的其余部分,因此可以在任何 servlet 應(yīng)用程序中使用,例如使用 JAX-RS 的 servlet 應(yīng)用程序。

方法安全

除了保護(hù) Web 應(yīng)用程序安全外,Spring Security 還提供了將訪問(wèn)規(guī)則應(yīng)用于 Java 方法執(zhí)行的支持。對(duì)于 Spring Security,這只是“保護(hù)資源”的另一種類型。對(duì)于用戶而言,這意味著使用相同的 ConfigAttribute 字符串格式(例如角色或表達(dá)式)聲明訪問(wèn)規(guī)則,但在代碼中的其他位置。第一步是啟用方法安全性,例如在應(yīng)用程序的頂級(jí)配置中:

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

然后我們可以直接修飾方法資源,例如:

@Service
public class MyService {

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

}

此示例是一種使用安全方法的服務(wù)。如果 Spring 創(chuàng)建了這種類型的 @Bean,則它將被代理,并且在實(shí)際執(zhí)行該方法之前,調(diào)用者將必須通過(guò)安全攔截器。如果訪問(wèn)被拒絕,則調(diào)用者將獲得 AccessDeniedException 而不是實(shí)際的方法結(jié)果。

方法上還可以使用其他注釋來(lái)強(qiáng)制執(zhí)行安全性約束,尤其是 @PreAuthorize@PostAuthorize,它們可以使您編寫分別包含對(duì)方法參數(shù)和返回值的引用的表達(dá)式。

結(jié)合使用 Web 安全性和方法安全性并不少見。過(guò)濾器鏈提供了用戶體驗(yàn)功能,例如身份驗(yàn)證和重定向到登錄頁(yè)面等,并且方法安全性在更精細(xì)的級(jí)別上提供了保護(hù)。

使用線程

Spring Security 從根本上講是線程綁定的,因?yàn)樗枰巩?dāng)前經(jīng)過(guò)身份驗(yàn)證的主體可供各種下游使用者使用?;緲?gòu)件是 SecurityContext,它可以包含一個(gè) Authentication(當(dāng)用戶登錄時(shí),它將是經(jīng)過(guò)顯式身份驗(yàn)證的Authentication)。您始終可以通過(guò) SecurityContextHolder 中的靜態(tài)便捷方法來(lái)訪問(wèn)和操作 SecurityContext,而該方法又可以簡(jiǎn)單地操作 TheadLocal,例如:

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

用戶應(yīng)用程序代碼執(zhí)行此操作并不常見,但是,如果你需要編寫一個(gè)自定義的身份驗(yàn)證過(guò)濾器,它會(huì)很有用(盡管即使如此,Spring Security 中也可以使用基類來(lái)避免使用 SecurityContextHolder)。

如果需要訪問(wèn) Web 端點(diǎn)中當(dāng)前已認(rèn)證的用戶,則可以在 @RequestMapping 中使用方法參數(shù)。例如:

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

該注釋將當(dāng)前的身份驗(yàn)證 (Authentication) 從 SecurityContext 中拉出,并對(duì)其調(diào)用 getPrincipal() 方法以產(chǎn)生方法參數(shù)。身份驗(yàn)證中的主體類型取決于用于驗(yàn)證身份驗(yàn)證的 AuthenticationManager,因此這是獲得對(duì)用戶數(shù)據(jù)的類型安全引用的有用的小技巧。

如果使用 Spring Security,則 HttpServletRequest 中的 Principal 將為 Authentication 類型,因此您也可以直接使用它:

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

如果您需要編寫在不使用 Spring Security 時(shí)可以工作的代碼,那么這有時(shí)會(huì)很有用(您在加載 Authentication 類時(shí)需要更加謹(jǐn)慎)。

異步處理安全方法

由于 SecurityContext 是線程綁定的,因此,如果要執(zhí)行任何調(diào)用安全方法的后臺(tái)處理,例如使用 @Async,您需要確保傳播上下文。

這歸結(jié)為將 SecurityContext 包裝為在后臺(tái)執(zhí)行的任務(wù)(Runnable,Callable 等)。Spring Security 提供了一些幫助程序,例如 RunnableCallable 的包裝器。要將 SecurityContext 傳播到 @Async 方法,您需要提供 AsyncConfigurer 并確保 Executor 具有正確的類型:

@Configuration
public class ApplicationConfiguration extends AsyncConfigurerSupport {

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

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容