Spring Security 6.4 新特性

Spring Security 6.4 提供了許多新特性。以下是該版本的主要亮點,您也可以檢視版本說明以獲取每個特性和錯誤修復的詳細列表。

棄用通知

隨著我們接近 Spring Security 7,及時瞭解棄用情況非常重要。因此,本節指出了 6.4 版本中的棄用項。

  • 方法安全 (Method Security) - 建議使用 AuthorizationManager#authorize 代替 AuthorizationManager#check,後者已被棄用。這主要是為了讓返回型別成為介面而不是具體類。如果您正在呼叫 AuthorizationManager#check,請改為呼叫 AuthorizationManager#authorize

    與此相關,接受 AuthorizationDecision 引數的 AuthorizationEventPublisher#publishEvent 方法已被棄用,建議使用接受 AuthorizationResult 介面引數的同名方法。

  • 方法安全 (Method Security) - PrePostTemplateDefaults 已被棄用,建議使用更通用的 AnnotationTemplateExpressionDefaults,因為現在 `@AuthenticationPrincipal` 和 `@CurrentSecurityContext` 也支援元註解屬性。如果您正在構造 PrePostTemplateDefaults,請將其更改為 AnnotationTemplateExpressionDefaults

  • OAuth 2.0 - NimbusOpaqueTokenIntrospector 已被棄用,建議使用 SpringOpaqueTokenIntrospector,以移除 Spring Security OAuth 2.0 資源伺服器對 oidc-oauth2-sdk 包的依賴。如果您正在構造 NimbusOpaqueTokenIntrospector,請將其替換為 SpringOpaqueTokenIntrospector 的建構函式。

  • OAuth 2.0 - DefaultAuthorizationCodeTokenResponseClientDefaultClientCredentialsTokenResponseClientDefaultJwtBearerTokenResponseClientDefaultPasswordTokenResponseClientDefaultRefreshTokenTokenResponseClientDefaultTokenExchangeTokenResponseClient 已被棄用,建議使用其 RestClient 等效實現。

    與此相關,JwtBearerGrantRequestEntityConverterOAuth2AuthorizationCodeGrantRequestEntityConverterOAuth2ClientCredentialsGrantRequestEntityConverterOAuth2PasswordGrantRequestEntityConverterOAuth2RefreshTokenGrantRequestEntityConverter 已被棄用,建議改為向上述令牌響應客戶端之一提供 DefaultOAuth2TokenRequestParametersConverter 的例項。

    例如,如果您有以下配置

    private static class MyCustomConverter
        extends AbstractOAuth2AuthorizationGrantRequestEntityConverter<OAuth2AuthorizationCodeGrantRequest> {
    	@Override
        protected MultiValueMap<String, String> createParameters
                (OAuth2AuthorizationCodeGrantRequest request) {
    		MultiValueMap<String, String> parameters = super.createParameters(request);
    		parameters.add("custom", "value");
    		return parameters;
        }
    }
    
    @Bean
    OAuth2AccessTokenResponseClient authorizationCode() {
    	DefaultAuthorizationCodeTokenResponseClient client =
            new DefaultAuthorizationCodeTokenResponseClient();
    	Converter<AuthorizationCodeGrantRequest, RequestEntity<?>> entityConverter =
            new OAuth2AuthorizationCodeGrantRequestEntityConverter();
    	entityConverter.setParametersConverter(new MyCustomConverter());
    	client.setRequestEntityConverter(entityConverter);
        return client;
    }

    此配置已棄用,因為它使用了 DefaultAuthorizationCodeTokenResponseClientOAuth2AuthorizationCodeGrantRequestEntityConverter。現在推薦的配置是

    private static class MyCustomConverter implements Converter<OAuth2AuthorizationCodeGrantRequest, Map<String, String>> {
    	@Override
        public MultiValueMap<String, String> convert(OAuth2AuthorizeCodeGrantRequest request) {
    		MultiValueMap<String, String> parameters = OAuth2AuthorizationCodeGrantRequest.defaultParameters(request);
    		parameters.add("custom", "value");
    		return parameters;
        }
    }
    
    @Bean
    OAuth2AccessTokenResponseClient authorizationCode() {
    	RestClientAuthorizationCodeTokenResponseClient client =
            new RestClientAuthorizationCodeTokenResponseClient();
    	client.setParametersConverter(new MyCustomConverter());
        return client;
    }
  • SAML 2.0 - Spring Security SAML 2.0 服務提供者介面的未版本化 OpenSAML 實現已被棄用,建議使用版本化實現。例如,OpenSamlAuthenticationTokenConverter 現在已被 OpenSaml4AuthenticationTokenConverterOpenSaml5AuthenticationTokenConverter 替代。如果您正在構造這些已棄用版本之一,請將其替換為您正在使用的 OpenSAML 版本對應的實現。

  • SAML 2.0 - 關於 AssertingPartyDetails 的方法已被棄用,建議使用接受 AssertingPartyMetadata 介面引數的等效方法。

  • LDAP - DistinguishedName 的用法現已棄用,以與 Spring LDAP 的棄用保持一致。

一次性令牌登入

通行金鑰

Spring Security 現在支援通行金鑰

方法安全

  • 所有方法安全註解現在都支援 Spring Framework 的 `@AliasFor`

  • `@AuthenticationPrincipal` 和 `@CurrentSecurityContext` 現在支援註解模板

    這意味著您現在可以像這樣使用 Spring 的元註解支援

    • Java

    • Kotlin

    @Target(TargetType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @AuthenticationPrincipal("claims['{claim}']")
    @interface CurrentUsername {
    	String claim() default "sub";
    }
    
    // ...
    
    @GetMapping
    public String method(@CurrentUsername("username") String username) {
    	// ...
    }
    annotation CurrentUsername(val claim: String = "sub")
    
    // ...
    
    @GetMapping
    fun method(@CurrentUsername("username") val username: String): String {
    	// ...
    }
  • 對齊 Security 註解搜尋AbstractFallbackMethodSecurityMetadataSource 的演算法進行了一些改進。這有助於從早期版本的 Spring Security 進行遷移。

  • Native 應用現在可以使用 `@AuthorizeReturnObject`

  • Native 應用現在可以在 `@PreAuthorize` 和 `@PostAuthorize` 中引用 bean

  • SecurityAnnotationScanners 提供了一個方便的 API,用於掃描 Security 註解以及將 Security 的選擇和模板特性新增到自定義註解中。

OAuth 2.0

  • oauth2Login() 現在接受將 OAuth2AuthorizationRequestResolver 作為 `@Bean`

  • ClientRegistrations 現在支援外部獲取的配置。

  • 在響應式 oauth2Login() 中向 DSL 添加了 loginPage()

  • OIDC Back-Channel 支援現在接受型別為 logout+jwt 的退出登入令牌

  • RestClient 現在可以配置 OAuth2ClientHttpRequestInterceptor訪問受保護的資源請求

  • 添加了基於 RestClientOAuth2AccessTokenResponseClient 實現,以便更一致地配置訪問令牌請求。

    要啟用 RestClient 支援,只需為每種授權型別釋出一個 bean,如下例所示

    • Java

    • Kotlin

    @Configuration
    public class SecurityConfig {
    
    	@Bean
    	public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
    		return new RestClientAuthorizationCodeTokenResponseClient();
    	}
    
    	@Bean
    	public OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {
    		return new RestClientRefreshTokenTokenResponseClient();
    	}
    
    	@Bean
    	public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
    		return new RestClientClientCredentialsTokenResponseClient();
    	}
    
    	@Bean
    	public OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {
    		return new RestClientJwtBearerTokenResponseClient();
    	}
    
    	@Bean
    	public OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeAccessTokenResponseClient() {
    		return new RestClientTokenExchangeTokenResponseClient();
    	}
    
    }
    @Configuration
    class SecurityConfig {
    
    	@Bean
    	fun authorizationCodeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
    		return RestClientAuthorizationCodeTokenResponseClient()
    	}
    
    	@Bean
    	fun refreshTokenAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> {
    		return RestClientRefreshTokenTokenResponseClient()
    	}
    
    	@Bean
    	fun clientCredentialsAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {
    		return RestClientClientCredentialsTokenResponseClient()
    	}
    
    	@Bean
    	fun jwtBearerAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> {
    		return RestClientJwtBearerTokenResponseClient()
    	}
    
    	@Bean
    	fun tokenExchangeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> {
    		return RestClientTokenExchangeTokenResponseClient()
    	}
    
    }
  • 令牌交換現在支援重新整理令牌

SAML 2.0

  • 添加了OpenSAML 5 支援。現在您可以使用 OpenSAML 4 或 OpenSAML 5;預設情況下,Spring Security 將根據您 classpath 中存在的版本選擇正確的實現。

  • 簡化了使用 EntityID 作為 registrationId 的過程。

    一種常見模式是透過其 entityID 來標識斷言方。在以前的版本中,這需要直接配置 OpenSamlAuthenticationRequestResolver。現在,請求解析器除了在路徑中查詢 registrationId 外,預設還將其作為請求引數查詢。這允許您使用 RelyingPartyRegistrationsOpenSaml4/5AssertingPartyMetadataRepository,而無需修改 registrationId 值或自定義請求解析器。

    與此相關,您現在可以配置您的 authenticationRequestUri 包含一個查詢引數

  • 現在可以根據元資料的過期時間在後臺重新整理斷言方 (Asserting Parties)。

    例如,您現在可以使用 OpenSaml5AssertingPartyMetadataRepository 來實現

    • Java

    • Kotlin

    @Component
    public class RefreshableRelyingPartyRegistrationRepository implements IterableRelyingPartyRegistrationRepository {
    	private final AssertingPartyMetadataRepository assertingParties = OpenSaml5AssertingPartyMetadataRepository
    		.fromTrustedMetadataLocation("https://idp.example.org").build();
    
    	@Override
    	public RelyingPartyRegistration findByRegistrationId(String registrationId) {
    		AssertingPartyMetadata assertingParty = this.assertingParties.findByEntityId(registrationId);
    		return RelyingPartyRegistration.withAssertingPartyMetadata(assertingParty)
    			// relying party configurations
    			.build();
    	}
    
    	// ...
    }
    @Component
    open class RefreshableRelyingPartyRegistrationRepository: IterableRelyingPartyRegistrationRepository {
    	private val assertingParties: AssertingPartyMetadataRepository = OpenSaml5AssertingPartyMetadataRepository
    		.fromTrustedMetadataLocation("https://idp.example.org").build()
    
    	override fun findByRegistrationId(String registrationId): RelyingPartyRegistration {
    		val assertingParty = this.assertingParties.findByEntityId(registrationId)
    		return RelyingPartyRegistration.withAssertingPartyMetadata(assertingParty)
    			// relying party configurations
    			.build()
    	}
    
    	// ...
    }

    此實現還支援驗證元資料的簽名。

  • 您現在可以簽署依賴方元資料

  • RelyingPartyRegistrationRepository 的結果現在可以快取。如果您希望在應用啟動後延遲載入註冊值,這將非常有用。如果您希望透過 Spring Cache 控制何時重新整理元資料,這也很有用。

  • 為了與 SAML 2.0 標準對齊,元資料端點現在使用 application/samlmetadata+xml MIME 型別

Web

  • CSRF BREACH 令牌現在更加一致

  • “記住我” cookie 現在更加可定製

  • 安全過濾器鏈能發現更多無效配置。例如,在 `any-request` 過濾器鏈之後宣告的過濾器鏈是無效的,因為它永遠不會被呼叫。

    • Java

    • Kotlin

    @Bean
    @Order(0)
    SecurityFilterChain api(HttpSecurity http) throws Exception {
        http
            // implicit securityMatcher("/**")
            .authorizeHttpRequests(...)
            .httpBasic(...)
    
        return http.build();
    }
    
    @Bean
    @Order(1)
    SecurityFilterChain app(HttpSecurity http) throws Exception {
        http
            .securityMatcher("/app/**")
            .authorizeHttpRequests(...)
            .formLogin(...)
    
        return http.build();
    }
    @Bean
    @Order(0)
    fun api(val http: HttpSecurity): SecurityFilterChain {
        http {
    		authorizeHttpRequests {
    			// ...
    		}
    	}
        return http.build()
    }
    
    @Bean
    @Order(1)
    fun app(val http: HttpSecurity): SecurityFilterChain {
        http {
    		securityMatcher("/app/**")
    		authorizeHttpRequests {
    			// ...
    		}
    	}
        return http.build()
    }

    您可以在相關 ticket 中閱讀更多資訊。

  • ServerHttpSecurity 現在將 ServerWebExchangeFirewall 作為一個 `@Bean` 拾取。

可觀測性

可觀測性現在支援分別開關授權、認證和請求的可觀測性。例如,要關閉過濾器鏈的可觀測性,您可以釋出一個如下所示的 `@Bean`。

  • Java

  • Kotlin

@Bean
SecurityObservationSettings allSpringSecurityObservations() {
	return SecurityObservationSettings.withDefaults()
            .shouldObserveFilterChains(false).build();
}
@Bean
fun allSpringSecurityObservations(): SecurityObservationSettings {
    return SecurityObservationSettings.builder()
            .shouldObserveFilterChains(false).build()
}

Kotlin

ACL