Enable Null checking in spring-security-test via JSpecify

Closes gh-17840
This commit is contained in:
Rob Winch 2025-09-03 12:59:46 -05:00
parent 194be8ffb6
commit 6a84f96930
No known key found for this signature in database
19 changed files with 324 additions and 44 deletions

View File

@ -1,3 +1,7 @@
plugins {
id 'security-nullability'
}
apply plugin: 'io.spring.convention.spring-module' apply plugin: 'io.spring.convention.spring-module'
dependencies { dependencies {

View File

@ -16,6 +16,8 @@
package org.springframework.security.test.aot.hint; package org.springframework.security.test.aot.hint;
import org.jspecify.annotations.Nullable;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.RuntimeHintsRegistrar;
@ -36,7 +38,7 @@ import org.springframework.util.ClassUtils;
class WebTestUtilsRuntimeHints implements RuntimeHintsRegistrar { class WebTestUtilsRuntimeHints implements RuntimeHintsRegistrar {
@Override @Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) { public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
if (!ClassUtils.isPresent("jakarta.servlet.Filter", classLoader)) { if (!ClassUtils.isPresent("jakarta.servlet.Filter", classLoader)) {
return; return;
} }

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* AOT Support for Spring Security test.
*/
@NullMarked
package org.springframework.security.test.aot.hint;
import org.jspecify.annotations.NullMarked;

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Support for Framework's Test annotations.
*/
@NullMarked
package org.springframework.security.test.context.annotation;
import org.jspecify.annotations.NullMarked;

View File

@ -0,0 +1,24 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security support managing the
* {@link org.springframework.security.core.context.SecurityContext}.
*/
@NullMarked
package org.springframework.security.test.context;
import org.jspecify.annotations.NullMarked;

View File

@ -20,6 +20,9 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedElement;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.GenericTypeResolver; import org.springframework.core.GenericTypeResolver;
@ -76,6 +79,7 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
* {@link WithSecurityContext} on it. If that is not found, the class is inspected. If * {@link WithSecurityContext} on it. If that is not found, the class is inspected. If
* still not found, then no {@link SecurityContext} is populated. * still not found, then no {@link SecurityContext} is populated.
*/ */
@NullUnmarked
@Override @Override
public void beforeTestMethod(TestContext testContext) { public void beforeTestMethod(TestContext testContext) {
TestSecurityContext testSecurityContext = createTestSecurityContext(testContext.getTestMethod(), testContext); TestSecurityContext testSecurityContext = createTestSecurityContext(testContext.getTestMethod(), testContext);
@ -98,6 +102,7 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
* If configured before test execution sets the SecurityContext * If configured before test execution sets the SecurityContext
* @since 5.1 * @since 5.1
*/ */
@NullUnmarked
@Override @Override
public void beforeTestExecution(TestContext testContext) { public void beforeTestExecution(TestContext testContext) {
Supplier<SecurityContext> supplier = (Supplier<SecurityContext>) testContext Supplier<SecurityContext> supplier = (Supplier<SecurityContext>) testContext
@ -107,13 +112,13 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
} }
} }
private TestSecurityContext createTestSecurityContext(AnnotatedElement annotated, TestContext context) { private @Nullable TestSecurityContext createTestSecurityContext(AnnotatedElement annotated, TestContext context) {
WithSecurityContext withSecurityContext = AnnotatedElementUtils.findMergedAnnotation(annotated, WithSecurityContext withSecurityContext = AnnotatedElementUtils.findMergedAnnotation(annotated,
WithSecurityContext.class); WithSecurityContext.class);
return createTestSecurityContext(annotated, withSecurityContext, context); return createTestSecurityContext(annotated, withSecurityContext, context);
} }
private TestSecurityContext createTestSecurityContext(Class<?> annotated, TestContext context) { private @Nullable TestSecurityContext createTestSecurityContext(Class<?> annotated, TestContext context) {
TestContextAnnotationUtils.AnnotationDescriptor<WithSecurityContext> withSecurityContextDescriptor = TestContextAnnotationUtils TestContextAnnotationUtils.AnnotationDescriptor<WithSecurityContext> withSecurityContextDescriptor = TestContextAnnotationUtils
.findAnnotationDescriptor(annotated, WithSecurityContext.class); .findAnnotationDescriptor(annotated, WithSecurityContext.class);
if (withSecurityContextDescriptor == null) { if (withSecurityContextDescriptor == null) {
@ -124,9 +129,10 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
return createTestSecurityContext(rootDeclaringClass, withSecurityContext, context); return createTestSecurityContext(rootDeclaringClass, withSecurityContext, context);
} }
@NullUnmarked
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
private TestSecurityContext createTestSecurityContext(AnnotatedElement annotated, private @Nullable TestSecurityContext createTestSecurityContext(AnnotatedElement annotated,
WithSecurityContext withSecurityContext, TestContext context) { @Nullable WithSecurityContext withSecurityContext, TestContext context) {
if (withSecurityContext == null) { if (withSecurityContext == null) {
return null; return null;
} }
@ -147,7 +153,9 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
return new TestSecurityContext(supplier, initialize); return new TestSecurityContext(supplier, initialize);
} }
private Annotation findAnnotation(AnnotatedElement annotated, Class<? extends Annotation> type) { @NullUnmarked
private @Nullable Annotation findAnnotation(AnnotatedElement annotated,
@Nullable Class<? extends Annotation> type) {
Annotation findAnnotation = AnnotatedElementUtils.findMergedAnnotation(annotated, type); Annotation findAnnotation = AnnotatedElementUtils.findMergedAnnotation(annotated, type);
if (findAnnotation != null) { if (findAnnotation != null) {
return findAnnotation; return findAnnotation;
@ -181,6 +189,7 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
* Clears out the {@link TestSecurityContextHolder} and the * Clears out the {@link TestSecurityContextHolder} and the
* {@link SecurityContextHolder} after each test method. * {@link SecurityContextHolder} after each test method.
*/ */
@NullUnmarked
@Override @Override
public void afterTestMethod(TestContext testContext) { public void afterTestMethod(TestContext testContext) {
this.securityContextHolderStrategyConverter.convert(testContext).clearContext(); this.securityContextHolderStrategyConverter.convert(testContext).clearContext();

View File

@ -16,6 +16,8 @@
package org.springframework.security.test.context.support; package org.springframework.security.test.context.support;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException; import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@ -89,7 +91,7 @@ final class WithUserDetailsSecurityContextFactory implements WithSecurityContext
: this.beans.getBean(UserDetailsService.class); : this.beans.getBean(UserDetailsService.class);
} }
UserDetailsService findAndAdaptReactiveUserDetailsService(String beanName) { @Nullable UserDetailsService findAndAdaptReactiveUserDetailsService(String beanName) {
try { try {
ReactiveUserDetailsService reactiveUserDetailsService = StringUtils.hasLength(beanName) ReactiveUserDetailsService reactiveUserDetailsService = StringUtils.hasLength(beanName)
? this.beans.getBean(beanName, ReactiveUserDetailsService.class) ? this.beans.getBean(beanName, ReactiveUserDetailsService.class)
@ -110,6 +112,7 @@ final class WithUserDetailsSecurityContextFactory implements WithSecurityContext
} }
@Override @Override
@SuppressWarnings("NullAway") // Dataflow analysis limitation
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return this.userDetailsService.findByUsername(username).block(); return this.userDetailsService.findByUsername(username).block();
} }

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security support classes for the Spring TestContext Framework.
*/
@NullMarked
package org.springframework.security.test.context.support;
import org.jspecify.annotations.NullMarked;

View File

@ -29,6 +29,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jspecify.annotations.NullUnmarked;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -190,6 +191,7 @@ public final class SecurityMockServerConfigurers {
* @return the {@link OAuth2LoginMutator} to further configure or use * @return the {@link OAuth2LoginMutator} to further configure or use
* @since 5.3 * @since 5.3
*/ */
@NullUnmarked
public static OAuth2LoginMutator mockOAuth2Login() { public static OAuth2LoginMutator mockOAuth2Login() {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null, OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null,
null, Collections.singleton("read")); null, Collections.singleton("read"));
@ -203,6 +205,7 @@ public final class SecurityMockServerConfigurers {
* @return the {@link OidcLoginMutator} to further configure or use * @return the {@link OidcLoginMutator} to further configure or use
* @since 5.3 * @since 5.3
*/ */
@NullUnmarked
public static OidcLoginMutator mockOidcLogin() { public static OidcLoginMutator mockOidcLogin() {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null, OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null,
null, Collections.singleton("read")); null, Collections.singleton("read"));
@ -252,6 +255,7 @@ public final class SecurityMockServerConfigurers {
private CsrfMutator() { private CsrfMutator() {
} }
@NullUnmarked
@Override @Override
public void afterConfigurerAdded(WebTestClient.Builder builder, public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) { @Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {
@ -394,6 +398,7 @@ public final class SecurityMockServerConfigurers {
builder.filters(addSetupMutatorFilter()); builder.filters(addSetupMutatorFilter());
} }
@NullUnmarked
@Override @Override
public void afterConfigurerAdded(WebTestClient.Builder builder, public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder webHttpHandlerBuilder, @Nullable WebHttpHandlerBuilder webHttpHandlerBuilder,
@ -537,6 +542,7 @@ public final class SecurityMockServerConfigurers {
configurer().afterConfigureAdded(serverSpec); configurer().afterConfigureAdded(serverSpec);
} }
@NullUnmarked
@Override @Override
public void afterConfigurerAdded(WebTestClient.Builder builder, public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) { @Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {
@ -547,6 +553,7 @@ public final class SecurityMockServerConfigurers {
configurer().afterConfigurerAdded(builder, httpHandlerBuilder, connector); configurer().afterConfigurerAdded(builder, httpHandlerBuilder, connector);
} }
@NullUnmarked
private <T extends WebTestClientConfigurer & MockServerConfigurer> T configurer() { private <T extends WebTestClientConfigurer & MockServerConfigurer> T configurer() {
return mockAuthentication( return mockAuthentication(
new JwtAuthenticationToken(this.jwt, this.authoritiesConverter.convert(this.jwt))); new JwtAuthenticationToken(this.jwt, this.authoritiesConverter.convert(this.jwt)));
@ -631,6 +638,7 @@ public final class SecurityMockServerConfigurers {
configurer().afterConfigureAdded(serverSpec); configurer().afterConfigureAdded(serverSpec);
} }
@NullUnmarked
@Override @Override
public void afterConfigurerAdded(WebTestClient.Builder builder, public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) { @Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {
@ -688,6 +696,7 @@ public final class SecurityMockServerConfigurers {
return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", issuedAt, expiresAt); return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", issuedAt, expiresAt);
} }
@NullUnmarked
private Instant getInstant(Map<String, Object> attributes, String name) { private Instant getInstant(Map<String, Object> attributes, String name) {
Object value = attributes.get(name); Object value = attributes.get(name);
if (value == null) { if (value == null) {
@ -865,12 +874,15 @@ public final class SecurityMockServerConfigurers {
private OAuth2AccessToken accessToken; private OAuth2AccessToken accessToken;
@Nullable
private OidcIdToken idToken; private OidcIdToken idToken;
@SuppressWarnings("NullAway.Init")
private OidcUserInfo userInfo; private OidcUserInfo userInfo;
private Supplier<OidcUser> oidcUser = this::defaultPrincipal; private Supplier<OidcUser> oidcUser = this::defaultPrincipal;
@Nullable
private Collection<GrantedAuthority> authorities; private Collection<GrantedAuthority> authorities;
private OidcLoginMutator(OAuth2AccessToken accessToken) { private OidcLoginMutator(OAuth2AccessToken accessToken) {
@ -1015,6 +1027,7 @@ public final class SecurityMockServerConfigurers {
return authorities; return authorities;
} }
@NullUnmarked
private OidcIdToken getOidcIdToken() { private OidcIdToken getOidcIdToken() {
if (this.idToken != null) { if (this.idToken != null) {
return this.idToken; return this.idToken;
@ -1036,10 +1049,12 @@ public final class SecurityMockServerConfigurers {
* @author Josh Cummings * @author Josh Cummings
* @since 5.3 * @since 5.3
*/ */
@NullUnmarked
public static final class OAuth2ClientMutator implements WebTestClientConfigurer, MockServerConfigurer { public static final class OAuth2ClientMutator implements WebTestClientConfigurer, MockServerConfigurer {
private String registrationId = "test"; private String registrationId = "test";
@Nullable
private ClientRegistration clientRegistration; private ClientRegistration clientRegistration;
private String principalName = "user"; private String principalName = "user";
@ -1115,12 +1130,14 @@ public final class SecurityMockServerConfigurers {
public void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) { public void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {
} }
@NullUnmarked
@Override @Override
public void afterConfigurerAdded(WebTestClient.Builder builder, public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) { @Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {
httpHandlerBuilder.filters(addAuthorizedClientFilter()); httpHandlerBuilder.filters(addAuthorizedClientFilter());
} }
@NullUnmarked
private Consumer<List<WebFilter>> addAuthorizedClientFilter() { private Consumer<List<WebFilter>> addAuthorizedClientFilter() {
OAuth2AuthorizedClient client = getClient(); OAuth2AuthorizedClient client = getClient();
return (filters) -> filters.add(0, (exchange, chain) -> { return (filters) -> filters.add(0, (exchange, chain) -> {
@ -1136,6 +1153,7 @@ public final class SecurityMockServerConfigurers {
}); });
} }
@NullUnmarked
private OAuth2AuthorizedClient getClient() { private OAuth2AuthorizedClient getClient() {
Assert.notNull(this.clientRegistration, Assert.notNull(this.clientRegistration,
"Please specify a ClientRegistration via one of the clientRegistration methods"); "Please specify a ClientRegistration via one of the clientRegistration methods");
@ -1163,12 +1181,14 @@ public final class SecurityMockServerConfigurers {
private final ReactiveOAuth2AuthorizedClientManager delegate; private final ReactiveOAuth2AuthorizedClientManager delegate;
@Nullable
private ServerOAuth2AuthorizedClientRepository authorizedClientRepository; private ServerOAuth2AuthorizedClientRepository authorizedClientRepository;
TestOAuth2AuthorizedClientManager(ReactiveOAuth2AuthorizedClientManager delegate) { TestOAuth2AuthorizedClientManager(ReactiveOAuth2AuthorizedClientManager delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
@NullUnmarked
@Override @Override
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) { public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {
ServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName()); ServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
@ -1183,7 +1203,8 @@ public final class SecurityMockServerConfigurers {
exchange.getAttributes().put(ENABLED_ATTR_NAME, Boolean.TRUE); exchange.getAttributes().put(ENABLED_ATTR_NAME, Boolean.TRUE);
} }
boolean isEnabled(ServerWebExchange exchange) { @NullUnmarked
boolean isEnabled(@Nullable ServerWebExchange exchange) {
return Boolean.TRUE.equals(exchange.getAttribute(ENABLED_ATTR_NAME)); return Boolean.TRUE.equals(exchange.getAttribute(ENABLED_ATTR_NAME));
} }
@ -1202,7 +1223,8 @@ public final class SecurityMockServerConfigurers {
private final ServerOAuth2AuthorizedClientRepository delegate; private final ServerOAuth2AuthorizedClientRepository delegate;
TestOAuth2AuthorizedClientRepository(ServerOAuth2AuthorizedClientRepository delegate) { @NullUnmarked
TestOAuth2AuthorizedClientRepository(@Nullable ServerOAuth2AuthorizedClientRepository delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
@ -1261,7 +1283,8 @@ public final class SecurityMockServerConfigurers {
* @return the {@link ReactiveOAuth2AuthorizedClientManager} for the specified * @return the {@link ReactiveOAuth2AuthorizedClientManager} for the specified
* {@link ServerWebExchange} * {@link ServerWebExchange}
*/ */
static ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository(ServerWebExchange exchange) { static @Nullable ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository(
ServerWebExchange exchange) {
ReactiveOAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(exchange); ReactiveOAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(exchange);
if (manager == null) { if (manager == null) {
return DEFAULT_CLIENT_REPO; return DEFAULT_CLIENT_REPO;
@ -1294,7 +1317,8 @@ public final class SecurityMockServerConfigurers {
((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository = repository; ((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository = repository;
} }
static ReactiveOAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(ServerWebExchange exchange) { static @Nullable ReactiveOAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(
ServerWebExchange exchange) {
OAuth2AuthorizedClientArgumentResolver resolver = findResolver(exchange, OAuth2AuthorizedClientArgumentResolver resolver = findResolver(exchange,
OAuth2AuthorizedClientArgumentResolver.class); OAuth2AuthorizedClientArgumentResolver.class);
if (resolver == null) { if (resolver == null) {
@ -1323,7 +1347,7 @@ public final class SecurityMockServerConfigurers {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends HandlerMethodArgumentResolver> T findResolver(ServerWebExchange exchange, static <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(ServerWebExchange exchange,
Class<T> resolverClass) { Class<T> resolverClass) {
if (!ClassUtils.isPresent( if (!ClassUtils.isPresent(
"org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter", "org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter",
@ -1335,7 +1359,7 @@ public final class SecurityMockServerConfigurers {
private static class WebFluxClasspathGuard { private static class WebFluxClasspathGuard {
static <T extends HandlerMethodArgumentResolver> T findResolver(ServerWebExchange exchange, static <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(ServerWebExchange exchange,
Class<T> resolverClass) { Class<T> resolverClass) {
RequestMappingHandlerAdapter handlerAdapter = getRequestMappingHandlerAdapter(exchange); RequestMappingHandlerAdapter handlerAdapter = getRequestMappingHandlerAdapter(exchange);
if (handlerAdapter == null) { if (handlerAdapter == null) {
@ -1358,7 +1382,7 @@ public final class SecurityMockServerConfigurers {
return null; return null;
} }
private static RequestMappingHandlerAdapter getRequestMappingHandlerAdapter( private static @Nullable RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(
ServerWebExchange exchange) { ServerWebExchange exchange) {
ApplicationContext context = exchange.getApplicationContext(); ApplicationContext context = exchange.getApplicationContext();
if (context != null) { if (context != null) {

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security upport for testing Spring WebFlux server endpoints via WebTestClient.
*/
@NullMarked
package org.springframework.security.test.web.reactive.server;
import org.jspecify.annotations.NullMarked;

View File

@ -17,6 +17,7 @@
package org.springframework.security.test.web.servlet.request; package org.springframework.security.test.web.servlet.request;
import jakarta.servlet.ServletContext; import jakarta.servlet.ServletContext;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.Mergeable; import org.springframework.beans.Mergeable;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -91,7 +92,7 @@ public final class SecurityMockMvcRequestBuilders {
private RequestPostProcessor postProcessor = csrf(); private RequestPostProcessor postProcessor = csrf();
private Mergeable parent; private @Nullable Mergeable parent;
private LogoutRequestBuilder() { private LogoutRequestBuilder() {
} }
@ -135,7 +136,7 @@ public final class SecurityMockMvcRequestBuilders {
} }
@Override @Override
public Object merge(Object parent) { public Object merge(@Nullable Object parent) {
if (parent == null) { if (parent == null) {
return this; return this;
} }
@ -168,7 +169,7 @@ public final class SecurityMockMvcRequestBuilders {
private MediaType acceptMediaType = MediaType.APPLICATION_FORM_URLENCODED; private MediaType acceptMediaType = MediaType.APPLICATION_FORM_URLENCODED;
private Mergeable parent; private @Nullable Mergeable parent;
private RequestPostProcessor postProcessor = csrf(); private RequestPostProcessor postProcessor = csrf();
@ -297,7 +298,7 @@ public final class SecurityMockMvcRequestBuilders {
} }
@Override @Override
public Object merge(Object parent) { public Object merge(@Nullable Object parent) {
if (parent == null) { if (parent == null) {
return this; return this;
} }

View File

@ -40,6 +40,8 @@ import java.util.stream.Collectors;
import jakarta.servlet.ServletContext; import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.DefaultResourceLoader;
@ -397,6 +399,7 @@ public final class SecurityMockMvcRequestPostProcessors {
* @return the {@link OidcLoginRequestPostProcessor} for additional customization * @return the {@link OidcLoginRequestPostProcessor} for additional customization
* @since 5.3 * @since 5.3
*/ */
@NullUnmarked
public static OAuth2LoginRequestPostProcessor oauth2Login() { public static OAuth2LoginRequestPostProcessor oauth2Login() {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null, OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null,
null, Collections.singleton("read")); null, Collections.singleton("read"));
@ -425,6 +428,7 @@ public final class SecurityMockMvcRequestPostProcessors {
* @return the {@link OidcLoginRequestPostProcessor} for additional customization * @return the {@link OidcLoginRequestPostProcessor} for additional customization
* @since 5.3 * @since 5.3
*/ */
@NullUnmarked
public static OidcLoginRequestPostProcessor oidcLogin() { public static OidcLoginRequestPostProcessor oidcLogin() {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null, OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null,
null, Collections.singleton("read")); null, Collections.singleton("read"));
@ -513,6 +517,7 @@ public final class SecurityMockMvcRequestPostProcessors {
private CsrfRequestPostProcessor() { private CsrfRequestPostProcessor() {
} }
@NullUnmarked
@Override @Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
CsrfTokenRepository repository = WebTestUtils.getCsrfTokenRepository(request); CsrfTokenRepository repository = WebTestUtils.getCsrfTokenRepository(request);
@ -577,7 +582,7 @@ public final class SecurityMockMvcRequestPostProcessors {
} }
@Override @Override
public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) { public void saveToken(@Nullable CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
if (isEnabled(request)) { if (isEnabled(request)) {
request.setAttribute(TOKEN_ATTR_NAME, token); request.setAttribute(TOKEN_ATTR_NAME, token);
} }
@ -587,7 +592,7 @@ public final class SecurityMockMvcRequestPostProcessors {
} }
@Override @Override
public CsrfToken loadToken(HttpServletRequest request) { public @Nullable CsrfToken loadToken(HttpServletRequest request) {
if (isEnabled(request)) { if (isEnabled(request)) {
return (CsrfToken) request.getAttribute(TOKEN_ATTR_NAME); return (CsrfToken) request.getAttribute(TOKEN_ATTR_NAME);
} }
@ -697,8 +702,9 @@ public final class SecurityMockMvcRequestPostProcessors {
* @return the MD5 of the digest authentication response, encoded in hex * @return the MD5 of the digest authentication response, encoded in hex
* @throws IllegalArgumentException if the supplied qop value is unsupported. * @throws IllegalArgumentException if the supplied qop value is unsupported.
*/ */
private static String generateDigest(String username, String realm, String password, String httpMethod, private static String generateDigest(String username, String realm, String password,
String uri, String qop, String nonce, String nc, String cnonce) throws IllegalArgumentException { @Nullable String httpMethod, @Nullable String uri, String qop, String nonce, String nc, String cnonce)
throws IllegalArgumentException {
String a1Md5 = encodePasswordInA1Format(username, realm, password); String a1Md5 = encodePasswordInA1Format(username, realm, password);
String a2 = httpMethod + ":" + uri; String a2 = httpMethod + ":" + uri;
String a2Md5 = md5Hex(a2); String a2Md5 = md5Hex(a2);
@ -1129,6 +1135,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return this; return this;
} }
@NullUnmarked
@Override @Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
CsrfFilter.skipRequest(request); CsrfFilter.skipRequest(request);
@ -1255,6 +1262,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", issuedAt, expiresAt); return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", issuedAt, expiresAt);
} }
@NullUnmarked
private Instant getInstant(Map<String, Object> attributes, String name) { private Instant getInstant(Map<String, Object> attributes, String name) {
Object value = attributes.get(name); Object value = attributes.get(name);
if (value == null) { if (value == null) {
@ -1407,13 +1415,14 @@ public final class SecurityMockMvcRequestPostProcessors {
private OAuth2AccessToken accessToken; private OAuth2AccessToken accessToken;
private OidcIdToken idToken; private @Nullable OidcIdToken idToken;
@SuppressWarnings("NullAway.Init")
private OidcUserInfo userInfo; private OidcUserInfo userInfo;
private Supplier<OidcUser> oidcUser = this::defaultPrincipal; private Supplier<OidcUser> oidcUser = this::defaultPrincipal;
private Collection<GrantedAuthority> authorities; private @Nullable Collection<GrantedAuthority> authorities;
private OidcLoginRequestPostProcessor(OAuth2AccessToken accessToken) { private OidcLoginRequestPostProcessor(OAuth2AccessToken accessToken) {
this.accessToken = accessToken; this.accessToken = accessToken;
@ -1525,6 +1534,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return authorities; return authorities;
} }
@NullUnmarked
private OidcIdToken getOidcIdToken() { private OidcIdToken getOidcIdToken() {
if (this.idToken != null) { if (this.idToken != null) {
return this.idToken; return this.idToken;
@ -1546,11 +1556,12 @@ public final class SecurityMockMvcRequestPostProcessors {
* @author Josh Cummings * @author Josh Cummings
* @since 5.3 * @since 5.3
*/ */
@NullUnmarked
public static final class OAuth2ClientRequestPostProcessor implements RequestPostProcessor { public static final class OAuth2ClientRequestPostProcessor implements RequestPostProcessor {
private String registrationId = "test"; private String registrationId = "test";
private ClientRegistration clientRegistration; private @Nullable ClientRegistration clientRegistration;
private String principalName = "user"; private String principalName = "user";
@ -1610,6 +1621,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return this; return this;
} }
@NullUnmarked
@Override @Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
if (this.clientRegistration == null) { if (this.clientRegistration == null) {
@ -1650,14 +1662,15 @@ public final class SecurityMockMvcRequestPostProcessors {
private final OAuth2AuthorizedClientManager delegate; private final OAuth2AuthorizedClientManager delegate;
private OAuth2AuthorizedClientRepository authorizedClientRepository; private @Nullable OAuth2AuthorizedClientRepository authorizedClientRepository;
TestOAuth2AuthorizedClientManager(OAuth2AuthorizedClientManager delegate) { TestOAuth2AuthorizedClientManager(OAuth2AuthorizedClientManager delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
@NullUnmarked
@Override @Override
public OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) { public @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {
HttpServletRequest request = authorizeRequest.getAttribute(HttpServletRequest.class.getName()); HttpServletRequest request = authorizeRequest.getAttribute(HttpServletRequest.class.getName());
if (isEnabled(request)) { if (isEnabled(request)) {
return this.authorizedClientRepository.loadAuthorizedClient( return this.authorizedClientRepository.loadAuthorizedClient(
@ -1670,7 +1683,8 @@ public final class SecurityMockMvcRequestPostProcessors {
request.setAttribute(ENABLED_ATTR_NAME, Boolean.TRUE); request.setAttribute(ENABLED_ATTR_NAME, Boolean.TRUE);
} }
boolean isEnabled(HttpServletRequest request) { @NullUnmarked
boolean isEnabled(@Nullable HttpServletRequest request) {
return Boolean.TRUE.equals(request.getAttribute(ENABLED_ATTR_NAME)); return Boolean.TRUE.equals(request.getAttribute(ENABLED_ATTR_NAME));
} }
@ -1689,7 +1703,8 @@ public final class SecurityMockMvcRequestPostProcessors {
private final OAuth2AuthorizedClientRepository delegate; private final OAuth2AuthorizedClientRepository delegate;
TestOAuth2AuthorizedClientRepository(OAuth2AuthorizedClientRepository delegate) { @NullUnmarked
TestOAuth2AuthorizedClientRepository(@Nullable OAuth2AuthorizedClientRepository delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
@ -1748,7 +1763,8 @@ public final class SecurityMockMvcRequestPostProcessors {
* @return the {@link OAuth2AuthorizedClientManager} for the specified * @return the {@link OAuth2AuthorizedClientManager} for the specified
* {@link HttpServletRequest} * {@link HttpServletRequest}
*/ */
static OAuth2AuthorizedClientRepository getAuthorizedClientRepository(HttpServletRequest request) { static @Nullable OAuth2AuthorizedClientRepository getAuthorizedClientRepository(
HttpServletRequest request) {
OAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(request); OAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(request);
if (manager == null) { if (manager == null) {
return DEFAULT_CLIENT_REPO; return DEFAULT_CLIENT_REPO;
@ -1781,7 +1797,8 @@ public final class SecurityMockMvcRequestPostProcessors {
((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository = repository; ((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository = repository;
} }
static OAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(HttpServletRequest request) { static @Nullable OAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(
HttpServletRequest request) {
OAuth2AuthorizedClientArgumentResolver resolver = findResolver(request, OAuth2AuthorizedClientArgumentResolver resolver = findResolver(request,
OAuth2AuthorizedClientArgumentResolver.class); OAuth2AuthorizedClientArgumentResolver.class);
if (resolver == null) { if (resolver == null) {
@ -1809,7 +1826,7 @@ public final class SecurityMockMvcRequestPostProcessors {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends HandlerMethodArgumentResolver> T findResolver(HttpServletRequest request, static <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(HttpServletRequest request,
Class<T> resolverClass) { Class<T> resolverClass) {
if (!ClassUtils.isPresent( if (!ClassUtils.isPresent(
"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter", null)) { "org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter", null)) {
@ -1820,7 +1837,7 @@ public final class SecurityMockMvcRequestPostProcessors {
private static class WebMvcClasspathGuard { private static class WebMvcClasspathGuard {
static <T extends HandlerMethodArgumentResolver> T findResolver(HttpServletRequest request, static <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(HttpServletRequest request,
Class<T> resolverClass) { Class<T> resolverClass) {
ServletContext servletContext = request.getServletContext(); ServletContext servletContext = request.getServletContext();
RequestMappingHandlerAdapter mapping = getRequestMappingHandlerAdapter(servletContext); RequestMappingHandlerAdapter mapping = getRequestMappingHandlerAdapter(servletContext);
@ -1839,7 +1856,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return null; return null;
} }
private static RequestMappingHandlerAdapter getRequestMappingHandlerAdapter( private static @Nullable RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(
ServletContext servletContext) { ServletContext servletContext) {
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext); WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if (context != null) { if (context != null) {

View File

@ -0,0 +1,24 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security built-in org.springframework.test.web.servlet.RequestBuilder
* implementations.
*/
@NullMarked
package org.springframework.security.test.web.servlet.request;
import org.jspecify.annotations.NullMarked;

View File

@ -20,6 +20,9 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl; import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -81,21 +84,22 @@ public final class SecurityMockMvcResultMatchers {
*/ */
public static final class AuthenticatedMatcher extends AuthenticationMatcher<AuthenticatedMatcher> { public static final class AuthenticatedMatcher extends AuthenticationMatcher<AuthenticatedMatcher> {
private SecurityContext expectedContext; private @Nullable SecurityContext expectedContext;
private Authentication expectedAuthentication; private @Nullable Authentication expectedAuthentication;
private Object expectedAuthenticationPrincipal; private @Nullable Object expectedAuthenticationPrincipal;
private String expectedAuthenticationName; private @Nullable String expectedAuthenticationName;
private Collection<? extends GrantedAuthority> expectedGrantedAuthorities; private @Nullable Collection<? extends GrantedAuthority> expectedGrantedAuthorities;
private Consumer<Authentication> assertAuthentication; private @Nullable Consumer<Authentication> assertAuthentication;
AuthenticatedMatcher() { AuthenticatedMatcher() {
} }
@NullUnmarked
@Override @Override
public void match(MvcResult result) { public void match(MvcResult result) {
SecurityContext context = load(result); SecurityContext context = load(result);

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security server-side support for testing Spring MVC applications.
*/
@NullMarked
package org.springframework.security.test.web.servlet.response;
import org.jspecify.annotations.NullMarked;

View File

@ -24,6 +24,7 @@ import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import org.jspecify.annotations.NullUnmarked;
import org.springframework.security.config.BeanIds; import org.springframework.security.config.BeanIds;
import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.test.web.servlet.request.RequestPostProcessor;
@ -66,6 +67,7 @@ final class SecurityMockMvcConfigurer extends MockMvcConfigurerAdapter {
builder.addFilters(this.delegateFilter); builder.addFilters(this.delegateFilter);
} }
@NullUnmarked
@Override @Override
public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder, public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder,
WebApplicationContext context) { WebApplicationContext context) {
@ -100,6 +102,7 @@ final class SecurityMockMvcConfigurer extends MockMvcConfigurerAdapter {
*/ */
static class DelegateFilter implements Filter { static class DelegateFilter implements Filter {
@SuppressWarnings("NullAway.Init")
private Filter delegate; private Filter delegate;
DelegateFilter() { DelegateFilter() {

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security built-in MockMvcBuilder implementations.
*/
@NullMarked
package org.springframework.security.test.web.servlet.setup;
import org.jspecify.annotations.NullMarked;

View File

@ -21,6 +21,8 @@ import java.util.List;
import jakarta.servlet.Filter; import jakarta.servlet.Filter;
import jakarta.servlet.ServletContext; import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.security.config.BeanIds; import org.springframework.security.config.BeanIds;
@ -64,6 +66,7 @@ public abstract class WebTestUtils {
* @return the {@link SecurityContextRepository} for the specified * @return the {@link SecurityContextRepository} for the specified
* {@link HttpServletRequest} * {@link HttpServletRequest}
*/ */
@NullUnmarked
public static SecurityContextRepository getSecurityContextRepository(HttpServletRequest request) { public static SecurityContextRepository getSecurityContextRepository(HttpServletRequest request) {
SecurityContextPersistenceFilter filter = findFilter(request, SecurityContextPersistenceFilter.class); SecurityContextPersistenceFilter filter = findFilter(request, SecurityContextPersistenceFilter.class);
if (filter != null) { if (filter != null) {
@ -103,7 +106,7 @@ public abstract class WebTestUtils {
* @return the {@link CsrfTokenRepository} for the specified * @return the {@link CsrfTokenRepository} for the specified
* {@link HttpServletRequest} * {@link HttpServletRequest}
*/ */
public static CsrfTokenRepository getCsrfTokenRepository(HttpServletRequest request) { public static @Nullable CsrfTokenRepository getCsrfTokenRepository(HttpServletRequest request) {
CsrfFilter filter = findFilter(request, CsrfFilter.class); CsrfFilter filter = findFilter(request, CsrfFilter.class);
if (filter == null) { if (filter == null) {
return DEFAULT_TOKEN_REPO; return DEFAULT_TOKEN_REPO;
@ -120,7 +123,7 @@ public abstract class WebTestUtils {
* @return the {@link CsrfTokenRequestHandler} for the specified * @return the {@link CsrfTokenRequestHandler} for the specified
* {@link HttpServletRequest} * {@link HttpServletRequest}
*/ */
public static CsrfTokenRequestHandler getCsrfTokenRequestHandler(HttpServletRequest request) { public static @Nullable CsrfTokenRequestHandler getCsrfTokenRequestHandler(HttpServletRequest request) {
CsrfFilter filter = findFilter(request, CsrfFilter.class); CsrfFilter filter = findFilter(request, CsrfFilter.class);
if (filter == null) { if (filter == null) {
return DEFAULT_CSRF_HANDLER; return DEFAULT_CSRF_HANDLER;
@ -142,7 +145,7 @@ public abstract class WebTestUtils {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends Filter> T findFilter(HttpServletRequest request, Class<T> filterClass) { static <T extends Filter> @Nullable T findFilter(HttpServletRequest request, Class<T> filterClass) {
ServletContext servletContext = request.getServletContext(); ServletContext servletContext = request.getServletContext();
Filter springSecurityFilterChain = getSpringSecurityFilterChain(servletContext); Filter springSecurityFilterChain = getSpringSecurityFilterChain(servletContext);
if (springSecurityFilterChain == null) { if (springSecurityFilterChain == null) {
@ -160,7 +163,7 @@ public abstract class WebTestUtils {
return null; return null;
} }
private static Filter getSpringSecurityFilterChain(ServletContext servletContext) { private static @Nullable Filter getSpringSecurityFilterChain(ServletContext servletContext) {
Filter result = (Filter) servletContext.getAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN); Filter result = (Filter) servletContext.getAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
if (result != null) { if (result != null) {
return result; return result;

View File

@ -0,0 +1,24 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security supporting the org.springframework.web.context package, such as
* WebApplicationContext implementations and various utility classes.
*/
@NullMarked
package org.springframework.security.test.web.support;
import org.jspecify.annotations.NullMarked;