Spring Security .apply( configurer ) deprecated

개인 프로젝트의 Spring boot 버전을 3.2 로 업데이트 하는 과정에서 deprecated 된 함수를 발견해서, 해결했던 방법을 기록한다.
문제가 된 코드는 Spring Security 의 FilterChain 을 정의하는 이부분에서 발생했다.

@Bean
@Profile("jpa-oauth-server")
public SecurityFilterChain filterChain(HttpSecurity httpSecurity,
                                       RegisteredClientRepository registeredClientRepository,
                                       OAuth2AuthorizationService authorizationService,
                                       OAuth2AuthorizationConsentService oAuth2AuthorizationConsentService,
                                       JwtEncoder jwtEncoder,
                                       AuthorizationServerSettings settings) throws Exception {

    ...
    httpSecurity
            .authorizeHttpRequests(authorize ->
                    authorize.anyRequest().authenticated()
            )
            .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
            // DEPRECATED in 6.2
            .apply(authorizationServerConfigurer);

바로 Spring Security 에서 CustomConfigurer 를 적용하는 .apply 함수가 Spring Security 6.2 부터 Deprecated 되었다. 링크를 따라가보면 ” For removal in 7.0. Use AbstractConfiguredSecurityBuilder.with(SecurityConfigurerAdapter, Customizer) instead. ” 라고 명시되어 있다.
내용은 7.0 부터 사라질 예정이니 .with() 를 사용하라는 건데, 코드를 살펴보자.

@Deprecated(
        since = "6.2",
        forRemoval = true
)
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
    configurer.addObjectPostProcessor(this.objectPostProcessor);
    configurer.setBuilder(this);
    this.add(configurer);
    return configurer;
}

public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
    this.add(configurer);
    return configurer;
}

public <C extends SecurityConfigurerAdapter<O, B>> B with(C configurer, Customizer<C> customizer) throws Exception {
    configurer.addObjectPostProcessor(this.objectPostProcessor);
    configurer.setBuilder(this);
    this.add(configurer);
    customizer.customize(configurer);
    return this;
}

기존에 사용하던 .apply 는 deprecated 되고, 기존에 못보던 .with( ) 라는 함수가 생겨난것을 확인할 수 있다. 소스를 보면 거의 동일한 역할을 하는 함수인데 customizer.customize(configurer) 가 추가되고 Return type 이 변경된 것이 전부이다.

해결 방법

가장 쉬운 방법은 Docs 에서 설명하는대로 .apply 를 .with 로 대체하는 방법이다. Customizer는 기본값으로, 기존과 동일하게 유지하려면 그냥 Customizer.withDefaults() 를 적용하면 된다.

@Bean
@Profile("jpa-oauth-server")
public SecurityFilterChain filterChain(HttpSecurity httpSecurity,
                                       RegisteredClientRepository registeredClientRepository,
                                       OAuth2AuthorizationService authorizationService,
                                       OAuth2AuthorizationConsentService oAuth2AuthorizationConsentService,
                                       JwtEncoder jwtEncoder,
                                       AuthorizationServerSettings settings) throws Exception {

    ...
    httpSecurity
            .authorizeHttpRequests(authorize ->
                    authorize.anyRequest().authenticated()
            )
            .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
            // DEPRECATED in 6.2
            // .apply(authorizationServerConfigurer) 이것과 동일하게 작동한다.
            .with(authorizationServerConfigurer, Customizer.withDefaults());

하지만 하지만, .apply 가 deprecated 되고 Customizer 라는 Functional Interface 가 추가된 만큼 기존의 코드를 조금 리팩토링했다.
기존에 사용하던 Configurer 코드 중에 같은 OAuth2AuthorizationServerConfigurer 의 설정값을 변경하는 부분이 있다.

httpSecurity.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults())
                .registeredClientRepository(registeredClientRepository)
//                .clientAuthentication(clientConfigurer -> clientConfigurer.authenticationProvider(provider))
                .authorizationService(authorizationService)
                .authorizationConsentService(oAuth2AuthorizationConsentService)
                .tokenGenerator(new JwtGenerator(jwtEncoder))
                .authorizationServerSettings(settings);

기존 코드는 .apply 를 통해 configurer 를 적용한 뒤 다시 getConfigurer 를 통해 가져와 설정값을 적용했는데, 이부분을 다음과 같이 이런 식으로 변경할 수 있다.

//Before
@Bean
@Profile("jpa-oauth-server")
public SecurityFilterChain filterChain(HttpSecurity httpSecurity,
                                       RegisteredClientRepository registeredClientRepository,
                                       OAuth2AuthorizationService authorizationService,
                                       OAuth2AuthorizationConsentService oAuth2AuthorizationConsentService,
                                       JwtEncoder jwtEncoder,
                                       AuthorizationServerSettings settings) throws Exception {

    ...
    httpSecurity
            .authorizeHttpRequests(authorize ->
                    authorize.anyRequest().authenticated()
            )
            .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
            .apply(authorizationServerConfigurer);
    
    httpSecurity.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
            .oidc(Customizer.withDefaults())
            .registeredClientRepository(registeredClientRepository)
            .authorizationService(authorizationService)
            .authorizationConsentService(oAuth2AuthorizationConsentService)
            .tokenGenerator(new JwtGenerator(jwtEncoder))
            .authorizationServerSettings(settings);

//After
@Bean
@Profile("jpa-oauth-server")
public SecurityFilterChain filterChain(HttpSecurity httpSecurity,
                                       RegisteredClientRepository registeredClientRepository,
                                       OAuth2AuthorizationService authorizationService,
                                       OAuth2AuthorizationConsentService oAuth2AuthorizationConsentService,
                                       JwtEncoder jwtEncoder,
                                       AuthorizationServerSettings settings) throws Exception {

    ...
    httpSecurity
            .authorizeHttpRequests(authorize ->
                    authorize.anyRequest().authenticated()
            )
            .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
            // DEPRECATED in 6.2
            // .apply(authorizationServerConfigurer)
            .with(authorizationServerConfigurer, oAuth2AuthorizationServerConfigurer -> {
                    oAuth2AuthorizationServerConfigurer
                            .oidc(Customizer.withDefaults())
                            .registeredClientRepository(registeredClientRepository)
                            .authorizationService(authorizationService)
                            .authorizationConsentService(oAuth2AuthorizationConsentService)
                            .tokenGenerator(new JwtGenerator(jwtEncoder))
                            .authorizationServerSettings(settings);
            });

함수형 인터페이스를 통해 훨씬 더 간결하게 사용할 수 있게 된 것 같다.

여담

이미 작성되어 있는 코드들을 리팩토링 할 때, Customizer.withDefaults() 를 많이 사용하게 될 텐데, 그 때문에 .with( ) 메소드의 두번째 인자로 Customizer.withDefaults 를 사용하는 함수를 overloading 해 달라는 issue 가 있으나, 답변은 No 인 상황이다. ( 자세한 이유는 이슈에서 확인하자. )

Leave a Comment