Spring Integration 中的安全性

安全性是任何現代企業(或雲)應用中的重要功能之一。對於分散式系統(例如基於企業整合模式構建的系統)來說,這一點更為關鍵。訊息的獨立性和松耦合使得目標系統能夠透過訊息的 payload 中的任何型別的資料進行通訊。我們可以信任所有這些訊息,或者保護我們的服務免受“感染性”訊息的侵害。

從版本 6.3 開始,整個 spring-integration-security 模組已被移除,轉而支援更通用的 spring-security-messaging 庫提供的 API。

保護通道

為了保護整合流中的訊息通道,必須將 AuthorizationChannelInterceptor 新增到這些通道中,或者可以將其配置為具有相應模式的全域性通道攔截器。

  • Java

  • XML

@Bean
@GlobalChannelInterceptor(patterns = "secured*")
AuthorizationChannelInterceptor authorizationChannelInterceptor() {
    return new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole("ADMIN", "PRESIDENT"));
}
<channel-interceptor pattern="securedChannel*">
    <beans:bean class="org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor">
        <beans:constructor-arg>
            <beans:bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
                        factory-method="hasAnyRole">
                <beans:constructor-arg>
                    <beans:array>
                        <beans:value>ADMIN</beans:value>
                        <beans:value>PRESIDENT</beans:value>
                    </beans:array>
                </beans:constructor-arg>
            </beans:bean>
        </beans:constructor-arg>
    </beans:bean>
</channel-interceptor>

更多資訊請參閱全域性通道攔截器配置

安全上下文傳播

為了確保我們與應用程式的互動是安全的,並且符合其安全系統規則,我們應該提供一些包含身份驗證(principal)物件的安全上下文。Spring Security 專案提供了一種靈活、規範的機制來透過 HTTP、WebSocket 或 SOAP 協議(透過簡單的 Spring Security 擴充套件,這也可以應用於任何其他整合協議)對應用程式客戶端進行身份驗證。它還提供了 SecurityContext,用於對應用程式物件(例如訊息通道)進行進一步的授權檢查。預設情況下,SecurityContext 透過使用 (ThreadLocalSecurityContextHolderStrategy) 繫結到當前 Thread 的執行狀態。透過對受保護方法的 AOP (面向切面程式設計) 攔截器來訪問它,以檢查(例如)呼叫的 principal 是否具有足夠的許可權來呼叫該方法。這在當前執行緒中執行良好。然而,通常處理邏輯可以在另一個執行緒、多個執行緒,甚至外部系統上執行。

如果應用程式構建在 Spring Integration 元件及其訊息通道之上,標準的執行緒繫結行為很容易配置。在這種情況下,受保護的物件可以是任何服務啟用器或轉換器,透過其 <request-handler-advice-chain> 中的 MethodSecurityInterceptor(參見為端點新增行為)甚至 MessageChannel(參見上文保護通道)進行保護。使用 DirectChannel 通訊時,SecurityContext 會自動可用,因為下游流在當前執行緒上執行。然而,對於 QueueChannelExecutorChannel 和帶 ExecutorPublishSubscribeChannel,由於這些通道的特性,訊息會在一個執行緒與另一個執行緒(或多個執行緒)之間傳輸。為了支援這種情況,我們有兩種選擇:

  • 在訊息頭中傳輸一個 Authentication 物件,並在受保護物件訪問之前在另一側提取並進行身份驗證。

  • SecurityContext 傳播到接收傳輸訊息的執行緒。

這在 spring-security-messaging 模組中實現為 org.springframework.security.messaging.context.SecurityContextPropagationChannelInterceptor,可以將其新增到任何 MessageChannel 中,或配置為 @GlobalChannelInterceptor。此攔截器的邏輯基於從當前執行緒(從 preSend() 方法)提取 SecurityContext,並從 postReceive() (beforeHandle()) 方法將其填充到另一個執行緒。更多資訊請參閱 SecurityContextPropagationChannelInterceptor 的 Javadocs。

SecurityContext 的傳播和填充只是工作的一半。由於訊息不是訊息流中執行緒的所有者,並且系統必須確保其免受任何傳入訊息的侵害,因此必須從 ThreadLocal 中清理 SecurityContextSecurityContextPropagationChannelInterceptor 提供了 afterMessageHandled() 攔截器方法實現。它透過在呼叫結束時從傳播的 principal 中釋放執行緒來執行清理操作。這意味著,當處理移交訊息的執行緒完成訊息處理時(無論成功與否),上下文都會被清除,以防止在處理另一條訊息時被無意中使用。

當使用非同步閘道器時,您應該使用 Spring Security 併發支援中相應的 AbstractDelegatingSecurityContextSupport 實現,以確保在閘道器呼叫期間進行安全上下文傳播。以下示例展示瞭如何實現:

@Configuration
@EnableIntegration
@IntegrationComponentScan
public class ContextConfiguration {

    @Bean
    public AsyncTaskExecutor securityContextExecutor() {
        return new DelegatingSecurityContextAsyncTaskExecutor(
                         new SimpleAsyncTaskExecutor());
    }

}

@MessagingGateway(asyncExecutor = "securityContextExecutor")
public interface SecuredGateway {

    @Gateway(requestChannel = "queueChannel")
    Future<String> send(String payload);

}