瀏覽代碼

Enable Null checking in spring-security-web via JSpecify

Closes gh-17535
Rob Winch 2 周之前
父節點
當前提交
c2ba662b91
共有 100 個文件被更改,包括 409 次插入179 次删除
  1. 9 9
      config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDslTests.kt
  2. 2 2
      config/src/test/kotlin/org/springframework/security/config/annotation/web/LogoutDslTests.kt
  3. 2 2
      config/src/test/kotlin/org/springframework/security/config/annotation/web/RequiresChannelDslTests.kt
  4. 1 1
      config/src/test/kotlin/org/springframework/security/config/annotation/web/SecurityContextDslTests.kt
  5. 2 2
      config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpBasicDslTests.kt
  6. 3 0
      core/src/main/java/org/springframework/security/access/ConfigAttribute.java
  7. 1 1
      core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java
  8. 1 0
      core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java
  9. 3 0
      core/src/main/java/org/springframework/security/access/vote/RoleVoter.java
  10. 2 0
      core/src/main/java/org/springframework/security/authentication/AuthenticationTrustResolver.java
  11. 5 4
      core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java
  12. 4 2
      core/src/main/java/org/springframework/security/authentication/jaas/AbstractJaasAuthenticationProvider.java
  13. 2 2
      core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationToken.java
  14. 4 1
      core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java
  15. 3 1
      core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java
  16. 1 0
      core/src/main/java/org/springframework/security/authorization/method/MethodExpressionAuthorizationManager.java
  17. 3 1
      core/src/main/java/org/springframework/security/authorization/method/NoOpAuthorizationEventPublisher.java
  18. 1 1
      core/src/main/java/org/springframework/security/core/AuthenticationException.java
  19. 4 0
      web/spring-security-web.gradle
  20. 3 2
      web/src/main/java/org/springframework/security/web/DefaultSecurityFilterChain.java
  21. 5 2
      web/src/main/java/org/springframework/security/web/FilterChainProxy.java
  22. 32 29
      web/src/main/java/org/springframework/security/web/FilterInvocation.java
  23. 5 2
      web/src/main/java/org/springframework/security/web/ObservationFilterChainDecorator.java
  24. 4 2
      web/src/main/java/org/springframework/security/web/PortMapper.java
  25. 4 2
      web/src/main/java/org/springframework/security/web/PortMapperImpl.java
  26. 2 1
      web/src/main/java/org/springframework/security/web/PortResolverImpl.java
  27. 2 1
      web/src/main/java/org/springframework/security/web/access/AccessDeniedHandlerImpl.java
  28. 4 2
      web/src/main/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluator.java
  29. 4 2
      web/src/main/java/org/springframework/security/web/access/DefaultWebInvocationPrivilegeEvaluator.java
  30. 2 1
      web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java
  31. 2 1
      web/src/main/java/org/springframework/security/web/access/PathPatternRequestTransformer.java
  32. 4 2
      web/src/main/java/org/springframework/security/web/access/RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.java
  33. 2 1
      web/src/main/java/org/springframework/security/web/access/channel/AbstractRetryEntryPoint.java
  34. 4 1
      web/src/main/java/org/springframework/security/web/access/channel/ChannelDecisionManagerImpl.java
  35. 7 2
      web/src/main/java/org/springframework/security/web/access/channel/ChannelProcessingFilter.java
  36. 5 1
      web/src/main/java/org/springframework/security/web/access/channel/InsecureChannelProcessor.java
  37. 3 1
      web/src/main/java/org/springframework/security/web/access/channel/RetryWithHttpEntryPoint.java
  38. 3 1
      web/src/main/java/org/springframework/security/web/access/channel/RetryWithHttpsEntryPoint.java
  39. 4 1
      web/src/main/java/org/springframework/security/web/access/channel/SecureChannelProcessor.java
  40. 3 0
      web/src/main/java/org/springframework/security/web/access/channel/package-info.java
  41. 3 2
      web/src/main/java/org/springframework/security/web/access/expression/AbstractVariableEvaluationContextPostProcessor.java
  42. 4 3
      web/src/main/java/org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandler.java
  43. 3 1
      web/src/main/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandler.java
  44. 5 3
      web/src/main/java/org/springframework/security/web/access/expression/DelegatingEvaluationContext.java
  45. 3 0
      web/src/main/java/org/springframework/security/web/access/expression/WebExpressionConfigAttribute.java
  46. 2 1
      web/src/main/java/org/springframework/security/web/access/expression/WebExpressionVoter.java
  47. 5 2
      web/src/main/java/org/springframework/security/web/access/expression/WebSecurityExpressionRoot.java
  48. 3 0
      web/src/main/java/org/springframework/security/web/access/expression/package-info.java
  49. 2 1
      web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java
  50. 2 1
      web/src/main/java/org/springframework/security/web/access/intercept/DefaultFilterInvocationSecurityMetadataSource.java
  51. 4 3
      web/src/main/java/org/springframework/security/web/access/intercept/FilterSecurityInterceptor.java
  52. 5 3
      web/src/main/java/org/springframework/security/web/access/intercept/RequestKey.java
  53. 1 1
      web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java
  54. 3 0
      web/src/main/java/org/springframework/security/web/access/intercept/package-info.java
  55. 3 0
      web/src/main/java/org/springframework/security/web/access/package-info.java
  56. 3 1
      web/src/main/java/org/springframework/security/web/aot/hint/WebMvcSecurityRuntimeHints.java
  57. 23 0
      web/src/main/java/org/springframework/security/web/aot/hint/package-info.java
  58. 6 4
      web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java
  59. 8 7
      web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationTargetUrlRequestHandler.java
  60. 2 1
      web/src/main/java/org/springframework/security/web/authentication/AuthenticationConverter.java
  61. 2 1
      web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java
  62. 2 1
      web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationConverter.java
  63. 1 0
      web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPoint.java
  64. 3 1
      web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java
  65. 2 1
      web/src/main/java/org/springframework/security/web/authentication/NullRememberMeServices.java
  66. 2 1
      web/src/main/java/org/springframework/security/web/authentication/RememberMeServices.java
  67. 2 1
      web/src/main/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationFailureHandler.java
  68. 5 4
      web/src/main/java/org/springframework/security/web/authentication/WebAuthenticationDetails.java
  69. 3 1
      web/src/main/java/org/springframework/security/web/authentication/logout/CompositeLogoutHandler.java
  70. 3 1
      web/src/main/java/org/springframework/security/web/authentication/logout/CookieClearingLogoutHandler.java
  71. 4 3
      web/src/main/java/org/springframework/security/web/authentication/logout/DelegatingLogoutSuccessHandler.java
  72. 3 2
      web/src/main/java/org/springframework/security/web/authentication/logout/ForwardLogoutSuccessHandler.java
  73. 3 1
      web/src/main/java/org/springframework/security/web/authentication/logout/HeaderWriterLogoutHandler.java
  74. 3 2
      web/src/main/java/org/springframework/security/web/authentication/logout/HttpStatusReturningLogoutSuccessHandler.java
  75. 2 0
      web/src/main/java/org/springframework/security/web/authentication/logout/LogoutFilter.java
  76. 2 1
      web/src/main/java/org/springframework/security/web/authentication/logout/LogoutHandler.java
  77. 4 2
      web/src/main/java/org/springframework/security/web/authentication/logout/LogoutSuccessEventPublishingLogoutHandler.java
  78. 3 2
      web/src/main/java/org/springframework/security/web/authentication/logout/LogoutSuccessHandler.java
  79. 3 1
      web/src/main/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.java
  80. 3 2
      web/src/main/java/org/springframework/security/web/authentication/logout/SimpleUrlLogoutSuccessHandler.java
  81. 3 0
      web/src/main/java/org/springframework/security/web/authentication/logout/package-info.java
  82. 2 1
      web/src/main/java/org/springframework/security/web/authentication/ott/DefaultGenerateOneTimeTokenRequestResolver.java
  83. 1 1
      web/src/main/java/org/springframework/security/web/authentication/ott/GenerateOneTimeTokenFilter.java
  84. 2 1
      web/src/main/java/org/springframework/security/web/authentication/ott/OneTimeTokenAuthenticationConverter.java
  85. 23 0
      web/src/main/java/org/springframework/security/web/authentication/ott/package-info.java
  86. 3 0
      web/src/main/java/org/springframework/security/web/authentication/package-info.java
  87. 2 1
      web/src/main/java/org/springframework/security/web/authentication/password/HaveIBeenPwnedRestApiPasswordChecker.java
  88. 23 0
      web/src/main/java/org/springframework/security/web/authentication/password/package-info.java
  89. 8 6
      web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java
  90. 3 1
      web/src/main/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationProvider.java
  91. 6 4
      web/src/main/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationToken.java
  92. 2 1
      web/src/main/java/org/springframework/security/web/authentication/preauth/RequestAttributeAuthenticationFilter.java
  93. 2 1
      web/src/main/java/org/springframework/security/web/authentication/preauth/RequestHeaderAuthenticationFilter.java
  94. 1 0
      web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.java
  95. 2 1
      web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eePreAuthenticatedProcessingFilter.java
  96. 4 2
      web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlMappableAttributesRetriever.java
  97. 3 0
      web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/package-info.java
  98. 3 0
      web/src/main/java/org/springframework/security/web/authentication/preauth/package-info.java
  99. 11 10
      web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/DefaultWASUsernameAndGroupsExtractor.java
  100. 3 1
      web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASUsernameAndGroupsExtractor.java

+ 9 - 9
config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDslTests.kt

@@ -367,7 +367,7 @@ class HttpSecurityDslTests {
         this.spring.register(CustomFilterConfig::class.java).autowire()
 
         val filterChain = spring.context.getBean(FilterChainProxy::class.java)
-        val filters: List<Filter> = filterChain.getFilters("/")
+        val filters: List<Filter>? = filterChain.getFilters("/")
 
         assertThat(filters).anyMatch { it is CustomFilter }
     }
@@ -390,7 +390,7 @@ class HttpSecurityDslTests {
         this.spring.register(CustomFilterConfigReified::class.java).autowire()
 
         val filterChain = spring.context.getBean(FilterChainProxy::class.java)
-        val filters: List<Filter> = filterChain.getFilters("/")
+        val filters: List<Filter>? = filterChain.getFilters("/")
 
         assertThat(filters).anyMatch { it is CustomFilter }
     }
@@ -413,7 +413,7 @@ class HttpSecurityDslTests {
         this.spring.register(CustomFilterAfterConfig::class.java).autowire()
 
         val filterChain = spring.context.getBean(FilterChainProxy::class.java)
-        val filters: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
+        val filters: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
 
         assertThat(filters).containsSubsequence(
             UsernamePasswordAuthenticationFilter::class.java,
@@ -440,7 +440,7 @@ class HttpSecurityDslTests {
         this.spring.register(CustomFilterAfterConfigReified::class.java).autowire()
 
         val filterChain = spring.context.getBean(FilterChainProxy::class.java)
-        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
+        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
 
         assertThat(filterClasses).containsSubsequence(
             UsernamePasswordAuthenticationFilter::class.java,
@@ -467,7 +467,7 @@ class HttpSecurityDslTests {
         this.spring.register(CustomFilterBeforeConfig::class.java).autowire()
 
         val filterChain = spring.context.getBean(FilterChainProxy::class.java)
-        val filters: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
+        val filters: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
 
         assertThat(filters).containsSubsequence(
             CustomFilter::class.java,
@@ -494,7 +494,7 @@ class HttpSecurityDslTests {
         this.spring.register(CustomFilterBeforeConfigReified::class.java).autowire()
 
         val filterChain = spring.context.getBean(FilterChainProxy::class.java)
-        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
+        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
 
         assertThat(filterClasses).containsSubsequence(
             CustomFilter::class.java,
@@ -523,7 +523,7 @@ class HttpSecurityDslTests {
         this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()
 
         val filterChain = spring.context.getBean(FilterChainProxy::class.java)
-        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
+        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
 
         assertThat(filterClasses).contains(
             CustomFilter::class.java
@@ -535,7 +535,7 @@ class HttpSecurityDslTests {
         this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()
 
         val filterChain = spring.context.getBean(FilterChainProxy::class.java)
-        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
+        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
 
         assertThat(filterClasses).contains(
             CustomFilter::class.java
@@ -588,7 +588,7 @@ class HttpSecurityDslTests {
         this.spring.register(CustomDslUsingWithConfig::class.java).autowire()
 
         val filterChain = spring.context.getBean(FilterChainProxy::class.java)
-        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
+        val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
 
         assertThat(filterClasses).contains(
             UsernamePasswordAuthenticationFilter::class.java

+ 2 - 2
config/src/test/kotlin/org/springframework/security/config/annotation/web/LogoutDslTests.kt

@@ -350,8 +350,8 @@ class LogoutDslTests {
 
     class NoopLogoutHandler: LogoutHandler {
         override fun logout(
-            request: HttpServletRequest?,
-            response: HttpServletResponse?,
+            request: HttpServletRequest,
+            response: HttpServletResponse,
             authentication: Authentication?
         ) { }
 

+ 2 - 2
config/src/test/kotlin/org/springframework/security/config/annotation/web/RequiresChannelDslTests.kt

@@ -132,8 +132,8 @@ class RequiresChannelDslTests {
 
         companion object {
             val CHANNEL_PROCESSOR: ChannelProcessor = object : ChannelProcessor {
-                override fun decide(invocation: FilterInvocation?, config: MutableCollection<ConfigAttribute>?) {}
-                override fun supports(attribute: ConfigAttribute?): Boolean = true
+                override fun decide(invocation: FilterInvocation, config: MutableCollection<ConfigAttribute>) {}
+                override fun supports(attribute: ConfigAttribute): Boolean = true
             }
         }
 

+ 1 - 1
config/src/test/kotlin/org/springframework/security/config/annotation/web/SecurityContextDslTests.kt

@@ -93,7 +93,7 @@ class SecurityContextDslTests {
         testContext.autowire()
         val filterChainProxy = testContext.context.getBean(FilterChainProxy::class.java)
         // @formatter:off
-        val filterTypes = filterChainProxy.getFilters("/").toList()
+        val filterTypes = filterChainProxy.getFilters("/")!!.toList()
 
         assertThat(filterTypes)
                 .anyMatch { it is SecurityContextHolderFilter }

+ 2 - 2
config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpBasicDslTests.kt

@@ -270,8 +270,8 @@ class ServerHttpBasicDslTests {
 
     open class MockServerAuthenticationFailureHandler: ServerAuthenticationFailureHandler {
         override fun onAuthenticationFailure(
-            webFilterExchange: WebFilterExchange?,
-            exception: AuthenticationException?
+            webFilterExchange: WebFilterExchange,
+            exception: AuthenticationException
         ): Mono<Void> {
             return Mono.empty()
         }

+ 3 - 0
core/src/main/java/org/springframework/security/access/ConfigAttribute.java

@@ -18,6 +18,8 @@ package org.springframework.security.access;
 
 import java.io.Serializable;
 
+import org.jspecify.annotations.NullUnmarked;
+
 import org.springframework.security.access.intercept.RunAsManager;
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.core.annotation.SecurityAnnotationScanner;
@@ -45,6 +47,7 @@ import org.springframework.security.core.annotation.SecurityAnnotationScanner;
  * {@link AuthorizationManager}.
  */
 @Deprecated
+@NullUnmarked
 public interface ConfigAttribute extends Serializable {
 
 	/**

+ 1 - 1
core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java

@@ -177,7 +177,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 		this.trustResolver = trustResolver;
 	}
 
-	public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
+	public void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {
 		this.roleHierarchy = roleHierarchy;
 	}
 

+ 1 - 0
core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java

@@ -85,6 +85,7 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
 	}
 
 	@Override
+	@SuppressWarnings("NullAway") // FIXME: Dataflow analysis limitation
 	public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
 			MethodInvocation mi) {
 		MethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);

+ 3 - 0
core/src/main/java/org/springframework/security/access/vote/RoleVoter.java

@@ -18,6 +18,8 @@ package org.springframework.security.access.vote;
 
 import java.util.Collection;
 
+import org.jspecify.annotations.NullUnmarked;
+
 import org.springframework.security.access.AccessDecisionVoter;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.core.Authentication;
@@ -53,6 +55,7 @@ import org.springframework.security.core.GrantedAuthority;
  * instead
  */
 @Deprecated
+@NullUnmarked
 public class RoleVoter implements AccessDecisionVoter<Object> {
 
 	private String rolePrefix = "ROLE_";

+ 2 - 0
core/src/main/java/org/springframework/security/authentication/AuthenticationTrustResolver.java

@@ -18,6 +18,7 @@ package org.springframework.security.authentication;
 
 import org.jspecify.annotations.Nullable;
 
+import org.springframework.lang.Contract;
 import org.springframework.security.core.Authentication;
 
 /**
@@ -80,6 +81,7 @@ public interface AuthenticationTrustResolver {
 	 * {@link Authentication#isAuthenticated()} is true.
 	 * @since 6.1.7
 	 */
+	@Contract("null -> false")
 	default boolean isAuthenticated(@Nullable Authentication authentication) {
 		return authentication != null && authentication.isAuthenticated() && !isAnonymous(authentication);
 	}

+ 5 - 4
core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java

@@ -39,7 +39,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
 
 	private static final long serialVersionUID = 620L;
 
-	private final Object principal;
+	private final @Nullable Object principal;
 
 	private @Nullable Object credentials;
 
@@ -49,7 +49,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
 	 * will return <code>false</code>.
 	 *
 	 */
-	public UsernamePasswordAuthenticationToken(Object principal, @Nullable Object credentials) {
+	public UsernamePasswordAuthenticationToken(@Nullable Object principal, @Nullable Object credentials) {
 		super(null);
 		this.principal = principal;
 		this.credentials = credentials;
@@ -82,7 +82,8 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
 	 *
 	 * @since 5.7
 	 */
-	public static UsernamePasswordAuthenticationToken unauthenticated(Object principal, @Nullable Object credentials) {
+	public static UsernamePasswordAuthenticationToken unauthenticated(@Nullable Object principal,
+			@Nullable Object credentials) {
 		return new UsernamePasswordAuthenticationToken(principal, credentials);
 	}
 
@@ -106,7 +107,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
 	}
 
 	@Override
-	public Object getPrincipal() {
+	public @Nullable Object getPrincipal() {
 		return this.principal;
 	}
 

+ 4 - 2
core/src/main/java/org/springframework/security/authentication/jaas/AbstractJaasAuthenticationProvider.java

@@ -178,8 +178,10 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
 			// applied.
 			authorities = getAuthorities(principals);
 			// Convert the authorities set back to an array and apply it to the token.
-			JaasAuthenticationToken result = new JaasAuthenticationToken(request.getPrincipal(),
-					request.getCredentials(), new ArrayList<>(authorities), loginContext);
+			Object principal = request.getPrincipal();
+			Assert.notNull(principal, "The principal cannot be null");
+			JaasAuthenticationToken result = new JaasAuthenticationToken(principal, request.getCredentials(),
+					new ArrayList<>(authorities), loginContext);
 			// Publish the success event
 			publishSuccessEvent(result);
 			// we're done, return the token.

+ 2 - 2
core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationToken.java

@@ -71,8 +71,8 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
 	 * @deprecated Please use constructor that takes a {@link String} instead
 	 */
 	@Deprecated(forRemoval = true, since = "7.0")
-	public static OneTimeTokenAuthenticationToken unauthenticated(String tokenValue) {
-		return new OneTimeTokenAuthenticationToken(null, tokenValue);
+	public static OneTimeTokenAuthenticationToken unauthenticated(@Nullable String tokenValue) {
+		return new OneTimeTokenAuthenticationToken(null, (tokenValue != null) ? tokenValue : "");
 	}
 
 	/**

+ 4 - 1
core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java

@@ -18,6 +18,8 @@ package org.springframework.security.authorization;
 
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
 import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
 import org.springframework.security.core.Authentication;
@@ -46,6 +48,7 @@ public interface AuthorizationEventPublisher {
 	 * @param <T> the secured object's type
 	 * @since 6.4
 	 */
-	<T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object, AuthorizationResult result);
+	<T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
+			@Nullable AuthorizationResult result);
 
 }

+ 3 - 1
core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java

@@ -19,6 +19,8 @@ package org.springframework.security.authorization;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
 import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
@@ -57,7 +59,7 @@ public final class SpringAuthorizationEventPublisher implements AuthorizationEve
 	 */
 	@Override
 	public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
-			AuthorizationResult result) {
+			@Nullable AuthorizationResult result) {
 		if (result == null) {
 			return;
 		}

+ 1 - 0
core/src/main/java/org/springframework/security/authorization/method/MethodExpressionAuthorizationManager.java

@@ -74,6 +74,7 @@ public final class MethodExpressionAuthorizationManager implements Authorization
 	 * expression
 	 */
 	@Override
+	@SuppressWarnings("NullAway") // FIXME: Dataflow analysis limitation
 	public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
 			MethodInvocation context) {
 		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, context);

+ 3 - 1
core/src/main/java/org/springframework/security/authorization/method/NoOpAuthorizationEventPublisher.java

@@ -18,6 +18,8 @@ package org.springframework.security.authorization.method;
 
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.authorization.AuthorizationEventPublisher;
 import org.springframework.security.authorization.AuthorizationResult;
 import org.springframework.security.core.Authentication;
@@ -32,7 +34,7 @@ final class NoOpAuthorizationEventPublisher implements AuthorizationEventPublish
 
 	@Override
 	public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
-			AuthorizationResult result) {
+			@Nullable AuthorizationResult result) {
 
 	}
 

+ 1 - 1
core/src/main/java/org/springframework/security/core/AuthenticationException.java

@@ -76,7 +76,7 @@ public abstract class AuthenticationException extends RuntimeException {
 	 * authentication attempt
 	 * @since 6.5
 	 */
-	public void setAuthenticationRequest(Authentication authenticationRequest) {
+	public void setAuthenticationRequest(@Nullable Authentication authenticationRequest) {
 		Assert.notNull(authenticationRequest, "authenticationRequest cannot be null");
 		this.authenticationRequest = authenticationRequest;
 	}

+ 4 - 0
web/spring-security-web.gradle

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

+ 3 - 2
web/src/main/java/org/springframework/security/web/DefaultSecurityFilterChain.java

@@ -24,6 +24,7 @@ import jakarta.servlet.Filter;
 import jakarta.servlet.http.HttpServletRequest;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.BeanFactory;
@@ -51,9 +52,9 @@ public final class DefaultSecurityFilterChain implements SecurityFilterChain, Be
 
 	private final List<Filter> filters;
 
-	private String beanName;
+	private @Nullable String beanName;
 
-	private ConfigurableListableBeanFactory beanFactory;
+	private @Nullable ConfigurableListableBeanFactory beanFactory;
 
 	public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
 		this(requestMatcher, Arrays.asList(filters));

+ 5 - 2
web/src/main/java/org/springframework/security/web/FilterChainProxy.java

@@ -30,6 +30,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -164,6 +165,7 @@ public class FilterChainProxy extends GenericFilterBean {
 	private FilterChainDecorator filterChainDecorator = new VirtualFilterChainDecorator();
 
 	public FilterChainProxy() {
+		this(Collections.emptyList());
 	}
 
 	public FilterChainProxy(SecurityFilterChain chain) {
@@ -171,6 +173,7 @@ public class FilterChainProxy extends GenericFilterBean {
 	}
 
 	public FilterChainProxy(List<SecurityFilterChain> filterChains) {
+		Assert.notNull(filterChains, "filterChains cannot be null");
 		this.filterChains = filterChains;
 	}
 
@@ -239,7 +242,7 @@ public class FilterChainProxy extends GenericFilterBean {
 	 * @param request the request to match
 	 * @return an ordered array of Filters defining the filter chain
 	 */
-	private List<Filter> getFilters(HttpServletRequest request) {
+	private @Nullable List<Filter> getFilters(HttpServletRequest request) {
 		int count = 0;
 		for (SecurityFilterChain chain : this.filterChains) {
 			if (logger.isTraceEnabled()) {
@@ -258,7 +261,7 @@ public class FilterChainProxy extends GenericFilterBean {
 	 * @param url the URL
 	 * @return matching filter list
 	 */
-	public List<Filter> getFilters(String url) {
+	public @Nullable List<Filter> getFilters(String url) {
 		PathPatternRequestTransformer requestTransformer = new PathPatternRequestTransformer();
 		HttpServletRequest transformed = requestTransformer.transform(new FilterInvocation(url, "GET").getRequest());
 		return getFilters(this.firewall.getFirewalledRequest(transformed));

+ 32 - 29
web/src/main/java/org/springframework/security/web/FilterInvocation.java

@@ -36,6 +36,7 @@ import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletRequestWrapper;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.http.HttpHeaders;
 import org.springframework.security.web.util.UrlUtils;
@@ -62,11 +63,11 @@ public class FilterInvocation {
 		throw new UnsupportedOperationException("Dummy filter chain");
 	};
 
-	private FilterChain chain;
+	private final FilterChain chain;
 
 	private HttpServletRequest request;
 
-	private HttpServletResponse response;
+	private @Nullable HttpServletResponse response;
 
 	public FilterInvocation(ServletRequest request, ServletResponse response, FilterChain chain) {
 		Assert.isTrue(request != null && response != null && chain != null, "Cannot pass null values to constructor");
@@ -79,11 +80,12 @@ public class FilterInvocation {
 		this(null, servletPath, method);
 	}
 
-	public FilterInvocation(String contextPath, String servletPath, String method) {
+	public FilterInvocation(@Nullable String contextPath, String servletPath, String method) {
 		this(contextPath, servletPath, method, null);
 	}
 
-	public FilterInvocation(String contextPath, String servletPath, String method, ServletContext servletContext) {
+	public FilterInvocation(@Nullable String contextPath, String servletPath, @Nullable String method,
+			@Nullable ServletContext servletContext) {
 		this(contextPath, servletPath, null, null, method, servletContext);
 	}
 
@@ -91,8 +93,8 @@ public class FilterInvocation {
 		this(contextPath, servletPath, pathInfo, query, method, null);
 	}
 
-	public FilterInvocation(String contextPath, String servletPath, String pathInfo, String query, String method,
-			ServletContext servletContext) {
+	public FilterInvocation(@Nullable String contextPath, String servletPath, @Nullable String pathInfo,
+			@Nullable String query, @Nullable String method, @Nullable ServletContext servletContext) {
 		DummyRequest request = new DummyRequest();
 		contextPath = (contextPath != null) ? contextPath : "/cp";
 		request.setContextPath(contextPath);
@@ -103,6 +105,7 @@ public class FilterInvocation {
 		request.setMethod(method);
 		request.setServletContext(servletContext);
 		this.request = request;
+		this.chain = DUMMY_CHAIN;
 	}
 
 	public FilterChain getChain() {
@@ -124,7 +127,7 @@ public class FilterInvocation {
 		return this.request;
 	}
 
-	public HttpServletResponse getHttpResponse() {
+	public @Nullable HttpServletResponse getHttpResponse() {
 		return this.response;
 	}
 
@@ -140,7 +143,7 @@ public class FilterInvocation {
 		return getHttpRequest();
 	}
 
-	public HttpServletResponse getResponse() {
+	public @Nullable HttpServletResponse getResponse() {
 		return getHttpResponse();
 	}
 
@@ -160,19 +163,19 @@ public class FilterInvocation {
 				DummyRequest.class.getClassLoader(), new Class[] { HttpServletRequest.class },
 				new UnsupportedOperationExceptionInvocationHandler());
 
-		private String requestURI;
+		private @Nullable String requestURI;
 
 		private String contextPath = "";
 
-		private String servletPath;
+		private @Nullable String servletPath;
 
-		private String pathInfo;
+		private @Nullable String pathInfo;
 
-		private String queryString;
+		private @Nullable String queryString;
 
-		private String method;
+		private @Nullable String method;
 
-		private ServletContext servletContext;
+		private @Nullable ServletContext servletContext;
 
 		private final HttpHeaders headers = new HttpHeaders();
 
@@ -188,7 +191,7 @@ public class FilterInvocation {
 		}
 
 		@Override
-		public Object getAttribute(String attributeName) {
+		public @Nullable Object getAttribute(String attributeName) {
 			return null;
 		}
 
@@ -196,12 +199,12 @@ public class FilterInvocation {
 			this.requestURI = requestURI;
 		}
 
-		void setPathInfo(String pathInfo) {
+		void setPathInfo(@Nullable String pathInfo) {
 			this.pathInfo = pathInfo;
 		}
 
 		@Override
-		public String getRequestURI() {
+		public @Nullable String getRequestURI() {
 			return this.requestURI;
 		}
 
@@ -219,40 +222,40 @@ public class FilterInvocation {
 		}
 
 		@Override
-		public String getServletPath() {
+		public @Nullable String getServletPath() {
 			return this.servletPath;
 		}
 
-		void setMethod(String method) {
+		void setMethod(@Nullable String method) {
 			this.method = method;
 		}
 
 		@Override
-		public String getMethod() {
+		public @Nullable String getMethod() {
 			return this.method;
 		}
 
 		@Override
-		public String getPathInfo() {
+		public @Nullable String getPathInfo() {
 			return this.pathInfo;
 		}
 
 		@Override
-		public String getQueryString() {
+		public @Nullable String getQueryString() {
 			return this.queryString;
 		}
 
-		void setQueryString(String queryString) {
+		void setQueryString(@Nullable String queryString) {
 			this.queryString = queryString;
 		}
 
 		@Override
-		public String getServerName() {
+		public @Nullable String getServerName() {
 			return null;
 		}
 
 		@Override
-		public String getHeader(String name) {
+		public @Nullable String getHeader(String name) {
 			return this.headers.getFirst(name);
 		}
 
@@ -284,7 +287,7 @@ public class FilterInvocation {
 		}
 
 		@Override
-		public String getParameter(String name) {
+		public @Nullable String getParameter(String name) {
 			String[] array = this.parameters.get(name);
 			return (array != null && array.length > 0) ? array[0] : null;
 		}
@@ -300,7 +303,7 @@ public class FilterInvocation {
 		}
 
 		@Override
-		public String[] getParameterValues(String name) {
+		public String @Nullable [] getParameterValues(String name) {
 			return this.parameters.get(name);
 		}
 
@@ -309,11 +312,11 @@ public class FilterInvocation {
 		}
 
 		@Override
-		public ServletContext getServletContext() {
+		public @Nullable ServletContext getServletContext() {
 			return this.servletContext;
 		}
 
-		void setServletContext(ServletContext servletContext) {
+		void setServletContext(@Nullable ServletContext servletContext) {
 			this.servletContext = servletContext;
 		}
 

+ 5 - 2
web/src/main/java/org/springframework/security/web/ObservationFilterChainDecorator.java

@@ -37,6 +37,8 @@ import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpServletRequest;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.util.StringUtils;
@@ -49,6 +51,7 @@ import org.springframework.util.StringUtils;
  * @author Nikita Konev
  * @since 6.0
  */
+@NullUnmarked // https://github.com/spring-projects/spring-security/issues/17815
 public final class ObservationFilterChainDecorator implements FilterChainProxy.FilterChainDecorator {
 
 	private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
@@ -507,7 +510,7 @@ public final class ObservationFilterChainDecorator implements FilterChainProxy.F
 
 		private final String filterSection;
 
-		private String filterName;
+		private @Nullable String filterName;
 
 		private int chainPosition;
 
@@ -530,7 +533,7 @@ public final class ObservationFilterChainDecorator implements FilterChainProxy.F
 			return this.filterSection;
 		}
 
-		String getFilterName() {
+		@Nullable String getFilterName() {
 			return this.filterName;
 		}
 

+ 4 - 2
web/src/main/java/org/springframework/security/web/PortMapper.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.web;
 
+import org.jspecify.annotations.Nullable;
+
 /**
  * <code>PortMapper</code> implementations provide callers with information about which
  * HTTP ports are associated with which HTTPS ports on the system, and vice versa.
@@ -32,7 +34,7 @@ public interface PortMapper {
 	 * @param httpsPort
 	 * @return the HTTP port or <code>null</code> if unknown
 	 */
-	Integer lookupHttpPort(Integer httpsPort);
+	@Nullable Integer lookupHttpPort(Integer httpsPort);
 
 	/**
 	 * Locates the HTTPS port associated with the specified HTTP port.
@@ -42,6 +44,6 @@ public interface PortMapper {
 	 * @param httpPort
 	 * @return the HTTPS port or <code>null</code> if unknown
 	 */
-	Integer lookupHttpsPort(Integer httpPort);
+	@Nullable Integer lookupHttpsPort(Integer httpPort);
 
 }

+ 4 - 2
web/src/main/java/org/springframework/security/web/PortMapperImpl.java

@@ -19,6 +19,8 @@ package org.springframework.security.web;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.util.Assert;
 
 /**
@@ -50,7 +52,7 @@ public class PortMapperImpl implements PortMapper {
 	}
 
 	@Override
-	public Integer lookupHttpPort(Integer httpsPort) {
+	public @Nullable Integer lookupHttpPort(Integer httpsPort) {
 		for (Integer httpPort : this.httpsPortMappings.keySet()) {
 			if (this.httpsPortMappings.get(httpPort).equals(httpsPort)) {
 				return httpPort;
@@ -60,7 +62,7 @@ public class PortMapperImpl implements PortMapper {
 	}
 
 	@Override
-	public Integer lookupHttpsPort(Integer httpPort) {
+	public @Nullable Integer lookupHttpsPort(Integer httpPort) {
 		return this.httpsPortMappings.get(httpPort);
 	}
 

+ 2 - 1
web/src/main/java/org/springframework/security/web/PortResolverImpl.java

@@ -19,6 +19,7 @@ package org.springframework.security.web;
 import java.util.Locale;
 
 import jakarta.servlet.ServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.util.Assert;
 
@@ -54,7 +55,7 @@ public class PortResolverImpl implements PortResolver {
 		return (mappedPort != null) ? mappedPort : serverPort;
 	}
 
-	private Integer getMappedPort(int serverPort, String scheme) {
+	private @Nullable Integer getMappedPort(int serverPort, String scheme) {
 		if ("http".equals(scheme)) {
 			return this.portMapper.lookupHttpPort(serverPort);
 		}

+ 2 - 1
web/src/main/java/org/springframework/security/web/access/AccessDeniedHandlerImpl.java

@@ -23,6 +23,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.http.HttpStatus;
@@ -47,7 +48,7 @@ public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
 
 	protected static final Log logger = LogFactory.getLog(AccessDeniedHandlerImpl.class);
 
-	private String errorPage;
+	private @Nullable String errorPage;
 
 	@Override
 	public void handle(HttpServletRequest request, HttpServletResponse response,

+ 4 - 2
web/src/main/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluator.java

@@ -18,6 +18,7 @@ package org.springframework.security.web.access;
 
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.authorization.AuthorizationResult;
@@ -38,7 +39,7 @@ public final class AuthorizationManagerWebInvocationPrivilegeEvaluator
 
 	private final AuthorizationManager<HttpServletRequest> authorizationManager;
 
-	private ServletContext servletContext;
+	private @Nullable ServletContext servletContext;
 
 	private HttpServletRequestTransformer requestTransformer = HttpServletRequestTransformer.IDENTITY;
 
@@ -54,7 +55,8 @@ public final class AuthorizationManagerWebInvocationPrivilegeEvaluator
 	}
 
 	@Override
-	public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
+	public boolean isAllowed(@Nullable String contextPath, String uri, @Nullable String method,
+			Authentication authentication) {
 		FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext);
 		HttpServletRequest httpRequest = this.requestTransformer.transform(filterInvocation.getHttpRequest());
 		AuthorizationResult result = this.authorizationManager.authorize(() -> authentication, httpRequest);

+ 4 - 2
web/src/main/java/org/springframework/security/web/access/DefaultWebInvocationPrivilegeEvaluator.java

@@ -21,6 +21,7 @@ import java.util.Collection;
 import jakarta.servlet.ServletContext;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.access.AccessDeniedException;
@@ -46,7 +47,7 @@ public class DefaultWebInvocationPrivilegeEvaluator implements WebInvocationPriv
 
 	private final AbstractSecurityInterceptor securityInterceptor;
 
-	private ServletContext servletContext;
+	private @Nullable ServletContext servletContext;
 
 	public DefaultWebInvocationPrivilegeEvaluator(AbstractSecurityInterceptor securityInterceptor) {
 		Assert.notNull(securityInterceptor, "SecurityInterceptor cannot be null");
@@ -86,7 +87,8 @@ public class DefaultWebInvocationPrivilegeEvaluator implements WebInvocationPriv
 	 * @return true if access is allowed, false if denied
 	 */
 	@Override
-	public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
+	public boolean isAllowed(@Nullable String contextPath, String uri, @Nullable String method,
+			Authentication authentication) {
 		Assert.notNull(uri, "uri parameter is required");
 		FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext);
 		Collection<ConfigAttribute> attributes = this.securityInterceptor.obtainSecurityMetadataSource()

+ 2 - 1
web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java

@@ -24,6 +24,7 @@ import jakarta.servlet.ServletRequest;
 import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.context.MessageSource;
 import org.springframework.context.MessageSourceAware;
@@ -169,7 +170,7 @@ public class ExceptionTranslationFilter extends GenericFilterBean implements Mes
 	}
 
 	private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,
-			FilterChain chain, RuntimeException exception) throws IOException, ServletException {
+			FilterChain chain, @Nullable RuntimeException exception) throws IOException, ServletException {
 		if (exception instanceof AuthenticationException) {
 			handleAuthenticationException(request, response, chain, (AuthenticationException) exception);
 		}

+ 2 - 1
web/src/main/java/org/springframework/security/web/access/PathPatternRequestTransformer.java

@@ -21,6 +21,7 @@ import java.util.Map;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletRequestWrapper;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
 import org.springframework.web.util.ServletRequestPathUtils;
@@ -51,7 +52,7 @@ public final class PathPatternRequestTransformer
 		}
 
 		@Override
-		public Object getAttribute(String name) {
+		public @Nullable Object getAttribute(String name) {
 			return this.attributes.get(name);
 		}
 

+ 4 - 2
web/src/main/java/org/springframework/security/web/access/RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.java

@@ -21,6 +21,7 @@ import java.util.List;
 
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.authorization.AuthorizationManager;
 import org.springframework.security.core.Authentication;
@@ -46,7 +47,7 @@ public final class RequestMatcherDelegatingWebInvocationPrivilegeEvaluator
 
 	private final List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> delegates;
 
-	private ServletContext servletContext;
+	private @Nullable ServletContext servletContext;
 
 	public RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
 			List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries) {
@@ -119,7 +120,8 @@ public final class RequestMatcherDelegatingWebInvocationPrivilegeEvaluator
 		return true;
 	}
 
-	private List<WebInvocationPrivilegeEvaluator> getDelegate(String contextPath, String uri, String method) {
+	private List<WebInvocationPrivilegeEvaluator> getDelegate(@Nullable String contextPath, String uri,
+			@Nullable String method) {
 		FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext);
 		HttpServletRequest request = filterInvocation.getHttpRequest();
 		for (RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate : this.delegates) {

+ 2 - 1
web/src/main/java/org/springframework/security/web/access/channel/AbstractRetryEntryPoint.java

@@ -22,6 +22,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.web.DefaultRedirectStrategy;
@@ -79,7 +80,7 @@ public abstract class AbstractRetryEntryPoint implements ChannelEntryPoint {
 		this.redirectStrategy.sendRedirect(request, response, redirectUrl);
 	}
 
-	protected abstract Integer getMappedPort(Integer mapFromPort);
+	protected abstract @Nullable Integer getMappedPort(Integer mapFromPort);
 
 	protected final PortMapper getPortMapper() {
 		return this.portMapper;

+ 4 - 1
web/src/main/java/org/springframework/security/web/access/channel/ChannelDecisionManagerImpl.java

@@ -22,6 +22,8 @@ import java.util.Collection;
 import java.util.List;
 
 import jakarta.servlet.ServletException;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.security.access.ConfigAttribute;
@@ -49,6 +51,7 @@ import org.springframework.util.Assert;
  * {@link RequestMatcher} for any sophisticated decision-making
  */
 @Deprecated
+@NullUnmarked
 public class ChannelDecisionManagerImpl implements ChannelDecisionManager, InitializingBean {
 
 	public static final String ANY_CHANNEL = "ANY_CHANNEL";
@@ -76,7 +79,7 @@ public class ChannelDecisionManagerImpl implements ChannelDecisionManager, Initi
 		}
 	}
 
-	protected List<ChannelProcessor> getChannelProcessors() {
+	protected @Nullable List<ChannelProcessor> getChannelProcessors() {
 		return this.channelProcessors;
 	}
 

+ 7 - 2
web/src/main/java/org/springframework/security/web/access/channel/ChannelProcessingFilter.java

@@ -27,6 +27,7 @@ import jakarta.servlet.ServletRequest;
 import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.access.ConfigAttribute;
@@ -88,8 +89,10 @@ import org.springframework.web.filter.GenericFilterBean;
 @Deprecated
 public class ChannelProcessingFilter extends GenericFilterBean {
 
+	@SuppressWarnings("NullAway.Init")
 	private ChannelDecisionManager channelDecisionManager;
 
+	@SuppressWarnings("NullAway.Init")
 	private FilterInvocationSecurityMetadataSource securityMetadataSource;
 
 	@Override
@@ -128,14 +131,16 @@ public class ChannelProcessingFilter extends GenericFilterBean {
 		if (attributes != null) {
 			this.logger.debug(LogMessage.format("Request: %s; ConfigAttributes: %s", filterInvocation, attributes));
 			this.channelDecisionManager.decide(filterInvocation, attributes);
-			if (filterInvocation.getResponse().isCommitted()) {
+			@Nullable HttpServletResponse channelResponse = filterInvocation.getResponse();
+			Assert.notNull(channelResponse, "HttpServletResponse is required");
+			if (channelResponse.isCommitted()) {
 				return;
 			}
 		}
 		chain.doFilter(request, response);
 	}
 
-	protected ChannelDecisionManager getChannelDecisionManager() {
+	protected @Nullable ChannelDecisionManager getChannelDecisionManager() {
 		return this.channelDecisionManager;
 	}
 

+ 5 - 1
web/src/main/java/org/springframework/security/web/access/channel/InsecureChannelProcessor.java

@@ -20,6 +20,8 @@ import java.io.IOException;
 import java.util.Collection;
 
 import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.security.access.ConfigAttribute;
@@ -63,7 +65,9 @@ public class InsecureChannelProcessor implements InitializingBean, ChannelProces
 		for (ConfigAttribute attribute : config) {
 			if (supports(attribute)) {
 				if (invocation.getHttpRequest().isSecure()) {
-					this.entryPoint.commence(invocation.getRequest(), invocation.getResponse());
+					@Nullable HttpServletResponse response = invocation.getResponse();
+					Assert.notNull(response, "HttpServletResponse required");
+					this.entryPoint.commence(invocation.getRequest(), response);
 				}
 			}
 		}

+ 3 - 1
web/src/main/java/org/springframework/security/web/access/channel/RetryWithHttpEntryPoint.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.access.channel;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.web.PortMapper;
 
 /**
@@ -38,7 +40,7 @@ public class RetryWithHttpEntryPoint extends AbstractRetryEntryPoint {
 	}
 
 	@Override
-	protected Integer getMappedPort(Integer mapFromPort) {
+	protected @Nullable Integer getMappedPort(Integer mapFromPort) {
 		return getPortMapper().lookupHttpPort(mapFromPort);
 	}
 

+ 3 - 1
web/src/main/java/org/springframework/security/web/access/channel/RetryWithHttpsEntryPoint.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.access.channel;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.web.PortMapper;
 
 /**
@@ -39,7 +41,7 @@ public class RetryWithHttpsEntryPoint extends AbstractRetryEntryPoint {
 	}
 
 	@Override
-	protected Integer getMappedPort(Integer mapFromPort) {
+	protected @Nullable Integer getMappedPort(Integer mapFromPort) {
 		return getPortMapper().lookupHttpsPort(mapFromPort);
 	}
 

+ 4 - 1
web/src/main/java/org/springframework/security/web/access/channel/SecureChannelProcessor.java

@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.util.Collection;
 
 import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletResponse;
 
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.security.access.ConfigAttribute;
@@ -63,7 +64,9 @@ public class SecureChannelProcessor implements InitializingBean, ChannelProcesso
 		for (ConfigAttribute attribute : config) {
 			if (supports(attribute)) {
 				if (!invocation.getHttpRequest().isSecure()) {
-					this.entryPoint.commence(invocation.getRequest(), invocation.getResponse());
+					HttpServletResponse response = invocation.getResponse();
+					Assert.notNull(response, "HttpServletResponse is required");
+					this.entryPoint.commence(invocation.getRequest(), response);
 				}
 			}
 		}

+ 3 - 0
web/src/main/java/org/springframework/security/web/access/channel/package-info.java

@@ -19,4 +19,7 @@
  * <p>
  * Most commonly used to enforce that requests are submitted over HTTP or HTTPS.
  */
+@NullMarked
 package org.springframework.security.web.access.channel;
+
+import org.jspecify.annotations.NullMarked;

+ 3 - 2
web/src/main/java/org/springframework/security/web/access/expression/AbstractVariableEvaluationContextPostProcessor.java

@@ -19,6 +19,7 @@ package org.springframework.security.web.access.expression;
 import java.util.Map;
 
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.expression.EvaluationContext;
 import org.springframework.security.web.FilterInvocation;
@@ -53,7 +54,7 @@ abstract class AbstractVariableEvaluationContextPostProcessor
 
 		private final HttpServletRequest request;
 
-		private Map<String, String> variables;
+		private @Nullable Map<String, String> variables;
 
 		VariableEvaluationContext(EvaluationContext delegate, HttpServletRequest request) {
 			super(delegate);
@@ -61,7 +62,7 @@ abstract class AbstractVariableEvaluationContextPostProcessor
 		}
 
 		@Override
-		public Object lookupVariable(String name) {
+		public @Nullable Object lookupVariable(String name) {
 			Object result = super.lookupVariable(name);
 			if (result != null) {
 				return result;

+ 4 - 3
web/src/main/java/org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandler.java

@@ -46,6 +46,7 @@ public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpres
 	private String defaultRolePrefix = "ROLE_";
 
 	@Override
+	@SuppressWarnings("NullAway") // https://github.com/spring-projects/spring-framework/issues/35371
 	public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
 			RequestAuthorizationContext context) {
 		WebSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, context);
@@ -56,13 +57,13 @@ public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpres
 	}
 
 	@Override
-	protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
+	protected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
 			RequestAuthorizationContext context) {
 		return createSecurityExpressionRoot(() -> authentication, context);
 	}
 
-	private WebSecurityExpressionRoot createSecurityExpressionRoot(Supplier<? extends Authentication> authentication,
-			RequestAuthorizationContext context) {
+	private WebSecurityExpressionRoot createSecurityExpressionRoot(
+			Supplier<? extends @Nullable Authentication> authentication, RequestAuthorizationContext context) {
 		WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, context.getRequest());
 		root.setRoleHierarchy(getRoleHierarchy());
 		root.setPermissionEvaluator(getPermissionEvaluator());

+ 3 - 1
web/src/main/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandler.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.access.expression;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
 import org.springframework.security.access.expression.SecurityExpressionHandler;
 import org.springframework.security.access.expression.SecurityExpressionOperations;
@@ -38,7 +40,7 @@ public class DefaultWebSecurityExpressionHandler extends AbstractSecurityExpress
 	private String defaultRolePrefix = "ROLE_";
 
 	@Override
-	protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
+	protected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
 			FilterInvocation fi) {
 		WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, fi);
 		root.setPermissionEvaluator(getPermissionEvaluator());

+ 5 - 3
web/src/main/java/org/springframework/security/web/access/expression/DelegatingEvaluationContext.java

@@ -18,6 +18,8 @@ package org.springframework.security.web.access.expression;
 
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.expression.BeanResolver;
 import org.springframework.expression.ConstructorResolver;
 import org.springframework.expression.EvaluationContext;
@@ -84,17 +86,17 @@ class DelegatingEvaluationContext implements EvaluationContext {
 	}
 
 	@Override
-	public BeanResolver getBeanResolver() {
+	public @Nullable BeanResolver getBeanResolver() {
 		return this.delegate.getBeanResolver();
 	}
 
 	@Override
-	public void setVariable(String name, Object value) {
+	public void setVariable(String name, @Nullable Object value) {
 		this.delegate.setVariable(name, value);
 	}
 
 	@Override
-	public Object lookupVariable(String name) {
+	public @Nullable Object lookupVariable(String name) {
 		return this.delegate.lookupVariable(name);
 	}
 

+ 3 - 0
web/src/main/java/org/springframework/security/web/access/expression/WebExpressionConfigAttribute.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.access.expression;
 
+import org.jspecify.annotations.NullUnmarked;
+
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
 import org.springframework.security.access.ConfigAttribute;
@@ -32,6 +34,7 @@ import org.springframework.security.web.FilterInvocation;
  * {@link AuthorizationManager}.
  */
 @Deprecated
+@NullUnmarked
 class WebExpressionConfigAttribute implements ConfigAttribute, EvaluationContextPostProcessor<FilterInvocation> {
 
 	private final Expression authorizeExpression;

+ 2 - 1
web/src/main/java/org/springframework/security/web/access/expression/WebExpressionVoter.java

@@ -20,6 +20,7 @@ import java.util.Collection;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.expression.EvaluationContext;
 import org.springframework.security.access.AccessDecisionVoter;
@@ -66,7 +67,7 @@ public class WebExpressionVoter implements AccessDecisionVoter<FilterInvocation>
 		return ACCESS_DENIED;
 	}
 
-	private WebExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {
+	private @Nullable WebExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {
 		for (ConfigAttribute attribute : attributes) {
 			if (attribute instanceof WebExpressionConfigAttribute) {
 				return (WebExpressionConfigAttribute) attribute;

+ 5 - 2
web/src/main/java/org/springframework/security/web/access/expression/WebSecurityExpressionRoot.java

@@ -19,6 +19,7 @@ package org.springframework.security.web.access.expression;
 import java.util.function.Supplier;
 
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.core.Authentication;
@@ -37,7 +38,7 @@ public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
 	 */
 	public final HttpServletRequest request;
 
-	public WebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
+	public WebSecurityExpressionRoot(@Nullable Authentication a, FilterInvocation fi) {
 		this(() -> a, fi.getRequest());
 	}
 
@@ -48,7 +49,9 @@ public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
 	 * @param request the {@link HttpServletRequest} to use
 	 * @since 5.8
 	 */
-	public WebSecurityExpressionRoot(Supplier<? extends Authentication> authentication, HttpServletRequest request) {
+	@SuppressWarnings("NullAway") // https://github.com/uber/NullAway/issues/1246
+	public WebSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication,
+			HttpServletRequest request) {
 		super(authentication);
 		this.request = request;
 	}

+ 3 - 0
web/src/main/java/org/springframework/security/web/access/expression/package-info.java

@@ -17,4 +17,7 @@
 /**
  * Implementation of web security expressions.
  */
+@NullMarked
 package org.springframework.security.web.access.expression;
+
+import org.jspecify.annotations.NullMarked;

+ 2 - 1
web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java

@@ -26,6 +26,7 @@ import jakarta.servlet.ServletRequest;
 import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
@@ -202,7 +203,7 @@ public class AuthorizationFilter extends GenericFilterBean {
 
 		@Override
 		public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
-				AuthorizationResult result) {
+				@Nullable AuthorizationResult result) {
 		}
 
 	}

+ 2 - 1
web/src/main/java/org/springframework/security/web/access/intercept/DefaultFilterInvocationSecurityMetadataSource.java

@@ -17,6 +17,7 @@
 package org.springframework.security.web.access.intercept;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -99,7 +100,7 @@ public class DefaultFilterInvocationSecurityMetadataSource implements FilterInvo
 				}
 			}
 		}
-		return null;
+		return Collections.emptyList();
 	}
 
 	@Override

+ 4 - 3
web/src/main/java/org/springframework/security/web/access/intercept/FilterSecurityInterceptor.java

@@ -24,6 +24,7 @@ import jakarta.servlet.FilterConfig;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.ServletRequest;
 import jakarta.servlet.ServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.access.SecurityMetadataSource;
 import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
@@ -48,7 +49,7 @@ public class FilterSecurityInterceptor extends AbstractSecurityInterceptor imple
 
 	private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";
 
-	private FilterInvocationSecurityMetadataSource securityMetadataSource;
+	private @Nullable FilterInvocationSecurityMetadataSource securityMetadataSource;
 
 	private boolean observeOncePerRequest = false;
 
@@ -83,12 +84,12 @@ public class FilterSecurityInterceptor extends AbstractSecurityInterceptor imple
 		invoke(new FilterInvocation(request, response, chain));
 	}
 
-	public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
+	public @Nullable FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
 		return this.securityMetadataSource;
 	}
 
 	@Override
-	public SecurityMetadataSource obtainSecurityMetadataSource() {
+	public @Nullable SecurityMetadataSource obtainSecurityMetadataSource() {
 		return this.securityMetadataSource;
 	}
 

+ 5 - 3
web/src/main/java/org/springframework/security/web/access/intercept/RequestKey.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.access.intercept;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.util.Assert;
 
 /**
@@ -26,13 +28,13 @@ public class RequestKey {
 
 	private final String url;
 
-	private final String method;
+	private final @Nullable String method;
 
 	public RequestKey(String url) {
 		this(url, null);
 	}
 
-	public RequestKey(String url, String method) {
+	public RequestKey(String url, @Nullable String method) {
 		Assert.notNull(url, "url cannot be null");
 		this.url = url;
 		this.method = method;
@@ -42,7 +44,7 @@ public class RequestKey {
 		return this.url;
 	}
 
-	String getMethod() {
+	@Nullable String getMethod() {
 		return this.method;
 	}
 

+ 1 - 1
web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java

@@ -64,7 +64,7 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
 	}
 
 	@Override
-	public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
+	public @Nullable AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
 			HttpServletRequest request) {
 		if (this.logger.isTraceEnabled()) {
 			this.logger.trace(LogMessage.format("Authorizing %s", requestLine(request)));

+ 3 - 0
web/src/main/java/org/springframework/security/web/access/intercept/package-info.java

@@ -17,4 +17,7 @@
 /**
  * Enforcement of security for HTTP requests, typically by the URL requested.
  */
+@NullMarked
 package org.springframework.security.web.access.intercept;
+
+import org.jspecify.annotations.NullMarked;

+ 3 - 0
web/src/main/java/org/springframework/security/web/access/package-info.java

@@ -17,4 +17,7 @@
 /**
  * Access-control related classes and packages.
  */
+@NullMarked
 package org.springframework.security.web.access;
+
+import org.jspecify.annotations.NullMarked;

+ 3 - 1
web/src/main/java/org/springframework/security/web/aot/hint/WebMvcSecurityRuntimeHints.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.aot.hint;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.aot.hint.MemberCategory;
 import org.springframework.aot.hint.RuntimeHints;
 import org.springframework.aot.hint.RuntimeHintsRegistrar;
@@ -33,7 +35,7 @@ import org.springframework.security.web.access.expression.WebSecurityExpressionR
 class WebMvcSecurityRuntimeHints implements RuntimeHintsRegistrar {
 
 	@Override
-	public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+	public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
 		hints.reflection()
 			.registerType(WebSecurityExpressionRoot.class, (builder) -> builder
 				.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS));

+ 23 - 0
web/src/main/java/org/springframework/security/web/aot/hint/package-info.java

@@ -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.
+ */
+
+/**
+ * Runtime hints for AOT web package.
+ */
+@NullMarked
+package org.springframework.security.web.aot.hint;
+
+import org.jspecify.annotations.NullMarked;

+ 6 - 4
web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java

@@ -24,6 +24,7 @@ import jakarta.servlet.ServletRequest;
 import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationEventPublisherAware;
@@ -120,7 +121,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
 	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
 		.getContextHolderStrategy();
 
-	protected ApplicationEventPublisher eventPublisher;
+	@Nullable protected ApplicationEventPublisher eventPublisher;
 
 	protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
 
@@ -129,6 +130,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
 				"Please either configure an AuthenticationConverter or override attemptAuthentication when extending AbstractAuthenticationProcessingFilter");
 	};
 
+	@SuppressWarnings("NullAway.Init")
 	private AuthenticationManager authenticationManager;
 
 	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
@@ -155,7 +157,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
 	 * @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>.
 	 */
 	protected AbstractAuthenticationProcessingFilter(String defaultFilterProcessesUrl) {
-		setFilterProcessesUrl(defaultFilterProcessesUrl);
+		this(pathPattern(defaultFilterProcessesUrl));
 	}
 
 	/**
@@ -177,7 +179,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
 	 */
 	protected AbstractAuthenticationProcessingFilter(String defaultFilterProcessesUrl,
 			AuthenticationManager authenticationManager) {
-		setFilterProcessesUrl(defaultFilterProcessesUrl);
+		this(pathPattern(defaultFilterProcessesUrl));
 		setAuthenticationManager(authenticationManager);
 	}
 
@@ -305,7 +307,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
 	 * @return the authenticated user token, or null if authentication is incomplete.
 	 * @throws AuthenticationException if authentication fails.
 	 */
-	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
+	public @Nullable Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
 			throws AuthenticationException, IOException, ServletException {
 		Authentication authentication = this.authenticationConverter.convert(request);
 		if (authentication == null) {

+ 8 - 7
web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationTargetUrlRequestHandler.java

@@ -23,6 +23,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.core.Authentication;
@@ -62,7 +63,7 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler {
 
 	protected final Log logger = LogFactory.getLog(this.getClass());
 
-	private String targetUrlParameter = null;
+	private @Nullable String targetUrlParameter = null;
 
 	private String defaultTargetUrl = "/";
 
@@ -81,8 +82,8 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler {
 	 * <p>
 	 * The redirect will not be performed if the response has already been committed.
 	 */
-	protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
-			throws IOException, ServletException {
+	protected void handle(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) throws IOException, ServletException {
 		String targetUrl = determineTargetUrl(request, response, authentication);
 		if (response.isCommitted()) {
 			this.logger.debug(LogMessage.format("Did not redirect to %s since response already committed.", targetUrl));
@@ -96,7 +97,7 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler {
 	 * @since 5.2
 	 */
 	protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response,
-			Authentication authentication) {
+			@Nullable Authentication authentication) {
 		return determineTargetUrl(request, response);
 	}
 
@@ -119,7 +120,7 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler {
 		return this.defaultTargetUrl;
 	}
 
-	private String getTargetUrlParameterValue(HttpServletRequest request) {
+	private @Nullable String getTargetUrlParameterValue(HttpServletRequest request) {
 		if (this.targetUrlParameter == null) {
 			return null;
 		}
@@ -133,7 +134,7 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler {
 		return this.defaultTargetUrl;
 	}
 
-	private void trace(String msg, String... msgParts) {
+	private void trace(String msg, @Nullable String... msgParts) {
 		if (this.logger.isTraceEnabled()) {
 			this.logger.trace(LogMessage.format(msg, msgParts));
 		}
@@ -189,7 +190,7 @@ public abstract class AbstractAuthenticationTargetUrlRequestHandler {
 		this.targetUrlParameter = targetUrlParameter;
 	}
 
-	protected String getTargetUrlParameter() {
+	protected @Nullable String getTargetUrlParameter() {
 		return this.targetUrlParameter;
 	}
 

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/AuthenticationConverter.java

@@ -17,6 +17,7 @@
 package org.springframework.security.web.authentication;
 
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.core.Authentication;
@@ -35,6 +36,6 @@ import org.springframework.security.core.AuthenticationException;
  */
 public interface AuthenticationConverter {
 
-	Authentication convert(HttpServletRequest request);
+	@Nullable Authentication convert(HttpServletRequest request);
 
 }

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java

@@ -24,6 +24,7 @@ import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpSession;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.http.HttpStatus;
 import org.springframework.security.authentication.AuthenticationManager;
@@ -218,7 +219,7 @@ public class AuthenticationFilter extends OncePerRequestFilter {
 		this.successHandler.onAuthenticationSuccess(request, response, chain, authentication);
 	}
 
-	private Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
+	private @Nullable Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
 			throws AuthenticationException, ServletException {
 		Authentication authentication = this.authenticationConverter.convert(request);
 		if (authentication == null) {

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationConverter.java

@@ -19,6 +19,7 @@ package org.springframework.security.web.authentication;
 import java.util.List;
 
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.util.Assert;
@@ -46,7 +47,7 @@ public final class DelegatingAuthenticationConverter implements AuthenticationCo
 	}
 
 	@Override
-	public Authentication convert(HttpServletRequest request) {
+	public @Nullable Authentication convert(HttpServletRequest request) {
 		for (AuthenticationConverter delegate : this.delegates) {
 			Authentication authentication = delegate.convert(request);
 			if (authentication != null) {

+ 1 - 0
web/src/main/java/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPoint.java

@@ -66,6 +66,7 @@ public class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPo
 
 	private final LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints;
 
+	@SuppressWarnings("NullAway.Init")
 	private AuthenticationEntryPoint defaultEntryPoint;
 
 	public DelegatingAuthenticationEntryPoint(LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints) {

+ 3 - 1
web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java

@@ -24,6 +24,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.core.log.LogMessage;
@@ -187,7 +188,8 @@ public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoin
 	 * Builds a URL to redirect the supplied request to HTTPS. Used to redirect the
 	 * current request to HTTPS, before doing a forward to the login page.
 	 */
-	protected String buildHttpsRedirectUrlForRequest(HttpServletRequest request) throws IOException, ServletException {
+	protected @Nullable String buildHttpsRedirectUrlForRequest(HttpServletRequest request)
+			throws IOException, ServletException {
 		int serverPort = this.portResolver.getServerPort(request);
 		Integer httpsPort = this.portMapper.lookupHttpsPort(serverPort);
 		if (httpsPort != null) {

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/NullRememberMeServices.java

@@ -18,6 +18,7 @@ package org.springframework.security.web.authentication;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 
@@ -32,7 +33,7 @@ import org.springframework.security.core.Authentication;
 public class NullRememberMeServices implements RememberMeServices {
 
 	@Override
-	public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
+	public @Nullable Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
 		return null;
 	}
 

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/RememberMeServices.java

@@ -18,6 +18,7 @@ package org.springframework.security.web.authentication;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 
@@ -69,7 +70,7 @@ public interface RememberMeServices {
 	 * @return a valid authentication object, or <code>null</code> if the request should
 	 * not be authenticated
 	 */
-	Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);
+	@Nullable Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);
 
 	/**
 	 * Called whenever an interactive authentication attempt was made, but the credentials

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/SimpleUrlAuthenticationFailureHandler.java

@@ -24,6 +24,7 @@ import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpSession;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.http.HttpStatus;
 import org.springframework.security.core.AuthenticationException;
@@ -50,7 +51,7 @@ public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFail
 
 	protected final Log logger = LogFactory.getLog(getClass());
 
-	private String defaultFailureUrl;
+	private @Nullable String defaultFailureUrl;
 
 	private boolean forwardToDestination = false;
 

+ 5 - 4
web/src/main/java/org/springframework/security/web/authentication/WebAuthenticationDetails.java

@@ -21,6 +21,7 @@ import java.util.Objects;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpSession;
+import org.jspecify.annotations.Nullable;
 
 /**
  * A holder of selected HTTP details related to a web authentication request.
@@ -34,7 +35,7 @@ public class WebAuthenticationDetails implements Serializable {
 
 	private final String remoteAddress;
 
-	private final String sessionId;
+	private final @Nullable String sessionId;
 
 	/**
 	 * Records the remote address and will also set the session Id if a session already
@@ -51,12 +52,12 @@ public class WebAuthenticationDetails implements Serializable {
 	 * @param sessionId session id
 	 * @since 5.7
 	 */
-	public WebAuthenticationDetails(String remoteAddress, String sessionId) {
+	public WebAuthenticationDetails(String remoteAddress, @Nullable String sessionId) {
 		this.remoteAddress = remoteAddress;
 		this.sessionId = sessionId;
 	}
 
-	private static String extractSessionId(HttpServletRequest request) {
+	private static @Nullable String extractSessionId(HttpServletRequest request) {
 		HttpSession session = request.getSession(false);
 		return (session != null) ? session.getId() : null;
 	}
@@ -74,7 +75,7 @@ public class WebAuthenticationDetails implements Serializable {
 	 * from.
 	 * @return the session ID
 	 */
-	public String getSessionId() {
+	public @Nullable String getSessionId() {
 		return this.sessionId;
 	}
 

+ 3 - 1
web/src/main/java/org/springframework/security/web/authentication/logout/CompositeLogoutHandler.java

@@ -21,6 +21,7 @@ import java.util.List;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.util.Assert;
@@ -49,7 +50,8 @@ public final class CompositeLogoutHandler implements LogoutHandler {
 	}
 
 	@Override
-	public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+	public void logout(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) {
 		for (LogoutHandler handler : this.logoutHandlers) {
 			handler.logout(request, response, authentication);
 		}

+ 3 - 1
web/src/main/java/org/springframework/security/web/authentication/logout/CookieClearingLogoutHandler.java

@@ -23,6 +23,7 @@ import java.util.function.Function;
 import jakarta.servlet.http.Cookie;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.util.Assert;
@@ -72,7 +73,8 @@ public final class CookieClearingLogoutHandler implements LogoutHandler {
 	}
 
 	@Override
-	public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+	public void logout(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) {
 		this.cookiesToClear.forEach((f) -> response.addCookie(f.apply(request)));
 	}
 

+ 4 - 3
web/src/main/java/org/springframework/security/web/authentication/logout/DelegatingLogoutSuccessHandler.java

@@ -23,6 +23,7 @@ import java.util.Map;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -39,7 +40,7 @@ public class DelegatingLogoutSuccessHandler implements LogoutSuccessHandler {
 
 	private final LinkedHashMap<RequestMatcher, LogoutSuccessHandler> matcherToHandler;
 
-	private LogoutSuccessHandler defaultLogoutSuccessHandler;
+	private @Nullable LogoutSuccessHandler defaultLogoutSuccessHandler;
 
 	public DelegatingLogoutSuccessHandler(LinkedHashMap<RequestMatcher, LogoutSuccessHandler> matcherToHandler) {
 		Assert.notEmpty(matcherToHandler, "matcherToHandler cannot be null");
@@ -47,8 +48,8 @@ public class DelegatingLogoutSuccessHandler implements LogoutSuccessHandler {
 	}
 
 	@Override
-	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
-			throws IOException, ServletException {
+	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) throws IOException, ServletException {
 		for (Map.Entry<RequestMatcher, LogoutSuccessHandler> entry : this.matcherToHandler.entrySet()) {
 			RequestMatcher matcher = entry.getKey();
 			if (matcher.matches(request)) {

+ 3 - 2
web/src/main/java/org/springframework/security/web/authentication/logout/ForwardLogoutSuccessHandler.java

@@ -21,6 +21,7 @@ import java.io.IOException;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.util.UrlUtils;
@@ -47,8 +48,8 @@ public class ForwardLogoutSuccessHandler implements LogoutSuccessHandler {
 	}
 
 	@Override
-	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
-			throws IOException, ServletException {
+	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) throws IOException, ServletException {
 		request.getRequestDispatcher(this.targetUrl).forward(request, response);
 	}
 

+ 3 - 1
web/src/main/java/org/springframework/security/web/authentication/logout/HeaderWriterLogoutHandler.java

@@ -18,6 +18,7 @@ package org.springframework.security.web.authentication.logout;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.header.HeaderWriter;
@@ -42,7 +43,8 @@ public final class HeaderWriterLogoutHandler implements LogoutHandler {
 	}
 
 	@Override
-	public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+	public void logout(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) {
 		this.headerWriter.writeHeaders(request, response);
 	}
 

+ 3 - 2
web/src/main/java/org/springframework/security/web/authentication/logout/HttpStatusReturningLogoutSuccessHandler.java

@@ -20,6 +20,7 @@ import java.io.IOException;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.http.HttpStatus;
 import org.springframework.security.core.Authentication;
@@ -61,8 +62,8 @@ public class HttpStatusReturningLogoutSuccessHandler implements LogoutSuccessHan
 	 * . Sets the status on the {@link HttpServletResponse}.
 	 */
 	@Override
-	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
-			throws IOException {
+	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) throws IOException {
 		response.setStatus(this.httpStatusToReturn.value());
 		response.getWriter().flush();
 	}

+ 2 - 0
web/src/main/java/org/springframework/security/web/authentication/logout/LogoutFilter.java

@@ -69,6 +69,7 @@ public class LogoutFilter extends GenericFilterBean {
 	 * intended to perform the actual logout functionality (such as clearing the security
 	 * context, invalidating the session, etc.).
 	 */
+	@SuppressWarnings("NullAway") // Dataflow analysis limitation
 	public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler... handlers) {
 		this.handler = new CompositeLogoutHandler(handlers);
 		Assert.notNull(logoutSuccessHandler, "logoutSuccessHandler cannot be null");
@@ -76,6 +77,7 @@ public class LogoutFilter extends GenericFilterBean {
 		setFilterProcessesUrl("/logout");
 	}
 
+	@SuppressWarnings("NullAway") // Dataflow analysis limitation
 	public LogoutFilter(String logoutSuccessUrl, LogoutHandler... handlers) {
 		this.handler = new CompositeLogoutHandler(handlers);
 		Assert.isTrue(!StringUtils.hasLength(logoutSuccessUrl) || UrlUtils.isValidRedirectUrl(logoutSuccessUrl),

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/logout/LogoutHandler.java

@@ -18,6 +18,7 @@ package org.springframework.security.web.authentication.logout;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 
@@ -37,6 +38,6 @@ public interface LogoutHandler {
 	 * @param response the HTTP response
 	 * @param authentication the current principal details
 	 */
-	void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication);
+	void logout(HttpServletRequest request, HttpServletResponse response, @Nullable Authentication authentication);
 
 }

+ 4 - 2
web/src/main/java/org/springframework/security/web/authentication/logout/LogoutSuccessEventPublishingLogoutHandler.java

@@ -18,6 +18,7 @@ package org.springframework.security.web.authentication.logout;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationEventPublisherAware;
@@ -32,10 +33,11 @@ import org.springframework.security.core.Authentication;
  */
 public final class LogoutSuccessEventPublishingLogoutHandler implements LogoutHandler, ApplicationEventPublisherAware {
 
-	private ApplicationEventPublisher eventPublisher;
+	private @Nullable ApplicationEventPublisher eventPublisher;
 
 	@Override
-	public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+	public void logout(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) {
 		if (this.eventPublisher == null) {
 			return;
 		}

+ 3 - 2
web/src/main/java/org/springframework/security/web/authentication/logout/LogoutSuccessHandler.java

@@ -21,6 +21,7 @@ import java.io.IOException;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 
@@ -37,7 +38,7 @@ import org.springframework.security.core.Authentication;
  */
 public interface LogoutSuccessHandler {
 
-	void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
-			throws IOException, ServletException;
+	void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) throws IOException, ServletException;
 
 }

+ 3 - 1
web/src/main/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.java

@@ -21,6 +21,7 @@ import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpSession;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.core.Authentication;
@@ -64,7 +65,8 @@ public class SecurityContextLogoutHandler implements LogoutHandler {
 	 * @param authentication not used (can be <code>null</code>)
 	 */
 	@Override
-	public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
+	public void logout(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) {
 		Assert.notNull(request, "HttpServletRequest required");
 		if (this.invalidateHttpSession) {
 			HttpSession session = request.getSession(false);

+ 3 - 2
web/src/main/java/org/springframework/security/web/authentication/logout/SimpleUrlLogoutSuccessHandler.java

@@ -21,6 +21,7 @@ import java.io.IOException;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
@@ -36,8 +37,8 @@ public class SimpleUrlLogoutSuccessHandler extends AbstractAuthenticationTargetU
 		implements LogoutSuccessHandler {
 
 	@Override
-	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
-			throws IOException, ServletException {
+	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
+			@Nullable Authentication authentication) throws IOException, ServletException {
 		super.handle(request, response, authentication);
 	}
 

+ 3 - 0
web/src/main/java/org/springframework/security/web/authentication/logout/package-info.java

@@ -17,4 +17,7 @@
 /**
  * Logout functionality based around a filter which handles a specific logout URL.
  */
+@NullMarked
 package org.springframework.security.web.authentication.logout;
+
+import org.jspecify.annotations.NullMarked;

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/ott/DefaultGenerateOneTimeTokenRequestResolver.java

@@ -19,6 +19,7 @@ package org.springframework.security.web.authentication.ott;
 import java.time.Duration;
 
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
 import org.springframework.util.Assert;
@@ -38,7 +39,7 @@ public final class DefaultGenerateOneTimeTokenRequestResolver implements Generat
 	private Duration expiresIn = DEFAULT_EXPIRES_IN;
 
 	@Override
-	public GenerateOneTimeTokenRequest resolve(HttpServletRequest request) {
+	public @Nullable GenerateOneTimeTokenRequest resolve(HttpServletRequest request) {
 		String username = request.getParameter("username");
 		if (!StringUtils.hasText(username)) {
 			return null;

+ 1 - 1
web/src/main/java/org/springframework/security/web/authentication/ott/GenerateOneTimeTokenFilter.java

@@ -74,11 +74,11 @@ public final class GenerateOneTimeTokenFilter extends OncePerRequestFilter {
 			return;
 		}
 		GenerateOneTimeTokenRequest generateRequest = this.requestResolver.resolve(request);
-		OneTimeToken ott = this.tokenService.generate(generateRequest);
 		if (generateRequest == null) {
 			filterChain.doFilter(request, response);
 			return;
 		}
+		OneTimeToken ott = this.tokenService.generate(generateRequest);
 		this.tokenGenerationSuccessHandler.handle(request, response, ott);
 	}
 

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/ott/OneTimeTokenAuthenticationConverter.java

@@ -19,6 +19,7 @@ package org.springframework.security.web.authentication.ott;
 import jakarta.servlet.http.HttpServletRequest;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;
 import org.springframework.security.core.Authentication;
@@ -39,7 +40,7 @@ public class OneTimeTokenAuthenticationConverter implements AuthenticationConver
 	private final Log logger = LogFactory.getLog(getClass());
 
 	@Override
-	public Authentication convert(HttpServletRequest request) {
+	public @Nullable Authentication convert(HttpServletRequest request) {
 		String token = request.getParameter("token");
 		if (!StringUtils.hasText(token)) {
 			this.logger.debug("No token found in request");

+ 23 - 0
web/src/main/java/org/springframework/security/web/authentication/ott/package-info.java

@@ -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.
+ */
+
+/**
+ * Package for One Time Token usage.
+ */
+@NullMarked
+package org.springframework.security.web.authentication.ott;
+
+import org.jspecify.annotations.NullMarked;

+ 3 - 0
web/src/main/java/org/springframework/security/web/authentication/package-info.java

@@ -18,4 +18,7 @@
  * Authentication processing mechanisms, which respond to the submission of authentication
  * credentials using various protocols (eg BASIC, CAS, form login etc).
  */
+@NullMarked
 package org.springframework.security.web.authentication;
+
+import org.jspecify.annotations.NullMarked;

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/password/HaveIBeenPwnedRestApiPasswordChecker.java

@@ -25,6 +25,7 @@ import java.util.Locale;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.authentication.password.CompromisedPasswordChecker;
 import org.springframework.security.authentication.password.CompromisedPasswordDecision;
@@ -60,7 +61,7 @@ public final class HaveIBeenPwnedRestApiPasswordChecker implements CompromisedPa
 	}
 
 	@Override
-	public CompromisedPasswordDecision check(String password) {
+	public CompromisedPasswordDecision check(@Nullable String password) {
 		if (password == null) {
 			return new CompromisedPasswordDecision(false);
 		}

+ 23 - 0
web/src/main/java/org/springframework/security/web/authentication/password/package-info.java

@@ -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.
+ */
+
+/**
+ * Classes for Password APIs.
+ */
+@NullMarked
+package org.springframework.security.web.authentication.password;
+
+import org.jspecify.annotations.NullMarked;

+ 8 - 6
web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java

@@ -25,6 +25,7 @@ import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpSession;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationEventPublisherAware;
@@ -96,11 +97,12 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
 	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
 		.getContextHolderStrategy();
 
-	private ApplicationEventPublisher eventPublisher = null;
+	private @Nullable ApplicationEventPublisher eventPublisher = null;
 
 	private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
 
-	private AuthenticationManager authenticationManager = null;
+	@SuppressWarnings("NullAway.Init")
+	private AuthenticationManager authenticationManager;
 
 	private boolean continueFilterChainOnUnsuccessfulAuthentication = true;
 
@@ -108,9 +110,9 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
 
 	private boolean invalidateSessionOnPrincipalChange = true;
 
-	private AuthenticationSuccessHandler authenticationSuccessHandler = null;
+	private @Nullable AuthenticationSuccessHandler authenticationSuccessHandler = null;
 
-	private AuthenticationFailureHandler authenticationFailureHandler = null;
+	private @Nullable AuthenticationFailureHandler authenticationFailureHandler = null;
 
 	private RequestMatcher requiresAuthenticationRequestMatcher = new PreAuthenticatedProcessingRequestMatcher();
 
@@ -357,14 +359,14 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
 	/**
 	 * Override to extract the principal information from the current request
 	 */
-	protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);
+	protected abstract @Nullable Object getPreAuthenticatedPrincipal(HttpServletRequest request);
 
 	/**
 	 * Override to extract the credentials (if applicable) from the current request.
 	 * Should not return null for a valid principal, though some implementations may
 	 * return a dummy value.
 	 */
-	protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
+	protected abstract @Nullable Object getPreAuthenticatedCredentials(HttpServletRequest request);
 
 	/**
 	 * Request matcher for default auth check logic

+ 3 - 1
web/src/main/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationProvider.java

@@ -18,6 +18,7 @@ package org.springframework.security.web.authentication.preauth;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.core.Ordered;
@@ -51,6 +52,7 @@ public class PreAuthenticatedAuthenticationProvider implements AuthenticationPro
 
 	private static final Log logger = LogFactory.getLog(PreAuthenticatedAuthenticationProvider.class);
 
+	@SuppressWarnings("NullAway.Init")
 	private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> preAuthenticatedUserDetailsService;
 
 	private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
@@ -74,7 +76,7 @@ public class PreAuthenticatedAuthenticationProvider implements AuthenticationPro
 	 * be ignored to allow other providers to authenticate it.
 	 */
 	@Override
-	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+	public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
 		if (!supports(authentication.getClass())) {
 			return null;
 		}

+ 6 - 4
web/src/main/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationToken.java

@@ -18,6 +18,8 @@ package org.springframework.security.web.authentication.preauth;
 
 import java.util.Collection;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.authentication.AbstractAuthenticationToken;
 import org.springframework.security.core.GrantedAuthority;
 
@@ -34,7 +36,7 @@ public class PreAuthenticatedAuthenticationToken extends AbstractAuthenticationT
 
 	private final Object principal;
 
-	private final Object credentials;
+	private final @Nullable Object credentials;
 
 	/**
 	 * Constructor used for an authentication request. The
@@ -43,7 +45,7 @@ public class PreAuthenticatedAuthenticationToken extends AbstractAuthenticationT
 	 * @param aPrincipal The pre-authenticated principal
 	 * @param aCredentials The pre-authenticated credentials
 	 */
-	public PreAuthenticatedAuthenticationToken(Object aPrincipal, Object aCredentials) {
+	public PreAuthenticatedAuthenticationToken(Object aPrincipal, @Nullable Object aCredentials) {
 		super(null);
 		this.principal = aPrincipal;
 		this.credentials = aCredentials;
@@ -56,7 +58,7 @@ public class PreAuthenticatedAuthenticationToken extends AbstractAuthenticationT
 	 * @param aPrincipal The authenticated principal
 	 * @param anAuthorities The granted authorities
 	 */
-	public PreAuthenticatedAuthenticationToken(Object aPrincipal, Object aCredentials,
+	public PreAuthenticatedAuthenticationToken(Object aPrincipal, @Nullable Object aCredentials,
 			Collection<? extends GrantedAuthority> anAuthorities) {
 		super(anAuthorities);
 		this.principal = aPrincipal;
@@ -68,7 +70,7 @@ public class PreAuthenticatedAuthenticationToken extends AbstractAuthenticationT
 	 * Get the credentials
 	 */
 	@Override
-	public Object getCredentials() {
+	public @Nullable Object getCredentials() {
 		return this.credentials;
 	}
 

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/preauth/RequestAttributeAuthenticationFilter.java

@@ -17,6 +17,7 @@
 package org.springframework.security.web.authentication.preauth;
 
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.util.Assert;
 
@@ -46,7 +47,7 @@ public class RequestAttributeAuthenticationFilter extends AbstractPreAuthenticat
 
 	private String principalEnvironmentVariable = "REMOTE_USER";
 
-	private String credentialsEnvironmentVariable;
+	private @Nullable String credentialsEnvironmentVariable;
 
 	private boolean exceptionIfVariableMissing = true;
 

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/preauth/RequestHeaderAuthenticationFilter.java

@@ -17,6 +17,7 @@
 package org.springframework.security.web.authentication.preauth;
 
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.util.Assert;
 
@@ -47,7 +48,7 @@ public class RequestHeaderAuthenticationFilter extends AbstractPreAuthenticatedP
 
 	private String principalRequestHeader = "SM_USER";
 
-	private String credentialsRequestHeader;
+	private @Nullable String credentialsRequestHeader;
 
 	private boolean exceptionIfHeaderMissing = true;
 

+ 1 - 0
web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.java

@@ -52,6 +52,7 @@ public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource implements
 	/**
 	 * The role attributes returned by the configured {@code MappableAttributesRetriever}
 	 */
+	@SuppressWarnings("NullAway.Init")
 	protected Set<String> j2eeMappableRoles;
 
 	protected Attributes2GrantedAuthoritiesMapper j2eeUserRoles2GrantedAuthoritiesMapper = new SimpleAttributes2GrantedAuthoritiesMapper();

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eePreAuthenticatedProcessingFilter.java

@@ -17,6 +17,7 @@
 package org.springframework.security.web.authentication.preauth.j2ee;
 
 import jakarta.servlet.http.HttpServletRequest;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
@@ -35,7 +36,7 @@ public class J2eePreAuthenticatedProcessingFilter extends AbstractPreAuthenticat
 	 * Return the J2EE user name.
 	 */
 	@Override
-	protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
+	protected @Nullable Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
 		Object principal = (httpRequest.getUserPrincipal() != null) ? httpRequest.getUserPrincipal().getName() : null;
 		this.logger.debug(LogMessage.format("PreAuthenticated J2EE principal: %s", principal));
 		return principal;

+ 4 - 2
web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlMappableAttributesRetriever.java

@@ -32,6 +32,7 @@ import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
@@ -60,9 +61,9 @@ public class WebXmlMappableAttributesRetriever
 
 	protected final Log logger = LogFactory.getLog(getClass());
 
-	private ResourceLoader resourceLoader;
+	private @Nullable ResourceLoader resourceLoader;
 
-	private Set<String> mappableAttributes;
+	private Set<String> mappableAttributes = new HashSet<>();
 
 	@Override
 	public void setResourceLoader(ResourceLoader resourceLoader) {
@@ -81,6 +82,7 @@ public class WebXmlMappableAttributesRetriever
 
 	@Override
 	public void afterPropertiesSet() throws Exception {
+		Assert.notNull(this.resourceLoader, "resourceLoader cannot be null");
 		Resource webXml = this.resourceLoader.getResource("/WEB-INF/web.xml");
 		Document doc = getDocument(webXml.getInputStream());
 		NodeList webApp = doc.getElementsByTagName("web-app");

+ 3 - 0
web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/package-info.java

@@ -21,4 +21,7 @@
  * into the security methods exposed by {@code HttpServletRequest} to build
  * {@code Authentication} object for the user.
  */
+@NullMarked
 package org.springframework.security.web.authentication.preauth.j2ee;
+
+import org.jspecify.annotations.NullMarked;

+ 3 - 0
web/src/main/java/org/springframework/security/web/authentication/preauth/package-info.java

@@ -18,4 +18,7 @@
  * Support for "pre-authenticated" scenarios, where Spring Security assumes the incoming
  * request has already been authenticated by some externally configured system.
  */
+@NullMarked
 package org.springframework.security.web.authentication.preauth;
+
+import org.jspecify.annotations.NullMarked;

+ 11 - 10
web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/DefaultWASUsernameAndGroupsExtractor.java

@@ -30,6 +30,7 @@ import javax.security.auth.Subject;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.log.LogMessage;
 
@@ -50,16 +51,16 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
 
 	private static final String USER_REGISTRY = "UserRegistry";
 
-	private static Method getRunAsSubject = null;
+	private static @Nullable Method getRunAsSubject = null;
 
-	private static Method getGroupsForUser = null;
+	private static @Nullable Method getGroupsForUser = null;
 
-	private static Method getSecurityName = null;
+	private static @Nullable Method getSecurityName = null;
 
-	private static Method narrow = null;
+	private static @Nullable Method narrow = null;
 
 	// SEC-803
-	private static Class<?> wsCredentialClass = null;
+	private static @Nullable Class<?> wsCredentialClass = null;
 
 	@Override
 	public List<String> getGroupsForCurrentUser() {
@@ -67,7 +68,7 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
 	}
 
 	@Override
-	public String getCurrentUserName() {
+	public @Nullable String getCurrentUserName() {
 		return getSecurityName(getRunAsSubject());
 	}
 
@@ -76,7 +77,7 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
 	 * @param subject The subject for which to retrieve the security name
 	 * @return String the security name for the given subject
 	 */
-	private static String getSecurityName(final Subject subject) {
+	private static @Nullable String getSecurityName(final Subject subject) {
 		logger.debug(LogMessage.format("Determining Websphere security name for subject %s", subject));
 		String userSecurityName = null;
 		if (subject != null) {
@@ -116,7 +117,7 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
 	 * @return the WebSphere group names for the given security name
 	 */
 	@SuppressWarnings("unchecked")
-	private static List<String> getWebSphereGroups(final String securityName) {
+	private static List<String> getWebSphereGroups(final @Nullable String securityName) {
 		Context context = null;
 		try {
 			// TODO: Cache UserRegistry object
@@ -140,7 +141,7 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
 		}
 	}
 
-	private static void closeContext(Context context) {
+	private static void closeContext(@Nullable Context context) {
 		try {
 			if (context != null) {
 				context.close();
@@ -151,7 +152,7 @@ final class DefaultWASUsernameAndGroupsExtractor implements WASUsernameAndGroups
 		}
 	}
 
-	private static Object invokeMethod(Method method, Object instance, Object... args) {
+	private static Object invokeMethod(Method method, @Nullable Object instance, Object... args) {
 		try {
 			return method.invoke(instance, args);
 		}

+ 3 - 1
web/src/main/java/org/springframework/security/web/authentication/preauth/websphere/WASUsernameAndGroupsExtractor.java

@@ -18,6 +18,8 @@ package org.springframework.security.web.authentication.preauth.websphere;
 
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
+
 /**
  * Provides indirection between classes using websphere and the actual container
  * interaction, allowing for easier unit testing.
@@ -31,6 +33,6 @@ interface WASUsernameAndGroupsExtractor {
 
 	List<String> getGroupsForCurrentUser();
 
-	String getCurrentUserName();
+	@Nullable String getCurrentUserName();
 
 }

部分文件因文件數量過多而無法顯示