Ver Fonte

Add nullability to spring-security-core

Closes gh-17534
Rob Winch há 1 mês atrás
pai
commit
7c887d2da1
100 ficheiros alterados com 522 adições e 191 exclusões
  1. 1 1
      config/src/test/kotlin/org/springframework/security/config/annotation/web/OAuth2ResourceServerDslTests.kt
  2. 1 1
      config/src/test/kotlin/org/springframework/security/config/annotation/web/session/SessionConcurrencyDslTests.kt
  3. 1 1
      config/src/test/kotlin/org/springframework/security/config/web/server/ServerFormLoginDslTests.kt
  4. 1 1
      config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpBasicDslTests.kt
  5. 1 1
      config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDslTests.kt
  6. 1 1
      config/src/test/kotlin/org/springframework/security/config/web/server/ServerOAuth2ClientDslTests.kt
  7. 4 0
      core/spring-security-core.gradle
  8. 5 2
      core/src/main/java/org/springframework/security/access/annotation/Jsr250MethodSecurityMetadataSource.java
  9. 7 3
      core/src/main/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataSource.java
  10. 3 0
      core/src/main/java/org/springframework/security/access/annotation/package-info.java
  11. 3 0
      core/src/main/java/org/springframework/security/access/event/package-info.java
  12. 9 5
      core/src/main/java/org/springframework/security/access/expression/AbstractSecurityExpressionHandler.java
  13. 6 1
      core/src/main/java/org/springframework/security/access/expression/ExpressionUtils.java
  14. 11 7
      core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java
  15. 7 3
      core/src/main/java/org/springframework/security/access/expression/method/AbstractExpressionBasedMethodConfigAttribute.java
  16. 16 7
      core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java
  17. 8 2
      core/src/main/java/org/springframework/security/access/expression/method/ExpressionBasedAnnotationAttributeFactory.java
  18. 2 0
      core/src/main/java/org/springframework/security/access/expression/method/ExpressionBasedPreInvocationAdvice.java
  19. 5 1
      core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityEvaluationContext.java
  20. 3 2
      core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionHandler.java
  21. 6 4
      core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionOperations.java
  22. 10 8
      core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionRoot.java
  23. 3 1
      core/src/main/java/org/springframework/security/access/expression/method/PostInvocationExpressionAttribute.java
  24. 4 2
      core/src/main/java/org/springframework/security/access/expression/method/PreInvocationExpressionAttribute.java
  25. 3 0
      core/src/main/java/org/springframework/security/access/expression/method/package-info.java
  26. 3 0
      core/src/main/java/org/springframework/security/access/expression/package-info.java
  27. 1 1
      core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.java
  28. 3 0
      core/src/main/java/org/springframework/security/access/hierarchicalroles/package-info.java
  29. 8 5
      core/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java
  30. 5 1
      core/src/main/java/org/springframework/security/access/intercept/AfterInvocationProviderManager.java
  31. 5 1
      core/src/main/java/org/springframework/security/access/intercept/MethodInvocationPrivilegeEvaluator.java
  32. 6 1
      core/src/main/java/org/springframework/security/access/intercept/NullRunAsManager.java
  33. 6 1
      core/src/main/java/org/springframework/security/access/intercept/RunAsImplAuthenticationProvider.java
  34. 7 2
      core/src/main/java/org/springframework/security/access/intercept/RunAsManagerImpl.java
  35. 4 1
      core/src/main/java/org/springframework/security/access/intercept/aopalliance/MethodSecurityInterceptor.java
  36. 5 2
      core/src/main/java/org/springframework/security/access/intercept/aopalliance/MethodSecurityMetadataSourceAdvisor.java
  37. 3 0
      core/src/main/java/org/springframework/security/access/intercept/aopalliance/package-info.java
  38. 2 0
      core/src/main/java/org/springframework/security/access/intercept/aspectj/MethodInvocationAdapter.java
  39. 3 0
      core/src/main/java/org/springframework/security/access/intercept/aspectj/package-info.java
  40. 3 0
      core/src/main/java/org/springframework/security/access/intercept/package-info.java
  41. 4 2
      core/src/main/java/org/springframework/security/access/method/AbstractFallbackMethodSecurityMetadataSource.java
  42. 2 0
      core/src/main/java/org/springframework/security/access/method/AbstractMethodSecurityMetadataSource.java
  43. 5 3
      core/src/main/java/org/springframework/security/access/method/DelegatingMethodSecurityMetadataSource.java
  44. 9 4
      core/src/main/java/org/springframework/security/access/method/MapBasedMethodSecurityMetadataSource.java
  45. 3 1
      core/src/main/java/org/springframework/security/access/method/MethodSecurityMetadataSource.java
  46. 3 0
      core/src/main/java/org/springframework/security/access/method/package-info.java
  47. 3 0
      core/src/main/java/org/springframework/security/access/package-info.java
  48. 4 1
      core/src/main/java/org/springframework/security/access/prepost/PostInvocationAdviceProvider.java
  49. 4 1
      core/src/main/java/org/springframework/security/access/prepost/PreInvocationAuthorizationAdviceVoter.java
  50. 7 4
      core/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java
  51. 7 2
      core/src/main/java/org/springframework/security/access/prepost/PrePostAnnotationSecurityMetadataSource.java
  52. 6 3
      core/src/main/java/org/springframework/security/access/prepost/PrePostInvocationAttributeFactory.java
  53. 3 0
      core/src/main/java/org/springframework/security/access/prepost/package-info.java
  54. 5 1
      core/src/main/java/org/springframework/security/access/vote/AbstractAclVoter.java
  55. 6 1
      core/src/main/java/org/springframework/security/access/vote/RoleHierarchyVoter.java
  56. 3 0
      core/src/main/java/org/springframework/security/access/vote/package-info.java
  57. 3 1
      core/src/main/java/org/springframework/security/aot/hint/CoreSecurityRuntimeHints.java
  58. 3 1
      core/src/main/java/org/springframework/security/aot/hint/OneTimeTokenRuntimeHints.java
  59. 20 0
      core/src/main/java/org/springframework/security/aot/hint/package-info.java
  60. 7 5
      core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java
  61. 11 7
      core/src/main/java/org/springframework/security/authentication/AbstractUserDetailsReactiveAuthenticationManager.java
  62. 3 1
      core/src/main/java/org/springframework/security/authentication/AnonymousAuthenticationProvider.java
  63. 7 6
      core/src/main/java/org/springframework/security/authentication/AuthenticationObservationContext.java
  64. 3 6
      core/src/main/java/org/springframework/security/authentication/AuthenticationObservationConvention.java
  65. 3 1
      core/src/main/java/org/springframework/security/authentication/AuthenticationProvider.java
  66. 3 1
      core/src/main/java/org/springframework/security/authentication/AuthenticationServiceException.java
  67. 7 3
      core/src/main/java/org/springframework/security/authentication/DefaultAuthenticationEventPublisher.java
  68. 3 1
      core/src/main/java/org/springframework/security/authentication/InternalAuthenticationServiceException.java
  69. 1 0
      core/src/main/java/org/springframework/security/authentication/ObservationAuthenticationManager.java
  70. 3 2
      core/src/main/java/org/springframework/security/authentication/ProviderManager.java
  71. 3 1
      core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationProvider.java
  72. 8 6
      core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java
  73. 14 6
      core/src/main/java/org/springframework/security/authentication/dao/DaoAuthenticationProvider.java
  74. 3 0
      core/src/main/java/org/springframework/security/authentication/dao/package-info.java
  75. 3 0
      core/src/main/java/org/springframework/security/authentication/event/package-info.java
  76. 9 4
      core/src/main/java/org/springframework/security/authentication/jaas/AbstractJaasAuthenticationProvider.java
  77. 1 0
      core/src/main/java/org/springframework/security/authentication/jaas/DefaultJaasAuthenticationProvider.java
  78. 1 0
      core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationProvider.java
  79. 4 2
      core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationToken.java
  80. 2 0
      core/src/main/java/org/springframework/security/authentication/jaas/JaasNameCallbackHandler.java
  81. 4 1
      core/src/main/java/org/springframework/security/authentication/jaas/JaasPasswordCallbackHandler.java
  82. 7 4
      core/src/main/java/org/springframework/security/authentication/jaas/SecurityContextLoginModule.java
  83. 3 0
      core/src/main/java/org/springframework/security/authentication/jaas/event/package-info.java
  84. 5 3
      core/src/main/java/org/springframework/security/authentication/jaas/memory/InMemoryConfiguration.java
  85. 3 0
      core/src/main/java/org/springframework/security/authentication/jaas/memory/package-info.java
  86. 3 0
      core/src/main/java/org/springframework/security/authentication/jaas/package-info.java
  87. 3 3
      core/src/main/java/org/springframework/security/authentication/ott/InMemoryOneTimeTokenService.java
  88. 7 4
      core/src/main/java/org/springframework/security/authentication/ott/JdbcOneTimeTokenService.java
  89. 8 6
      core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthenticationToken.java
  90. 2 5
      core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenService.java
  91. 20 0
      core/src/main/java/org/springframework/security/authentication/ott/package-info.java
  92. 20 0
      core/src/main/java/org/springframework/security/authentication/ott/reactive/package-info.java
  93. 3 0
      core/src/main/java/org/springframework/security/authentication/package-info.java
  94. 5 4
      core/src/main/java/org/springframework/security/authentication/password/CompromisedPasswordChecker.java
  95. 5 2
      core/src/main/java/org/springframework/security/authentication/password/ReactiveCompromisedPasswordChecker.java
  96. 20 0
      core/src/main/java/org/springframework/security/authentication/password/package-info.java
  97. 4 4
      core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java
  98. 8 5
      core/src/main/java/org/springframework/security/authorization/AuthorizationObservationContext.java
  99. 3 1
      core/src/main/java/org/springframework/security/authorization/AuthorizationProxyFactory.java
  100. 5 3
      core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java

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

@@ -242,7 +242,7 @@ class OAuth2ResourceServerDslTests {
 
     class MockAuthenticationManager(var authentication: Authentication) : AuthenticationManager {
 
-        override fun authenticate(authentication: Authentication?): Authentication {
+        override fun authenticate(authentication: Authentication): Authentication {
             return this.authentication
         }
 

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

@@ -217,7 +217,7 @@ class SessionConcurrencyDslTests {
                 sessionManagement {
                     sessionConcurrency {
                         maximumSessions {
-                            authentication -> if (isAdmin.authorize({ authentication }, null)!!.isGranted) -1 else 1
+                            authentication -> if (isAdmin.authorize({ authentication }, "")!!.isGranted) -1 else 1
                         }
                         maxSessionsPreventsLogin = true
                     }

+ 1 - 1
config/src/test/kotlin/org/springframework/security/config/web/server/ServerFormLoginDslTests.kt

@@ -172,7 +172,7 @@ class ServerFormLoginDslTests {
     }
 
     class NoopReactiveAuthenticationManager: ReactiveAuthenticationManager {
-        override fun authenticate(authentication: Authentication?): Mono<Authentication> {
+        override fun authenticate(authentication: Authentication): Mono<Authentication> {
             return Mono.empty()
         }
     }

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

@@ -151,7 +151,7 @@ class ServerHttpBasicDslTests {
     }
 
     class NoopReactiveAuthenticationManager: ReactiveAuthenticationManager {
-        override fun authenticate(authentication: Authentication?): Mono<Authentication> {
+        override fun authenticate(authentication: Authentication): Mono<Authentication> {
             return Mono.empty()
         }
     }

+ 1 - 1
config/src/test/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDslTests.kt

@@ -249,7 +249,7 @@ class ServerHttpSecurityDslTests {
     }
 
     class NoopReactiveAuthenticationManager: ReactiveAuthenticationManager {
-        override fun authenticate(authentication: Authentication?): Mono<Authentication> {
+        override fun authenticate(authentication: Authentication): Mono<Authentication> {
             return Mono.empty()
         }
     }

+ 1 - 1
config/src/test/kotlin/org/springframework/security/config/web/server/ServerOAuth2ClientDslTests.kt

@@ -276,7 +276,7 @@ class ServerOAuth2ClientDslTests {
     }
 
     class NoopReactiveAuthenticationManager: ReactiveAuthenticationManager {
-        override fun authenticate(authentication: Authentication?): Mono<Authentication> {
+        override fun authenticate(authentication: Authentication): Mono<Authentication> {
             return Mono.empty()
         }
     }

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

@@ -1,5 +1,9 @@
 import java.util.concurrent.Callable
 
+plugins {
+	id 'security-nullability'
+}
+
 apply plugin: 'io.spring.convention.spring-module'
 apply plugin: 'security-kotlin'
 

+ 5 - 2
core/src/main/java/org/springframework/security/access/annotation/Jsr250MethodSecurityMetadataSource.java

@@ -25,6 +25,8 @@ import java.util.List;
 import jakarta.annotation.security.DenyAll;
 import jakarta.annotation.security.PermitAll;
 import jakarta.annotation.security.RolesAllowed;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.security.access.ConfigAttribute;
@@ -39,6 +41,7 @@ import org.springframework.security.access.method.AbstractFallbackMethodSecurity
  * {@link org.springframework.security.authorization.method.Jsr250AuthorizationManager}
  * instead
  */
+@NullUnmarked
 @Deprecated
 public class Jsr250MethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
 
@@ -71,11 +74,11 @@ public class Jsr250MethodSecurityMetadataSource extends AbstractFallbackMethodSe
 	}
 
 	@Override
-	public Collection<ConfigAttribute> getAllConfigAttributes() {
+	public @Nullable Collection<ConfigAttribute> getAllConfigAttributes() {
 		return null;
 	}
 
-	private List<ConfigAttribute> processAnnotations(Annotation[] annotations) {
+	private @Nullable List<ConfigAttribute> processAnnotations(Annotation @Nullable [] annotations) {
 		if (annotations == null || annotations.length == 0) {
 			return null;
 		}

+ 7 - 3
core/src/main/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataSource.java

@@ -22,6 +22,9 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.core.GenericTypeResolver;
 import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.security.access.ConfigAttribute;
@@ -41,13 +44,14 @@ import org.springframework.util.Assert;
  * @deprecated Use
  * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor#secured}
  */
+@NullUnmarked
 @Deprecated
 @SuppressWarnings({ "unchecked" })
 public class SecuredAnnotationSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
 
 	private AnnotationMetadataExtractor annotationExtractor;
 
-	private Class<? extends Annotation> annotationType;
+	private @Nullable Class<? extends Annotation> annotationType;
 
 	public SecuredAnnotationSecurityMetadataSource() {
 		this(new SecuredAnnotationMetadataExtractor());
@@ -73,11 +77,11 @@ public class SecuredAnnotationSecurityMetadataSource extends AbstractFallbackMet
 	}
 
 	@Override
-	public Collection<ConfigAttribute> getAllConfigAttributes() {
+	public @Nullable Collection<ConfigAttribute> getAllConfigAttributes() {
 		return null;
 	}
 
-	private Collection<ConfigAttribute> processAnnotation(Annotation annotation) {
+	private @Nullable Collection<ConfigAttribute> processAnnotation(@Nullable Annotation annotation) {
 		return (annotation != null) ? this.annotationExtractor.extractAttributes(annotation) : null;
 	}
 

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

@@ -17,4 +17,7 @@
 /**
  * Support for JSR-250 and Spring Security {@code @Secured} annotations.
  */
+@NullMarked
 package org.springframework.security.access.annotation;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -17,4 +17,7 @@
 /**
  * Authorization event and listener classes.
  */
+@NullMarked
 package org.springframework.security.access.event;
+
+import org.jspecify.annotations.NullMarked;

+ 9 - 5
core/src/main/java/org/springframework/security/access/expression/AbstractSecurityExpressionHandler.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.access.expression;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.expression.BeanFactoryResolver;
@@ -43,9 +45,9 @@ public abstract class AbstractSecurityExpressionHandler<T>
 
 	private ExpressionParser expressionParser = new SpelExpressionParser();
 
-	private BeanResolver beanResolver;
+	private @Nullable BeanResolver beanResolver;
 
-	private RoleHierarchy roleHierarchy;
+	private @Nullable RoleHierarchy roleHierarchy;
 
 	private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
 
@@ -71,7 +73,9 @@ public abstract class AbstractSecurityExpressionHandler<T>
 	public final EvaluationContext createEvaluationContext(Authentication authentication, T invocation) {
 		SecurityExpressionOperations root = createSecurityExpressionRoot(authentication, invocation);
 		StandardEvaluationContext ctx = createEvaluationContextInternal(authentication, invocation);
-		ctx.setBeanResolver(this.beanResolver);
+		if (this.beanResolver != null) {
+			ctx.setBeanResolver(this.beanResolver);
+		}
 		ctx.setRootObject(root);
 		return ctx;
 	}
@@ -101,7 +105,7 @@ public abstract class AbstractSecurityExpressionHandler<T>
 	protected abstract SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
 			T invocation);
 
-	protected RoleHierarchy getRoleHierarchy() {
+	protected @Nullable RoleHierarchy getRoleHierarchy() {
 		return this.roleHierarchy;
 	}
 
@@ -117,7 +121,7 @@ public abstract class AbstractSecurityExpressionHandler<T>
 		this.permissionEvaluator = permissionEvaluator;
 	}
 
-	protected BeanResolver getBeanResolver() {
+	protected @Nullable BeanResolver getBeanResolver() {
 		return this.beanResolver;
 	}
 

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

@@ -27,7 +27,12 @@ public final class ExpressionUtils {
 
 	public static boolean evaluateAsBoolean(Expression expr, EvaluationContext ctx) {
 		try {
-			return expr.getValue(ctx, Boolean.class);
+			Boolean result = expr.getValue(ctx, Boolean.class);
+			if (result == null) {
+				throw new IllegalArgumentException(
+						"Expression was null but expected boolean result '" + expr.getExpressionString() + "'");
+			}
+			return result;
 		}
 		catch (EvaluationException ex) {
 			throw new IllegalArgumentException("Failed to evaluate expression '" + expr.getExpressionString() + "'",

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

@@ -21,9 +21,12 @@ import java.util.Collection;
 import java.util.Set;
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.access.PermissionEvaluator;
 import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
 import org.springframework.security.authentication.AuthenticationTrustResolver;
+import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
@@ -41,11 +44,11 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 
 	private final Supplier<Authentication> authentication;
 
-	private AuthenticationTrustResolver trustResolver;
+	private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
 
-	private RoleHierarchy roleHierarchy;
+	private @Nullable RoleHierarchy roleHierarchy;
 
-	private Set<String> roles;
+	private @Nullable Set<String> roles;
 
 	private String defaultRolePrefix = "ROLE_";
 
@@ -59,7 +62,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 	 */
 	public final boolean denyAll = false;
 
-	private PermissionEvaluator permissionEvaluator;
+	private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
 
 	public final String read = "read";
 
@@ -114,7 +117,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 		return hasAnyAuthorityName(this.defaultRolePrefix, roles);
 	}
 
-	private boolean hasAnyAuthorityName(String prefix, String... roles) {
+	private boolean hasAnyAuthorityName(@Nullable String prefix, String... roles) {
 		Set<String> roleSet = getAuthoritySet();
 		for (String role : roles) {
 			String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
@@ -166,7 +169,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 	 * {@link #getAuthentication()}
 	 * @return
 	 */
-	public Object getPrincipal() {
+	public @Nullable Object getPrincipal() {
 		return getAuthentication().getPrincipal();
 	}
 
@@ -218,6 +221,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 	}
 
 	public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
+		Assert.notNull(permissionEvaluator, "permissionEvaluator cannot be null");
 		this.permissionEvaluator = permissionEvaluator;
 	}
 
@@ -228,7 +232,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 	 * @param role
 	 * @return
 	 */
-	private static String getRoleWithDefaultPrefix(String defaultRolePrefix, String role) {
+	private static String getRoleWithDefaultPrefix(@Nullable String defaultRolePrefix, String role) {
 		if (role == null) {
 			return role;
 		}

+ 7 - 3
core/src/main/java/org/springframework/security/access/expression/method/AbstractExpressionBasedMethodConfigAttribute.java

@@ -16,6 +16,9 @@
 
 package org.springframework.security.access.expression.method;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParseException;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -35,12 +38,13 @@ import org.springframework.util.Assert;
  * @deprecated Use {@link org.springframework.security.authorization.AuthorizationManager}
  * interceptors instead
  */
+@NullUnmarked
 @Deprecated
 abstract class AbstractExpressionBasedMethodConfigAttribute implements ConfigAttribute {
 
-	private final Expression filterExpression;
+	private final @Nullable Expression filterExpression;
 
-	private final Expression authorizeExpression;
+	private final @Nullable Expression authorizeExpression;
 
 	/**
 	 * Parses the supplied expressions as Spring-EL.
@@ -71,7 +75,7 @@ abstract class AbstractExpressionBasedMethodConfigAttribute implements ConfigAtt
 	}
 
 	@Override
-	public String getAttribute() {
+	public @Nullable String getAttribute() {
 		return null;
 	}
 

+ 16 - 7
core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java

@@ -23,17 +23,20 @@ import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 import org.aopalliance.intercept.MethodInvocation;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.ParameterNameDiscoverer;
 import org.springframework.core.log.LogMessage;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
+import org.springframework.expression.TypedValue;
 import org.springframework.expression.spel.support.StandardEvaluationContext;
 import org.springframework.security.access.PermissionCacheOptimizer;
 import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
@@ -64,7 +67,7 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
 
 	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultSecurityParameterNameDiscoverer();
 
-	private PermissionCacheOptimizer permissionCacheOptimizer = null;
+	private @Nullable PermissionCacheOptimizer permissionCacheOptimizer = null;
 
 	private String defaultRolePrefix = "ROLE_";
 
@@ -85,7 +88,7 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
 		MethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);
 		MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(root, mi,
 				getParameterNameDiscoverer());
-		ctx.setBeanResolver(getBeanResolver());
+		Optional.ofNullable(getBeanResolver()).ifPresent(ctx::setBeanResolver);
 		return ctx;
 	}
 
@@ -104,7 +107,7 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
 		root.setThis(invocation.getThis());
 		root.setPermissionEvaluator(getPermissionEvaluator());
 		root.setTrustResolver(getTrustResolver());
-		root.setRoleHierarchy(getRoleHierarchy());
+		Optional.ofNullable(getRoleHierarchy()).ifPresent(root::setRoleHierarchy);
 		root.setDefaultRolePrefix(getDefaultRolePrefix());
 		return root;
 	}
@@ -119,14 +122,15 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
 	 * {@link Stream}
 	 */
 	@Override
-	public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) {
+	public Object filter(@Nullable Object filterTarget, Expression filterExpression, EvaluationContext ctx) {
 		MethodSecurityExpressionOperations rootObject = (MethodSecurityExpressionOperations) ctx.getRootObject()
 			.getValue();
+		Assert.notNull(rootObject, "rootObject cannot be null");
 		this.logger.debug(LogMessage.format("Filtering with expression: %s", filterExpression.getExpressionString()));
 		if (filterTarget instanceof Collection) {
 			return filterCollection((Collection<?>) filterTarget, filterExpression, ctx, rootObject);
 		}
-		if (filterTarget.getClass().isArray()) {
+		if (filterTarget != null && filterTarget.getClass().isArray()) {
 			return filterArray((Object[]) filterTarget, filterExpression, ctx, rootObject);
 		}
 		if (filterTarget instanceof Map) {
@@ -259,8 +263,13 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
 	}
 
 	@Override
-	public void setReturnObject(Object returnObject, EvaluationContext ctx) {
-		((MethodSecurityExpressionOperations) ctx.getRootObject().getValue()).setReturnObject(returnObject);
+	public void setReturnObject(@Nullable Object returnObject, EvaluationContext ctx) {
+		TypedValue rootObject = ctx.getRootObject();
+		Assert.notNull(rootObject, "rootObject cannot be null");
+		MethodSecurityExpressionOperations methodOperations = (MethodSecurityExpressionOperations) rootObject
+			.getValue();
+		Assert.notNull(methodOperations, "MethodSecurityExpressionOperations cannot be null");
+		methodOperations.setReturnObject(returnObject);
 	}
 
 	/**

+ 8 - 2
core/src/main/java/org/springframework/security/access/expression/method/ExpressionBasedAnnotationAttributeFactory.java

@@ -16,12 +16,16 @@
 
 package org.springframework.security.access.expression.method;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.ParseException;
 import org.springframework.security.access.prepost.PostInvocationAttribute;
 import org.springframework.security.access.prepost.PreInvocationAttribute;
 import org.springframework.security.access.prepost.PrePostInvocationAttributeFactory;
+import org.springframework.util.Assert;
 
 /**
  * {@link PrePostInvocationAttributeFactory} which interprets the annotation value as an
@@ -33,16 +37,18 @@ import org.springframework.security.access.prepost.PrePostInvocationAttributeFac
  * @deprecated Use {@link org.springframework.security.authorization.AuthorizationManager}
  * interceptors instead
  */
+@NullUnmarked
 @Deprecated
 public class ExpressionBasedAnnotationAttributeFactory implements PrePostInvocationAttributeFactory {
 
 	private final Object parserLock = new Object();
 
-	private ExpressionParser parser;
+	private @Nullable ExpressionParser parser;
 
 	private MethodSecurityExpressionHandler handler;
 
 	public ExpressionBasedAnnotationAttributeFactory(MethodSecurityExpressionHandler handler) {
+		Assert.notNull(handler, "handler cannot be null");
 		this.handler = handler;
 	}
 
@@ -64,7 +70,7 @@ public class ExpressionBasedAnnotationAttributeFactory implements PrePostInvocat
 	}
 
 	@Override
-	public PostInvocationAttribute createPostInvocationAttribute(String postFilterAttribute,
+	public @Nullable PostInvocationAttribute createPostInvocationAttribute(String postFilterAttribute,
 			String postAuthorizeAttribute) {
 		try {
 			ExpressionParser parser = getParser();

+ 2 - 0
core/src/main/java/org/springframework/security/access/expression/method/ExpressionBasedPreInvocationAdvice.java

@@ -19,6 +19,7 @@ package org.springframework.security.access.expression.method;
 import java.util.Collection;
 
 import org.aopalliance.intercept.MethodInvocation;
+import org.jspecify.annotations.NullUnmarked;
 
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
@@ -37,6 +38,7 @@ import org.springframework.util.Assert;
  * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
  * instead
  */
+@NullUnmarked
 @Deprecated
 public class ExpressionBasedPreInvocationAdvice implements PreInvocationAuthorizationAdvice {
 

+ 5 - 1
core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityEvaluationContext.java

@@ -19,6 +19,7 @@ package org.springframework.security.access.expression.method;
 import java.lang.reflect.Method;
 
 import org.aopalliance.intercept.MethodInvocation;
+import org.jspecify.annotations.NullUnmarked;
 
 import org.springframework.aop.framework.AopProxyUtils;
 import org.springframework.aop.support.AopUtils;
@@ -48,6 +49,8 @@ class MethodSecurityEvaluationContext extends MethodBasedEvaluationContext {
 		this(user, mi, new DefaultSecurityParameterNameDiscoverer());
 	}
 
+	@NullUnmarked // FIXME: rootObject in MethodBasedEvaluationContext is non-null
+					// (probably needs changed) but StandardEvaluationContext is Nullable
 	MethodSecurityEvaluationContext(Authentication user, MethodInvocation mi,
 			ParameterNameDiscoverer parameterNameDiscoverer) {
 		super(mi.getThis(), getSpecificMethod(mi), mi.getArguments(), parameterNameDiscoverer);
@@ -59,7 +62,8 @@ class MethodSecurityEvaluationContext extends MethodBasedEvaluationContext {
 	}
 
 	private static Method getSpecificMethod(MethodInvocation mi) {
-		return AopUtils.getMostSpecificMethod(mi.getMethod(), AopProxyUtils.ultimateTargetClass(mi.getThis()));
+		Class<?> targetClass = (mi.getThis() != null) ? AopProxyUtils.ultimateTargetClass(mi.getThis()) : null;
+		return AopUtils.getMostSpecificMethod(mi.getMethod(), targetClass);
 	}
 
 }

+ 3 - 2
core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionHandler.java

@@ -17,6 +17,7 @@
 package org.springframework.security.access.expression.method;
 
 import org.aopalliance.intercept.MethodInvocation;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
@@ -41,7 +42,7 @@ public interface MethodSecurityExpressionHandler extends SecurityExpressionHandl
 	 * {@link #createEvaluationContext(org.springframework.security.core.Authentication, Object)}
 	 * @return the filtered collection or array
 	 */
-	Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx);
+	Object filter(@Nullable Object filterTarget, Expression filterExpression, EvaluationContext ctx);
 
 	/**
 	 * Used to inform the expression system of the return object for the given evaluation
@@ -51,6 +52,6 @@ public interface MethodSecurityExpressionHandler extends SecurityExpressionHandl
 	 * call to
 	 * {@link #createEvaluationContext(org.springframework.security.core.Authentication, Object)}
 	 */
-	void setReturnObject(Object returnObject, EvaluationContext ctx);
+	void setReturnObject(@Nullable Object returnObject, EvaluationContext ctx);
 
 }

+ 6 - 4
core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionOperations.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.access.expression.method;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.access.expression.SecurityExpressionOperations;
 
 /**
@@ -29,12 +31,12 @@ public interface MethodSecurityExpressionOperations extends SecurityExpressionOp
 
 	void setFilterObject(Object filterObject);
 
-	Object getFilterObject();
+	@Nullable Object getFilterObject();
 
-	void setReturnObject(Object returnObject);
+	void setReturnObject(@Nullable Object returnObject);
 
-	Object getReturnObject();
+	@Nullable Object getReturnObject();
 
-	Object getThis();
+	@Nullable Object getThis();
 
 }

+ 10 - 8
core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionRoot.java

@@ -18,6 +18,8 @@ package org.springframework.security.access.expression.method;
 
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.core.Authentication;
 
@@ -30,11 +32,11 @@ import org.springframework.security.core.Authentication;
  */
 class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
 
-	private Object filterObject;
+	private @Nullable Object filterObject;
 
-	private Object returnObject;
+	private @Nullable Object returnObject;
 
-	private Object target;
+	private @Nullable Object target;
 
 	MethodSecurityExpressionRoot(Authentication a) {
 		super(a);
@@ -50,17 +52,17 @@ class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements Met
 	}
 
 	@Override
-	public Object getFilterObject() {
+	public @Nullable Object getFilterObject() {
 		return this.filterObject;
 	}
 
 	@Override
-	public void setReturnObject(Object returnObject) {
+	public void setReturnObject(@Nullable Object returnObject) {
 		this.returnObject = returnObject;
 	}
 
 	@Override
-	public Object getReturnObject() {
+	public @Nullable Object getReturnObject() {
 		return this.returnObject;
 	}
 
@@ -70,12 +72,12 @@ class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements Met
 	 * protected.
 	 * @param target the target object on which the method in is being invoked.
 	 */
-	void setThis(Object target) {
+	void setThis(@Nullable Object target) {
 		this.target = target;
 	}
 
 	@Override
-	public Object getThis() {
+	public @Nullable Object getThis() {
 		return this.target;
 	}
 

+ 3 - 1
core/src/main/java/org/springframework/security/access/expression/method/PostInvocationExpressionAttribute.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.access.expression.method;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParseException;
 import org.springframework.security.access.prepost.PostInvocationAttribute;
@@ -36,7 +38,7 @@ class PostInvocationExpressionAttribute extends AbstractExpressionBasedMethodCon
 		super(filterExpression, authorizeExpression);
 	}
 
-	PostInvocationExpressionAttribute(Expression filterExpression, Expression authorizeExpression)
+	PostInvocationExpressionAttribute(@Nullable Expression filterExpression, @Nullable Expression authorizeExpression)
 			throws ParseException {
 		super(filterExpression, authorizeExpression);
 	}

+ 4 - 2
core/src/main/java/org/springframework/security/access/expression/method/PreInvocationExpressionAttribute.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.access.expression.method;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParseException;
 import org.springframework.security.access.prepost.PreInvocationAttribute;
@@ -40,8 +42,8 @@ class PreInvocationExpressionAttribute extends AbstractExpressionBasedMethodConf
 		this.filterTarget = filterTarget;
 	}
 
-	PreInvocationExpressionAttribute(Expression filterExpression, String filterTarget, Expression authorizeExpression)
-			throws ParseException {
+	PreInvocationExpressionAttribute(@Nullable Expression filterExpression, String filterTarget,
+			Expression authorizeExpression) throws ParseException {
 		super(filterExpression, authorizeExpression);
 		this.filterTarget = filterTarget;
 	}

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

@@ -19,4 +19,7 @@
  *
  * @since 3.0
  */
+@NullMarked
 package org.springframework.security.access.expression.method;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -22,4 +22,7 @@
  *
  * @since 3.0
  */
+@NullMarked
 package org.springframework.security.access.expression;
+
+import org.jspecify.annotations.NullMarked;

+ 1 - 1
core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.java

@@ -86,7 +86,7 @@ public final class RoleHierarchyImpl implements RoleHierarchy {
 	 * {@code rolesReachableInOneOrMoreStepsMap} is a Map that under the key of a specific
 	 * role name contains a set of all roles reachable from this role in 1 or more steps
 	 */
-	private Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap = null;
+	private final Map<String, Set<GrantedAuthority>> rolesReachableInOneOrMoreStepsMap;
 
 	private RoleHierarchyImpl(Map<String, Set<GrantedAuthority>> hierarchy) {
 		this.rolesReachableInOneOrMoreStepsMap = buildRolesReachableInOneOrMoreStepsMap(hierarchy);

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

@@ -17,4 +17,7 @@
 /**
  * Role hierarchy implementation.
  */
+@NullMarked
 package org.springframework.security.access.hierarchicalroles;
+
+import org.jspecify.annotations.NullMarked;

+ 8 - 5
core/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java

@@ -22,6 +22,8 @@ import java.util.Set;
 
 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.beans.factory.InitializingBean;
 import org.springframework.context.ApplicationEvent;
@@ -114,6 +116,7 @@ import org.springframework.util.CollectionUtils;
  * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
  * for method security.
  */
+@NullUnmarked
 @Deprecated
 public abstract class AbstractSecurityInterceptor
 		implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {
@@ -125,11 +128,11 @@ public abstract class AbstractSecurityInterceptor
 	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
 		.getContextHolderStrategy();
 
-	private ApplicationEventPublisher eventPublisher;
+	private @Nullable ApplicationEventPublisher eventPublisher;
 
-	private AccessDecisionManager accessDecisionManager;
+	private @Nullable AccessDecisionManager accessDecisionManager;
 
-	private AfterInvocationManager afterInvocationManager;
+	private @Nullable AfterInvocationManager afterInvocationManager;
 
 	private AuthenticationManager authenticationManager = new NoOpAuthenticationManager();
 
@@ -190,7 +193,7 @@ public abstract class AbstractSecurityInterceptor
 		}
 	}
 
-	protected InterceptorStatusToken beforeInvocation(Object object) {
+	protected @Nullable InterceptorStatusToken beforeInvocation(Object object) {
 		Assert.notNull(object, "Object was null");
 		if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
 			throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName()
@@ -291,7 +294,7 @@ public abstract class AbstractSecurityInterceptor
 	 * @return the object the secure object invocation should ultimately return to its
 	 * caller (may be <tt>null</tt>)
 	 */
-	protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
+	protected Object afterInvocation(InterceptorStatusToken token, @Nullable Object returnedObject) {
 		if (token == null) {
 			// public object
 			return returnedObject;

+ 5 - 1
core/src/main/java/org/springframework/security/access/intercept/AfterInvocationProviderManager.java

@@ -22,6 +22,8 @@ import java.util.List;
 
 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.beans.factory.InitializingBean;
 import org.springframework.core.log.LogMessage;
@@ -52,12 +54,14 @@ import org.springframework.util.CollectionUtils;
  * @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor
  * @deprecated Use delegation with {@link AuthorizationManager}
  */
+@NullUnmarked
 @Deprecated
 public class AfterInvocationProviderManager implements AfterInvocationManager, InitializingBean {
 
 	protected static final Log logger = LogFactory.getLog(AfterInvocationProviderManager.class);
 
-	private List<AfterInvocationProvider> providers;
+	@SuppressWarnings("NullAway.Init")
+	private @Nullable List<AfterInvocationProvider> providers;
 
 	@Override
 	public void afterPropertiesSet() {

+ 5 - 1
core/src/main/java/org/springframework/security/access/intercept/MethodInvocationPrivilegeEvaluator.java

@@ -21,6 +21,8 @@ import java.util.Collection;
 import org.aopalliance.intercept.MethodInvocation;
 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.beans.factory.InitializingBean;
 import org.springframework.core.log.LogMessage;
@@ -46,12 +48,14 @@ import org.springframework.util.Assert;
  * @deprecated Use {@link org.springframework.security.authorization.AuthorizationManager}
  * instead
  */
+@NullUnmarked
 @Deprecated
 public class MethodInvocationPrivilegeEvaluator implements InitializingBean {
 
 	protected static final Log logger = LogFactory.getLog(MethodInvocationPrivilegeEvaluator.class);
 
-	private AbstractSecurityInterceptor securityInterceptor;
+	@SuppressWarnings("NullAway.Init")
+	private @Nullable AbstractSecurityInterceptor securityInterceptor;
 
 	@Override
 	public void afterPropertiesSet() {

+ 6 - 1
core/src/main/java/org/springframework/security/access/intercept/NullRunAsManager.java

@@ -18,6 +18,9 @@ package org.springframework.security.access.intercept;
 
 import java.util.Collection;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.core.Authentication;
 
@@ -30,11 +33,13 @@ import org.springframework.security.core.Authentication;
  * @author Ben Alex
  * @deprecated please see {@link RunAsManager} deprecation notice
  */
+@NullUnmarked
 @Deprecated
 final class NullRunAsManager implements RunAsManager {
 
 	@Override
-	public Authentication buildRunAs(Authentication authentication, Object object, Collection<ConfigAttribute> config) {
+	public @Nullable Authentication buildRunAs(Authentication authentication, Object object,
+			Collection<ConfigAttribute> config) {
 		return null;
 	}
 

+ 6 - 1
core/src/main/java/org/springframework/security/access/intercept/RunAsImplAuthenticationProvider.java

@@ -16,6 +16,9 @@
 
 package org.springframework.security.access.intercept;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.context.MessageSource;
 import org.springframework.context.MessageSourceAware;
@@ -44,12 +47,14 @@ import org.springframework.util.Assert;
  * class is only used by now-deprecated components. There is not yet an equivalent
  * replacement in Spring Security.
  */
+@NullUnmarked
 @Deprecated
 public class RunAsImplAuthenticationProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware {
 
 	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
 
-	private String key;
+	@SuppressWarnings("NullAway.Init")
+	private @Nullable String key;
 
 	@Override
 	public void afterPropertiesSet() {

+ 7 - 2
core/src/main/java/org/springframework/security/access/intercept/RunAsManagerImpl.java

@@ -20,6 +20,9 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.core.Authentication;
@@ -56,10 +59,12 @@ import org.springframework.util.Assert;
  * class is only used by now-deprecated components. There is not yet an equivalent
  * replacement in Spring Security.
  */
+@NullUnmarked
 @Deprecated
 public class RunAsManagerImpl implements RunAsManager, InitializingBean {
 
-	private String key;
+	@SuppressWarnings("NullAway.Init")
+	private @Nullable String key;
 
 	private String rolePrefix = "ROLE_";
 
@@ -70,7 +75,7 @@ public class RunAsManagerImpl implements RunAsManager, InitializingBean {
 	}
 
 	@Override
-	public Authentication buildRunAs(Authentication authentication, Object object,
+	public @Nullable Authentication buildRunAs(Authentication authentication, Object object,
 			Collection<ConfigAttribute> attributes) {
 		List<GrantedAuthority> newAuthorities = new ArrayList<>();
 		for (ConfigAttribute attribute : attributes) {

+ 4 - 1
core/src/main/java/org/springframework/security/access/intercept/aopalliance/MethodSecurityInterceptor.java

@@ -18,6 +18,8 @@ package org.springframework.security.access.intercept.aopalliance;
 
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.access.SecurityMetadataSource;
 import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
@@ -42,10 +44,11 @@ import org.springframework.security.access.method.MethodSecurityMetadataSource;
  * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
  * instead
  */
+@NullUnmarked
 @Deprecated
 public class MethodSecurityInterceptor extends AbstractSecurityInterceptor implements MethodInterceptor {
 
-	private MethodSecurityMetadataSource securityMetadataSource;
+	private @Nullable MethodSecurityMetadataSource securityMetadataSource;
 
 	@Override
 	public Class<?> getSecureObjectClass() {

+ 5 - 2
core/src/main/java/org/springframework/security/access/intercept/aopalliance/MethodSecurityMetadataSourceAdvisor.java

@@ -23,6 +23,8 @@ import java.lang.reflect.Method;
 
 import org.aopalliance.aop.Advice;
 import org.aopalliance.intercept.MethodInterceptor;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.aop.Pointcut;
 import org.springframework.aop.support.AbstractPointcutAdvisor;
@@ -53,17 +55,18 @@ import org.springframework.util.CollectionUtils;
  * @author Luke Taylor
  * @deprecated Use {@link EnableMethodSecurity} or publish interceptors directly
  */
+@NullUnmarked
 @Deprecated
 @SuppressWarnings("serial")
 public class MethodSecurityMetadataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
 
 	private transient MethodSecurityMetadataSource attributeSource;
 
-	private transient MethodInterceptor interceptor;
+	private transient @Nullable MethodInterceptor interceptor;
 
 	private final Pointcut pointcut = new MethodSecurityMetadataSourcePointcut();
 
-	private BeanFactory beanFactory;
+	private @Nullable BeanFactory beanFactory;
 
 	private final String adviceBeanName;
 

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

@@ -18,4 +18,7 @@
  * Enforces security for AOP Alliance <code>MethodInvocation</code>s, such as via Spring
  * AOP.
  */
+@NullMarked
 package org.springframework.security.access.intercept.aopalliance;
+
+import org.jspecify.annotations.NullMarked;

+ 2 - 0
core/src/main/java/org/springframework/security/access/intercept/aspectj/MethodInvocationAdapter.java

@@ -23,6 +23,7 @@ import org.aopalliance.intercept.MethodInvocation;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.reflect.CodeSignature;
+import org.jspecify.annotations.NullUnmarked;
 
 import org.springframework.util.Assert;
 
@@ -35,6 +36,7 @@ import org.springframework.util.Assert;
  * @deprecated This class will be removed from the public API. See
  * `JoinPointMethodInvocation` in `spring-security-aspects` for its replacement
  */
+@NullUnmarked
 @Deprecated
 public final class MethodInvocationAdapter implements MethodInvocation {
 

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

@@ -18,4 +18,7 @@
  * Enforces security for AspectJ <code>JointPoint</code>s, delegating secure object
  * callbacks to the calling aspect.
  */
+@NullMarked
 package org.springframework.security.access.intercept.aspectj;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -33,4 +33,7 @@
  * an appropriate {@link org.springframework.security.access.SecurityMetadataSource} for
  * the type of resources the secure object represents.
  */
+@NullMarked
 package org.springframework.security.access.intercept;
+
+import org.jspecify.annotations.NullMarked;

+ 4 - 2
core/src/main/java/org/springframework/security/access/method/AbstractFallbackMethodSecurityMetadataSource.java

@@ -20,6 +20,8 @@ import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.Collections;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.aop.support.AopUtils;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.authorization.AuthorizationManager;
@@ -52,7 +54,7 @@ import org.springframework.security.authorization.AuthorizationManager;
 public abstract class AbstractFallbackMethodSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
 
 	@Override
-	public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
+	public Collection<ConfigAttribute> getAttributes(Method method, @Nullable Class<?> targetClass) {
 		// The method may be on an interface, but we need attributes from the target
 		// class.
 		// If the target class is null, the method will be unchanged.
@@ -92,7 +94,7 @@ public abstract class AbstractFallbackMethodSecurityMetadataSource extends Abstr
 	 * @param targetClass the target class for the invocation (may be <code>null</code>)
 	 * @return the security metadata (or null if no metadata applies)
 	 */
-	protected abstract Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass);
+	protected abstract Collection<ConfigAttribute> findAttributes(Method method, @Nullable Class<?> targetClass);
 
 	/**
 	 * Obtains the security metadata registered against the specified class.

+ 2 - 0
core/src/main/java/org/springframework/security/access/method/AbstractMethodSecurityMetadataSource.java

@@ -21,6 +21,7 @@ import java.util.Collection;
 import org.aopalliance.intercept.MethodInvocation;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.NullUnmarked;
 
 import org.springframework.aop.framework.AopProxyUtils;
 import org.springframework.security.access.ConfigAttribute;
@@ -36,6 +37,7 @@ import org.springframework.security.authorization.AuthorizationManager;
  * {@code <method-security>} and {@code <intercept-methods>} instead or use
  * annotation-based or {@link AuthorizationManager}-based authorization
  */
+@NullUnmarked
 @Deprecated
 public abstract class AbstractMethodSecurityMetadataSource implements MethodSecurityMetadataSource {
 

+ 5 - 3
core/src/main/java/org/springframework/security/access/method/DelegatingMethodSecurityMetadataSource.java

@@ -25,6 +25,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.authorization.AuthorizationManager;
@@ -57,7 +59,7 @@ public final class DelegatingMethodSecurityMetadataSource extends AbstractMethod
 	}
 
 	@Override
-	public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
+	public Collection<ConfigAttribute> getAttributes(Method method, @Nullable Class<?> targetClass) {
 		DefaultCacheKey cacheKey = new DefaultCacheKey(method, targetClass);
 		synchronized (this.attributeCache) {
 			Collection<ConfigAttribute> cached = this.attributeCache.get(cacheKey);
@@ -104,9 +106,9 @@ public final class DelegatingMethodSecurityMetadataSource extends AbstractMethod
 
 		private final Method method;
 
-		private final Class<?> targetClass;
+		private final @Nullable Class<?> targetClass;
 
-		DefaultCacheKey(Method method, Class<?> targetClass) {
+		DefaultCacheKey(Method method, @Nullable Class<?> targetClass) {
 			this.method = method;
 			this.targetClass = targetClass;
 		}

+ 9 - 4
core/src/main/java/org/springframework/security/access/method/MapBasedMethodSecurityMetadataSource.java

@@ -25,6 +25,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.beans.factory.BeanClassLoaderAware;
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.access.ConfigAttribute;
@@ -47,11 +50,13 @@ import org.springframework.util.ClassUtils;
  * {@code <method-security>} and {@code <intercept-methods>} instead or use
  * annotation-based or {@link AuthorizationManager}-based authorization
  */
+@NullUnmarked
 @Deprecated
 public class MapBasedMethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource
 		implements BeanClassLoaderAware {
 
-	private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
+	@SuppressWarnings("NullAway")
+	private @Nullable ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
 
 	/**
 	 * Map from RegisteredMethod to ConfigAttribute list
@@ -80,7 +85,7 @@ public class MapBasedMethodSecurityMetadataSource extends AbstractFallbackMethod
 	 * Implementation does not support class-level attributes.
 	 */
 	@Override
-	protected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
+	protected @Nullable Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
 		return null;
 	}
 
@@ -89,14 +94,14 @@ public class MapBasedMethodSecurityMetadataSource extends AbstractFallbackMethod
 	 * applicable.
 	 */
 	@Override
-	protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
+	protected @Nullable Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
 		if (targetClass == null) {
 			return null;
 		}
 		return findAttributesSpecifiedAgainst(method, targetClass);
 	}
 
-	private List<ConfigAttribute> findAttributesSpecifiedAgainst(Method method, Class<?> clazz) {
+	private @Nullable List<ConfigAttribute> findAttributesSpecifiedAgainst(Method method, Class<?> clazz) {
 		RegisteredMethod registeredMethod = new RegisteredMethod(method, clazz);
 		if (this.methodMap.containsKey(registeredMethod)) {
 			return this.methodMap.get(registeredMethod);

+ 3 - 1
core/src/main/java/org/springframework/security/access/method/MethodSecurityMetadataSource.java

@@ -19,6 +19,8 @@ package org.springframework.security.access.method;
 import java.lang.reflect.Method;
 import java.util.Collection;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.access.SecurityMetadataSource;
 import org.springframework.security.authorization.AuthorizationManager;
@@ -37,6 +39,6 @@ import org.springframework.security.authorization.AuthorizationManager;
 @Deprecated
 public interface MethodSecurityMetadataSource extends SecurityMetadataSource {
 
-	Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass);
+	Collection<ConfigAttribute> getAttributes(Method method, @Nullable Class<?> targetClass);
 
 }

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

@@ -18,4 +18,7 @@
  * Provides {@code SecurityMetadataSource} implementations for securing Java method
  * invocations via different AOP libraries.
  */
+@NullMarked
 package org.springframework.security.access.method;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -21,4 +21,7 @@
  * {@link org.springframework.security.access.AccessDecisionManager AccessDecisionManager}
  * interface.
  */
+@NullMarked
 package org.springframework.security.access;
+
+import org.jspecify.annotations.NullMarked;

+ 4 - 1
core/src/main/java/org/springframework/security/access/prepost/PostInvocationAdviceProvider.java

@@ -21,6 +21,8 @@ import java.util.Collection;
 import org.aopalliance.intercept.MethodInvocation;
 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.security.access.AccessDeniedException;
 import org.springframework.security.access.AfterInvocationProvider;
@@ -40,6 +42,7 @@ import org.springframework.security.core.Authentication;
  * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
  * instead
  */
+@NullUnmarked
 @Deprecated
 public class PostInvocationAdviceProvider implements AfterInvocationProvider {
 
@@ -62,7 +65,7 @@ public class PostInvocationAdviceProvider implements AfterInvocationProvider {
 				returnedObject);
 	}
 
-	private PostInvocationAttribute findPostInvocationAttribute(Collection<ConfigAttribute> config) {
+	private @Nullable PostInvocationAttribute findPostInvocationAttribute(Collection<ConfigAttribute> config) {
 		for (ConfigAttribute attribute : config) {
 			if (attribute instanceof PostInvocationAttribute) {
 				return (PostInvocationAttribute) attribute;

+ 4 - 1
core/src/main/java/org/springframework/security/access/prepost/PreInvocationAuthorizationAdviceVoter.java

@@ -21,6 +21,8 @@ import java.util.Collection;
 import org.aopalliance.intercept.MethodInvocation;
 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.security.access.AccessDecisionVoter;
 import org.springframework.security.access.ConfigAttribute;
@@ -42,6 +44,7 @@ import org.springframework.security.core.Authentication;
  * {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}
  * instead
  */
+@NullUnmarked
 @Deprecated
 public class PreInvocationAuthorizationAdviceVoter implements AccessDecisionVoter<MethodInvocation> {
 
@@ -75,7 +78,7 @@ public class PreInvocationAuthorizationAdviceVoter implements AccessDecisionVote
 		return this.preAdvice.before(authentication, method, preAttr) ? ACCESS_GRANTED : ACCESS_DENIED;
 	}
 
-	private PreInvocationAttribute findPreInvocationAttribute(Collection<ConfigAttribute> config) {
+	private @Nullable PreInvocationAttribute findPreInvocationAttribute(Collection<ConfigAttribute> config) {
 		for (ConfigAttribute attribute : config) {
 			if (attribute instanceof PreInvocationAttribute) {
 				return (PreInvocationAttribute) attribute;

+ 7 - 4
core/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java

@@ -22,6 +22,8 @@ import java.util.Collection;
 import kotlinx.coroutines.reactive.ReactiveFlowKt;
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import reactor.core.Exceptions;
 import reactor.core.publisher.Flux;
@@ -54,6 +56,7 @@ import org.springframework.util.Assert;
  * or
  * {@link org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor}
  */
+@NullUnmarked
 @Deprecated
 public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor {
 
@@ -142,7 +145,7 @@ public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor
 			.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));
 	}
 
-	private static <T extends Publisher<?>> T proceed(final MethodInvocation invocation) {
+	private static <T extends Publisher<?>> @Nullable T proceed(final MethodInvocation invocation) {
 		try {
 			return (T) invocation.proceed();
 		}
@@ -151,7 +154,7 @@ public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor
 		}
 	}
 
-	private static Object flowProceed(final MethodInvocation invocation) {
+	private static @Nullable Object flowProceed(final MethodInvocation invocation) {
 		try {
 			return invocation.proceed();
 		}
@@ -160,7 +163,7 @@ public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor
 		}
 	}
 
-	private static PostInvocationAttribute findPostInvocationAttribute(Collection<ConfigAttribute> config) {
+	private static @Nullable PostInvocationAttribute findPostInvocationAttribute(Collection<ConfigAttribute> config) {
 		for (ConfigAttribute attribute : config) {
 			if (attribute instanceof PostInvocationAttribute) {
 				return (PostInvocationAttribute) attribute;
@@ -169,7 +172,7 @@ public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor
 		return null;
 	}
 
-	private static PreInvocationAttribute findPreInvocationAttribute(Collection<ConfigAttribute> config) {
+	private static @Nullable PreInvocationAttribute findPreInvocationAttribute(Collection<ConfigAttribute> config) {
 		for (ConfigAttribute attribute : config) {
 			if (attribute instanceof PreInvocationAttribute) {
 				return (PreInvocationAttribute) attribute;

+ 7 - 2
core/src/main/java/org/springframework/security/access/prepost/PrePostAnnotationSecurityMetadataSource.java

@@ -22,6 +22,9 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.access.ConfigAttribute;
@@ -54,6 +57,7 @@ import org.springframework.util.ClassUtils;
  * {@link org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager}
  * instead
  */
+@NullUnmarked
 @Deprecated
 public class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
 
@@ -98,7 +102,7 @@ public class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecur
 	}
 
 	@Override
-	public Collection<ConfigAttribute> getAllConfigAttributes() {
+	public @Nullable Collection<ConfigAttribute> getAllConfigAttributes() {
 		return null;
 	}
 
@@ -108,7 +112,8 @@ public class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecur
 	 * for the logic of this method. The ordering here is slightly different in that we
 	 * consider method-specific annotations on an interface before class-level ones.
 	 */
-	private <A extends Annotation> A findAnnotation(Method method, Class<?> targetClass, Class<A> annotationClass) {
+	private <A extends Annotation> @Nullable A findAnnotation(Method method, Class<?> targetClass,
+			Class<A> annotationClass) {
 		// The method may be on an interface, but we need attributes from the target
 		// class.
 		// If the target class is null, the method will be unchanged.

+ 6 - 3
core/src/main/java/org/springframework/security/access/prepost/PrePostInvocationAttributeFactory.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.access.prepost;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.aop.framework.AopInfrastructureBean;
 import org.springframework.security.authorization.AuthorizationManager;
 
@@ -29,9 +31,10 @@ import org.springframework.security.authorization.AuthorizationManager;
 @Deprecated
 public interface PrePostInvocationAttributeFactory extends AopInfrastructureBean {
 
-	PreInvocationAttribute createPreInvocationAttribute(String preFilterAttribute, String filterObject,
-			String preAuthorizeAttribute);
+	PreInvocationAttribute createPreInvocationAttribute(@Nullable String preFilterAttribute,
+			@Nullable String filterObject, @Nullable String preAuthorizeAttribute);
 
-	PostInvocationAttribute createPostInvocationAttribute(String postFilterAttribute, String postAuthorizeAttribute);
+	PostInvocationAttribute createPostInvocationAttribute(@Nullable String postFilterAttribute,
+			@Nullable String postAuthorizeAttribute);
 
 }

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

@@ -21,4 +21,7 @@
  * Other than the annotations themselves, the classes should be regarded as for internal
  * framework use and are liable to change without notice.
  */
+@NullMarked
 package org.springframework.security.access.prepost;
+
+import org.jspecify.annotations.NullMarked;

+ 5 - 1
core/src/main/java/org/springframework/security/access/vote/AbstractAclVoter.java

@@ -17,6 +17,8 @@
 package org.springframework.security.access.vote;
 
 import org.aopalliance.intercept.MethodInvocation;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.access.AccessDecisionVoter;
 import org.springframework.security.access.AuthorizationServiceException;
@@ -30,10 +32,12 @@ import org.springframework.util.Assert;
  * @deprecated Now used by only-deprecated classes. Generally speaking, in-memory ACL is
  * no longer advised, so no replacement is planned at this point.
  */
+@NullUnmarked
 @Deprecated
 public abstract class AbstractAclVoter implements AccessDecisionVoter<MethodInvocation> {
 
-	private Class<?> processDomainObjectClass;
+	@SuppressWarnings("NullAway.Init")
+	private @Nullable Class<?> processDomainObjectClass;
 
 	protected Object getDomainObjectInstance(MethodInvocation invocation) {
 		Object[] args = invocation.getArguments();

+ 6 - 1
core/src/main/java/org/springframework/security/access/vote/RoleHierarchyVoter.java

@@ -18,6 +18,9 @@ package org.springframework.security.access.vote;
 
 import java.util.Collection;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
@@ -33,10 +36,12 @@ import org.springframework.util.Assert;
  * {@link org.springframework.security.authorization.AuthorityAuthorizationManager#setRoleHierarchy}
  * instead
  */
+@NullUnmarked
 @Deprecated
 public class RoleHierarchyVoter extends RoleVoter {
 
-	private RoleHierarchy roleHierarchy = null;
+	@SuppressWarnings("NullAway")
+	private @Nullable RoleHierarchy roleHierarchy = null;
 
 	public RoleHierarchyVoter(RoleHierarchy roleHierarchy) {
 		Assert.notNull(roleHierarchy, "RoleHierarchy must not be null");

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

@@ -17,4 +17,7 @@
 /**
  * Implements a vote-based approach to authorization decisions.
  */
+@NullMarked
 package org.springframework.security.access.vote;
+
+import org.jspecify.annotations.NullMarked;

+ 3 - 1
core/src/main/java/org/springframework/security/aot/hint/CoreSecurityRuntimeHints.java

@@ -19,6 +19,8 @@ package org.springframework.security.aot.hint;
 import java.util.List;
 import java.util.stream.Stream;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.aot.hint.MemberCategory;
 import org.springframework.aot.hint.RuntimeHints;
 import org.springframework.aot.hint.RuntimeHintsRegistrar;
@@ -54,7 +56,7 @@ import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
 class CoreSecurityRuntimeHints implements RuntimeHintsRegistrar {
 
 	@Override
-	public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+	public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
 		registerExceptionEventsHints(hints);
 		registerExpressionEvaluationHints(hints);
 		registerMethodSecurityHints(hints);

+ 3 - 1
core/src/main/java/org/springframework/security/aot/hint/OneTimeTokenRuntimeHints.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.aot.hint;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.aot.hint.RuntimeHints;
 import org.springframework.aot.hint.RuntimeHintsRegistrar;
 import org.springframework.jdbc.core.JdbcOperations;
@@ -33,7 +35,7 @@ import org.springframework.security.authentication.ott.OneTimeTokenService;
 class OneTimeTokenRuntimeHints implements RuntimeHintsRegistrar {
 
 	@Override
-	public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+	public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
 		hints.resources().registerPattern("org/springframework/security/core/ott/jdbc/one-time-tokens-schema.sql");
 	}
 

+ 20 - 0
core/src/main/java/org/springframework/security/aot/hint/package-info.java

@@ -0,0 +1,20 @@
+/*
+ * Copyright 2002-2016 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.
+ */
+
+@NullMarked
+package org.springframework.security.aot.hint;
+
+import org.jspecify.annotations.NullMarked;

+ 7 - 5
core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java

@@ -21,6 +21,8 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.core.AuthenticatedPrincipal;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.CredentialsContainer;
@@ -41,7 +43,7 @@ public abstract class AbstractAuthenticationToken implements Authentication, Cre
 
 	private final Collection<GrantedAuthority> authorities;
 
-	private Object details;
+	private @Nullable Object details;
 
 	private boolean authenticated = false;
 
@@ -50,7 +52,7 @@ public abstract class AbstractAuthenticationToken implements Authentication, Cre
 	 * @param authorities the collection of <tt>GrantedAuthority</tt>s for the principal
 	 * represented by this authentication object.
 	 */
-	public AbstractAuthenticationToken(Collection<? extends GrantedAuthority> authorities) {
+	public AbstractAuthenticationToken(@Nullable Collection<? extends GrantedAuthority> authorities) {
 		if (authorities == null) {
 			this.authorities = AuthorityUtils.NO_AUTHORITIES;
 			return;
@@ -91,11 +93,11 @@ public abstract class AbstractAuthenticationToken implements Authentication, Cre
 	}
 
 	@Override
-	public Object getDetails() {
+	public @Nullable Object getDetails() {
 		return this.details;
 	}
 
-	public void setDetails(Object details) {
+	public void setDetails(@Nullable Object details) {
 		this.details = details;
 	}
 
@@ -111,7 +113,7 @@ public abstract class AbstractAuthenticationToken implements Authentication, Cre
 		eraseSecret(this.details);
 	}
 
-	private void eraseSecret(Object secret) {
+	private void eraseSecret(@Nullable Object secret) {
 		if (secret instanceof CredentialsContainer container) {
 			container.eraseCredentials();
 		}

+ 11 - 7
core/src/main/java/org/springframework/security/authentication/AbstractUserDetailsReactiveAuthenticationManager.java

@@ -18,6 +18,7 @@ package org.springframework.security.authentication;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Mono;
 import reactor.core.scheduler.Scheduler;
 import reactor.core.scheduler.Schedulers;
@@ -60,7 +61,7 @@ public abstract class AbstractUserDetailsReactiveAuthenticationManager
 
 	private PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
 
-	private ReactiveUserDetailsPasswordService userDetailsPasswordService;
+	private ReactiveUserDetailsPasswordService userDetailsPasswordService = ReactiveUserDetailsPasswordService.NOOP;
 
 	private Scheduler scheduler = Schedulers.boundedElastic();
 
@@ -68,7 +69,7 @@ public abstract class AbstractUserDetailsReactiveAuthenticationManager
 
 	private UserDetailsChecker postAuthenticationChecks = this::defaultPostAuthenticationChecks;
 
-	private ReactiveCompromisedPasswordChecker compromisedPasswordChecker;
+	private @Nullable ReactiveCompromisedPasswordChecker compromisedPasswordChecker;
 
 	private void defaultPreAuthenticationChecks(UserDetails user) {
 		if (!user.isAccountNonLocked()) {
@@ -99,7 +100,8 @@ public abstract class AbstractUserDetailsReactiveAuthenticationManager
 	@Override
 	public Mono<Authentication> authenticate(Authentication authentication) {
 		String username = authentication.getName();
-		String presentedPassword = (String) authentication.getCredentials();
+		String presentedPassword = (authentication.getCredentials() != null)
+				? authentication.getCredentials().toString() : null;
 		// @formatter:off
 		return retrieveUser(username)
 				.doOnNext(this.preAuthenticationChecks::check)
@@ -113,7 +115,7 @@ public abstract class AbstractUserDetailsReactiveAuthenticationManager
 		// @formatter:on
 	}
 
-	private Mono<Void> checkCompromisedPassword(String password) {
+	private Mono<Void> checkCompromisedPassword(@Nullable String password) {
 		if (this.compromisedPasswordChecker == null) {
 			return Mono.empty();
 		}
@@ -123,9 +125,10 @@ public abstract class AbstractUserDetailsReactiveAuthenticationManager
 					"The provided password is compromised, please change your password")));
 	}
 
-	private Mono<UserDetails> upgradeEncodingIfNecessary(UserDetails userDetails, String presentedPassword) {
-		boolean upgradeEncoding = this.userDetailsPasswordService != null
-				&& this.passwordEncoder.upgradeEncoding(userDetails.getPassword());
+	private Mono<UserDetails> upgradeEncodingIfNecessary(UserDetails userDetails, @Nullable String presentedPassword) {
+		String existingEncodedPassword = userDetails.getPassword();
+		boolean upgradeEncoding = existingEncodedPassword != null
+				&& this.passwordEncoder.upgradeEncoding(existingEncodedPassword);
 		if (upgradeEncoding) {
 			String newPassword = this.passwordEncoder.encode(presentedPassword);
 			return this.userDetailsPasswordService.updatePassword(userDetails, newPassword);
@@ -170,6 +173,7 @@ public abstract class AbstractUserDetailsReactiveAuthenticationManager
 	 * @param userDetailsPasswordService the service to use
 	 */
 	public void setUserDetailsPasswordService(ReactiveUserDetailsPasswordService userDetailsPasswordService) {
+		Assert.notNull(userDetailsPasswordService, "userDetailsPasswordService cannot be null");
 		this.userDetailsPasswordService = userDetailsPasswordService;
 	}
 

+ 3 - 1
core/src/main/java/org/springframework/security/authentication/AnonymousAuthenticationProvider.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.authentication;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.context.MessageSource;
 import org.springframework.context.MessageSourceAware;
 import org.springframework.context.support.MessageSourceAccessor;
@@ -45,7 +47,7 @@ public class AnonymousAuthenticationProvider implements AuthenticationProvider,
 	}
 
 	@Override
-	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+	public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
 		if (!supports(authentication.getClass())) {
 			return null;
 		}

+ 7 - 6
core/src/main/java/org/springframework/security/authentication/AuthenticationObservationContext.java

@@ -17,6 +17,7 @@
 package org.springframework.security.authentication;
 
 import io.micrometer.observation.Observation;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.util.Assert;
@@ -29,17 +30,17 @@ import org.springframework.util.Assert;
  */
 public class AuthenticationObservationContext extends Observation.Context {
 
-	private Authentication authenticationRequest;
+	private @Nullable Authentication authenticationRequest;
 
-	private Class<?> authenticationManager;
+	private @Nullable Class<?> authenticationManager;
 
-	private Authentication authenticationResult;
+	private @Nullable Authentication authenticationResult;
 
 	/**
 	 * Get the {@link Authentication} request that was observed
 	 * @return the observed {@link Authentication} request
 	 */
-	public Authentication getAuthenticationRequest() {
+	public @Nullable Authentication getAuthenticationRequest() {
 		return this.authenticationRequest;
 	}
 
@@ -60,7 +61,7 @@ public class AuthenticationObservationContext extends Observation.Context {
 	 * observed. In that case, this returns {@code null}.
 	 * @return any observed {@link Authentication} result, {@code null} otherwise
 	 */
-	public Authentication getAuthenticationResult() {
+	public @Nullable Authentication getAuthenticationResult() {
 		return this.authenticationResult;
 	}
 
@@ -76,7 +77,7 @@ public class AuthenticationObservationContext extends Observation.Context {
 	 * Get the {@link AuthenticationManager} class that processed the authentication
 	 * @return the observed {@link AuthenticationManager} class
 	 */
-	public Class<?> getAuthenticationManagerClass() {
+	public @Nullable Class<?> getAuthenticationManagerClass() {
 		return this.authenticationManager;
 	}
 

+ 3 - 6
core/src/main/java/org/springframework/security/authentication/AuthenticationObservationConvention.java

@@ -21,9 +21,7 @@ import java.util.Locale;
 import io.micrometer.common.KeyValues;
 import io.micrometer.observation.Observation;
 import io.micrometer.observation.ObservationConvention;
-import org.jetbrains.annotations.NotNull;
-
-import org.springframework.lang.NonNull;
+import org.jspecify.annotations.NonNull;
 
 /**
  * An {@link ObservationConvention} for translating authentications into
@@ -63,9 +61,8 @@ public final class AuthenticationObservationConvention
 	/**
 	 * {@inheritDoc}
 	 */
-	@NotNull
 	@Override
-	public KeyValues getLowCardinalityKeyValues(@NonNull AuthenticationObservationContext context) {
+	public @NonNull KeyValues getLowCardinalityKeyValues(@NonNull AuthenticationObservationContext context) {
 		return KeyValues.of("authentication.request.type", getAuthenticationType(context))
 			.and("authentication.method", getAuthenticationMethod(context))
 			.and("authentication.result.type", getAuthenticationResult(context))
@@ -104,7 +101,7 @@ public final class AuthenticationObservationConvention
 	 * {@inheritDoc}
 	 */
 	@Override
-	public boolean supportsContext(@NotNull Observation.Context context) {
+	public boolean supportsContext(Observation.Context context) {
 		return context instanceof AuthenticationObservationContext;
 	}
 

+ 3 - 1
core/src/main/java/org/springframework/security/authentication/AuthenticationProvider.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.authentication;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 
@@ -39,7 +41,7 @@ public interface AuthenticationProvider {
 	 * <code>Authentication</code> class will be tried.
 	 * @throws AuthenticationException if authentication fails.
 	 */
-	Authentication authenticate(Authentication authentication) throws AuthenticationException;
+	@Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException;
 
 	/**
 	 * Returns <code>true</code> if this <Code>AuthenticationProvider</code> supports the

+ 3 - 1
core/src/main/java/org/springframework/security/authentication/AuthenticationServiceException.java

@@ -18,6 +18,8 @@ package org.springframework.security.authentication;
 
 import java.io.Serial;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.core.AuthenticationException;
 
 /**
@@ -49,7 +51,7 @@ public class AuthenticationServiceException extends AuthenticationException {
 	 * @param msg the detail message
 	 * @param cause root cause
 	 */
-	public AuthenticationServiceException(String msg, Throwable cause) {
+	public AuthenticationServiceException(@Nullable String msg, Throwable cause) {
 		super(msg, cause);
 	}
 

+ 7 - 3
core/src/main/java/org/springframework/security/authentication/DefaultAuthenticationEventPublisher.java

@@ -24,6 +24,7 @@ import java.util.Properties;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationEventPublisherAware;
@@ -71,13 +72,15 @@ public class DefaultAuthenticationEventPublisher
 
 	private final HashMap<String, Constructor<? extends AbstractAuthenticationEvent>> exceptionMappings = new HashMap<>();
 
-	private Constructor<? extends AbstractAuthenticationFailureEvent> defaultAuthenticationFailureEventConstructor;
+	private @Nullable Constructor<? extends AbstractAuthenticationFailureEvent> defaultAuthenticationFailureEventConstructor;
 
 	public DefaultAuthenticationEventPublisher() {
-		this(null);
+		this((event) -> {
+		});
 	}
 
 	public DefaultAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
+		Assert.notNull(applicationEventPublisher, "applicationEventPublisher cannot be null");
 		this.applicationEventPublisher = applicationEventPublisher;
 		addMapping(BadCredentialsException.class.getName(), AuthenticationFailureBadCredentialsEvent.class);
 		addMapping(UsernameNotFoundException.class.getName(), AuthenticationFailureBadCredentialsEvent.class);
@@ -123,7 +126,8 @@ public class DefaultAuthenticationEventPublisher
 		}
 	}
 
-	private Constructor<? extends AbstractAuthenticationEvent> getEventConstructor(AuthenticationException exception) {
+	private @Nullable Constructor<? extends AbstractAuthenticationEvent> getEventConstructor(
+			AuthenticationException exception) {
 		Constructor<? extends AbstractAuthenticationEvent> eventConstructor = this.exceptionMappings
 			.get(exception.getClass().getName());
 		return (eventConstructor != null) ? eventConstructor : this.defaultAuthenticationFailureEventConstructor;

+ 3 - 1
core/src/main/java/org/springframework/security/authentication/InternalAuthenticationServiceException.java

@@ -18,6 +18,8 @@ package org.springframework.security.authentication;
 
 import java.io.Serial;
 
+import org.jspecify.annotations.Nullable;
+
 /**
  * <p>
  * Thrown if an authentication request could not be processed due to a system problem that
@@ -42,7 +44,7 @@ public class InternalAuthenticationServiceException extends AuthenticationServic
 	@Serial
 	private static final long serialVersionUID = -6029644854192497840L;
 
-	public InternalAuthenticationServiceException(String message, Throwable cause) {
+	public InternalAuthenticationServiceException(@Nullable String message, Throwable cause) {
 		super(message, cause);
 	}
 

+ 1 - 0
core/src/main/java/org/springframework/security/authentication/ObservationAuthenticationManager.java

@@ -46,6 +46,7 @@ public final class ObservationAuthenticationManager implements AuthenticationMan
 	}
 
 	@Override
+	@SuppressWarnings("NullAway") // Dataflow analysis limitation
 	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
 		AuthenticationObservationContext context = new AuthenticationObservationContext();
 		context.setAuthenticationRequest(authentication);

+ 3 - 2
core/src/main/java/org/springframework/security/authentication/ProviderManager.java

@@ -22,6 +22,7 @@ import java.util.List;
 
 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.context.MessageSource;
@@ -97,7 +98,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
 
 	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
 
-	private AuthenticationManager parent;
+	private @Nullable AuthenticationManager parent;
 
 	private boolean eraseCredentialsAfterAuthentication = true;
 
@@ -122,7 +123,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
 	 * @param providers the {@link AuthenticationProvider}s to use
 	 * @param parent a parent {@link AuthenticationManager} to fall back to
 	 */
-	public ProviderManager(List<AuthenticationProvider> providers, AuthenticationManager parent) {
+	public ProviderManager(List<AuthenticationProvider> providers, @Nullable AuthenticationManager parent) {
 		Assert.notNull(providers, "providers list cannot be null");
 		this.providers = providers;
 		this.parent = parent;

+ 3 - 1
core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationProvider.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.authentication;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.context.MessageSource;
 import org.springframework.context.MessageSourceAware;
@@ -49,7 +51,7 @@ public class RememberMeAuthenticationProvider implements AuthenticationProvider,
 	}
 
 	@Override
-	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+	public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
 		if (!supports(authentication.getClass())) {
 			return null;
 		}

+ 8 - 6
core/src/main/java/org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java

@@ -18,6 +18,8 @@ package org.springframework.security.authentication;
 
 import java.util.Collection;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.SpringSecurityCoreVersion;
 import org.springframework.util.Assert;
@@ -40,7 +42,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
 
 	private final Object principal;
 
-	private Object credentials;
+	private @Nullable Object credentials;
 
 	/**
 	 * This constructor can be safely used by any code that wishes to create a
@@ -48,7 +50,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
 	 * will return <code>false</code>.
 	 *
 	 */
-	public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
+	public UsernamePasswordAuthenticationToken(Object principal, @Nullable Object credentials) {
 		super(null);
 		this.principal = principal;
 		this.credentials = credentials;
@@ -64,7 +66,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
 	 * @param credentials
 	 * @param authorities
 	 */
-	public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
+	public UsernamePasswordAuthenticationToken(Object principal, @Nullable Object credentials,
 			Collection<? extends GrantedAuthority> authorities) {
 		super(authorities);
 		this.principal = principal;
@@ -81,7 +83,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
 	 *
 	 * @since 5.7
 	 */
-	public static UsernamePasswordAuthenticationToken unauthenticated(Object principal, Object credentials) {
+	public static UsernamePasswordAuthenticationToken unauthenticated(Object principal, @Nullable Object credentials) {
 		return new UsernamePasswordAuthenticationToken(principal, credentials);
 	}
 
@@ -94,13 +96,13 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
 	 *
 	 * @since 5.7
 	 */
-	public static UsernamePasswordAuthenticationToken authenticated(Object principal, Object credentials,
+	public static UsernamePasswordAuthenticationToken authenticated(Object principal, @Nullable Object credentials,
 			Collection<? extends GrantedAuthority> authorities) {
 		return new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
 	}
 
 	@Override
-	public Object getCredentials() {
+	public @Nullable Object getCredentials() {
 		return this.credentials;
 	}
 

+ 14 - 6
core/src/main/java/org/springframework/security/authentication/dao/DaoAuthenticationProvider.java

@@ -18,6 +18,8 @@ package org.springframework.security.authentication.dao;
 
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.InternalAuthenticationServiceException;
@@ -60,15 +62,16 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
 	 * {@link PasswordEncoder} implementations will short circuit if the password is not
 	 * in a valid format.
 	 */
-	private volatile String userNotFoundEncodedPassword;
+	private volatile @Nullable String userNotFoundEncodedPassword;
 
-	private UserDetailsService userDetailsService;
+	private final UserDetailsService userDetailsService;
 
-	private UserDetailsPasswordService userDetailsPasswordService;
+	private UserDetailsPasswordService userDetailsPasswordService = UserDetailsPasswordService.NOOP;
 
-	private CompromisedPasswordChecker compromisedPasswordChecker;
+	private @Nullable CompromisedPasswordChecker compromisedPasswordChecker;
 
 	public DaoAuthenticationProvider(UserDetailsService userDetailsService) {
+		Assert.notNull(userDetailsService, "userDetailsService cannot be null");
 		this.userDetailsService = userDetailsService;
 	}
 
@@ -120,14 +123,16 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
 	@Override
 	protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
 			UserDetails user) {
+		Assert.notNull(authentication.getCredentials(), "Authentication.getCredentials() cannot be null");
 		String presentedPassword = authentication.getCredentials().toString();
 		boolean isPasswordCompromised = this.compromisedPasswordChecker != null
 				&& this.compromisedPasswordChecker.check(presentedPassword).isCompromised();
 		if (isPasswordCompromised) {
 			throw new CompromisedPasswordException("The provided password is compromised, please change your password");
 		}
-		boolean upgradeEncoding = this.userDetailsPasswordService != null
-				&& this.passwordEncoder.get().upgradeEncoding(user.getPassword());
+		String existingEncodedPassword = user.getPassword();
+		boolean upgradeEncoding = existingEncodedPassword != null && this.userDetailsPasswordService != null
+				&& this.passwordEncoder.get().upgradeEncoding(existingEncodedPassword);
 		if (upgradeEncoding) {
 			String newPassword = this.passwordEncoder.get().encode(presentedPassword);
 			user = this.userDetailsPasswordService.updatePassword(user, newPassword);
@@ -143,6 +148,7 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
 
 	private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
 		if (authentication.getCredentials() != null) {
+			Assert.notNull(this.userNotFoundEncodedPassword, "userNotFoundEncodedPassword cannot be null");
 			String presentedPassword = authentication.getCredentials().toString();
 			this.passwordEncoder.get().matches(presentedPassword, this.userNotFoundEncodedPassword);
 		}
@@ -170,6 +176,7 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
 	}
 
 	public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
+		Assert.notNull(userDetailsPasswordService, "userDetailsPasswordService cannot be null");
 		this.userDetailsPasswordService = userDetailsPasswordService;
 	}
 
@@ -180,6 +187,7 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
 	 * @since 6.3
 	 */
 	public void setCompromisedPasswordChecker(CompromisedPasswordChecker compromisedPasswordChecker) {
+		Assert.notNull(compromisedPasswordChecker, "compromisedPasswordChecker cannot be null");
 		this.compromisedPasswordChecker = compromisedPasswordChecker;
 	}
 

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

@@ -17,4 +17,7 @@
 /**
  * An {@code AuthenticationProvider} which relies upon a data access object.
  */
+@NullMarked
 package org.springframework.security.authentication.dao;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -22,4 +22,7 @@
  * context. These events are received by all registered Spring
  * <code>ApplicationListener</code>s.
  */
+@NullMarked
 package org.springframework.security.authentication.event;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -31,6 +31,7 @@ import javax.security.auth.login.LoginException;
 
 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.context.ApplicationEventPublisher;
@@ -119,11 +120,12 @@ import org.springframework.util.ObjectUtils;
 public abstract class AbstractJaasAuthenticationProvider implements AuthenticationProvider,
 		ApplicationEventPublisherAware, InitializingBean, ApplicationListener<SessionDestroyedEvent> {
 
-	private ApplicationEventPublisher applicationEventPublisher;
+	private ApplicationEventPublisher applicationEventPublisher = (event) -> {
+	};
 
-	private AuthorityGranter[] authorityGranters;
+	private AuthorityGranter[] authorityGranters = new AuthorityGranter[0];
 
-	private JaasAuthenticationCallbackHandler[] callbackHandlers;
+	private JaasAuthenticationCallbackHandler[] callbackHandlers = new JaasAuthenticationCallbackHandler[0];
 
 	protected final Log log = LogFactory.getLog(getClass());
 
@@ -159,7 +161,7 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
 	 * loginContext.login() method fail.
 	 */
 	@Override
-	public Authentication authenticate(Authentication auth) throws AuthenticationException {
+	public @Nullable Authentication authenticate(Authentication auth) throws AuthenticationException {
 		if (!(auth instanceof UsernamePasswordAuthenticationToken request)) {
 			return null;
 		}
@@ -303,6 +305,7 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
 	 * @see JaasAuthenticationProvider
 	 */
 	public void setAuthorityGranters(AuthorityGranter[] authorityGranters) {
+		Assert.notNull(authorityGranters, "authorityGranters cannot be null");
 		this.authorityGranters = authorityGranters;
 	}
 
@@ -323,6 +326,7 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
 	 * @param callbackHandlers Array of JAASAuthenticationCallbackHandlers
 	 */
 	public void setCallbackHandlers(JaasAuthenticationCallbackHandler[] callbackHandlers) {
+		Assert.notNull(callbackHandlers, "callbackHandlers cannot be null");
 		this.callbackHandlers = callbackHandlers;
 	}
 
@@ -354,6 +358,7 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
 
 	@Override
 	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
+		Assert.notNull(applicationEventPublisher, "applicationEventPublisher cannot be null");
 		this.applicationEventPublisher = applicationEventPublisher;
 	}
 

+ 1 - 0
core/src/main/java/org/springframework/security/authentication/jaas/DefaultJaasAuthenticationProvider.java

@@ -87,6 +87,7 @@ import org.springframework.util.Assert;
  */
 public class DefaultJaasAuthenticationProvider extends AbstractJaasAuthenticationProvider {
 
+	@SuppressWarnings("NullAway.Init")
 	private Configuration configuration;
 
 	@Override

+ 1 - 0
core/src/main/java/org/springframework/security/authentication/jaas/JaasAuthenticationProvider.java

@@ -143,6 +143,7 @@ public class JaasAuthenticationProvider extends AbstractJaasAuthenticationProvid
 	// exists for passivity
 	protected static final Log log = LogFactory.getLog(JaasAuthenticationProvider.class);
 
+	@SuppressWarnings("NullAway.Init")
 	private Resource loginConfig;
 
 	private boolean refreshConfigurationOnStartup = true;

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

@@ -20,6 +20,8 @@ import java.util.List;
 
 import javax.security.auth.login.LoginContext;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.SpringSecurityCoreVersion;
@@ -36,12 +38,12 @@ public class JaasAuthenticationToken extends UsernamePasswordAuthenticationToken
 
 	private final transient LoginContext loginContext;
 
-	public JaasAuthenticationToken(Object principal, Object credentials, LoginContext loginContext) {
+	public JaasAuthenticationToken(Object principal, @Nullable Object credentials, LoginContext loginContext) {
 		super(principal, credentials);
 		this.loginContext = loginContext;
 	}
 
-	public JaasAuthenticationToken(Object principal, Object credentials, List<GrantedAuthority> authorities,
+	public JaasAuthenticationToken(Object principal, @Nullable Object credentials, List<GrantedAuthority> authorities,
 			LoginContext loginContext) {
 		super(principal, credentials, authorities);
 		this.loginContext = loginContext;

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

@@ -21,6 +21,7 @@ import javax.security.auth.callback.NameCallback;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.util.Assert;
 
 /**
  * The most basic Callbacks to be handled when using a LoginContext from JAAS, are the
@@ -55,6 +56,7 @@ public class JaasNameCallbackHandler implements JaasAuthenticationCallbackHandle
 		if (principal instanceof UserDetails) {
 			return ((UserDetails) principal).getUsername();
 		}
+		Assert.notNull(principal, "principal cannot be null");
 		return principal.toString();
 	}
 

+ 4 - 1
core/src/main/java/org/springframework/security/authentication/jaas/JaasPasswordCallbackHandler.java

@@ -47,7 +47,10 @@ public class JaasPasswordCallbackHandler implements JaasAuthenticationCallbackHa
 	@Override
 	public void handle(Callback callback, Authentication auth) {
 		if (callback instanceof PasswordCallback) {
-			((PasswordCallback) callback).setPassword(auth.getCredentials().toString().toCharArray());
+			Object credentials = auth.getCredentials();
+			if (credentials != null) {
+				((PasswordCallback) callback).setPassword(credentials.toString().toCharArray());
+			}
 		}
 	}
 

+ 7 - 4
core/src/main/java/org/springframework/security/authentication/jaas/SecurityContextLoginModule.java

@@ -25,6 +25,7 @@ import javax.security.auth.spi.LoginModule;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -60,9 +61,9 @@ public class SecurityContextLoginModule implements LoginModule {
 	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
 		.getContextHolderStrategy();
 
-	private Authentication authen;
+	private @Nullable Authentication authen;
 
-	private Subject subject;
+	private @Nullable Subject subject;
 
 	private boolean ignoreMissingAuthentication = false;
 
@@ -92,6 +93,7 @@ public class SecurityContextLoginModule implements LoginModule {
 		if (this.authen == null) {
 			return false;
 		}
+		Assert.notNull(this.subject, "subject cannot be null");
 		this.subject.getPrincipals().add(this.authen);
 		return true;
 	}
@@ -107,11 +109,11 @@ public class SecurityContextLoginModule implements LoginModule {
 		this.securityContextHolderStrategy = securityContextHolderStrategy;
 	}
 
-	Authentication getAuthentication() {
+	@Nullable Authentication getAuthentication() {
 		return this.authen;
 	}
 
-	Subject getSubject() {
+	@Nullable Subject getSubject() {
 		return this.subject;
 	}
 
@@ -165,6 +167,7 @@ public class SecurityContextLoginModule implements LoginModule {
 		if (this.authen == null) {
 			return false;
 		}
+		Assert.notNull(this.subject, "subject cannot be null");
 		this.subject.getPrincipals().remove(this.authen);
 		this.authen = null;
 		return true;

+ 3 - 0
core/src/main/java/org/springframework/security/authentication/jaas/event/package-info.java

@@ -18,4 +18,7 @@
  * JAAS authentication events which can be published to the Spring application context by
  * the JAAS authentication provider.
  */
+@NullMarked
 package org.springframework.security.authentication.jaas.event;
+
+import org.jspecify.annotations.NullMarked;

+ 5 - 3
core/src/main/java/org/springframework/security/authentication/jaas/memory/InMemoryConfiguration.java

@@ -22,6 +22,8 @@ import java.util.Map;
 import javax.security.auth.login.AppConfigurationEntry;
 import javax.security.auth.login.Configuration;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.util.Assert;
 
 /**
@@ -37,7 +39,7 @@ import org.springframework.util.Assert;
  */
 public class InMemoryConfiguration extends Configuration {
 
-	private final AppConfigurationEntry[] defaultConfiguration;
+	private final AppConfigurationEntry @Nullable [] defaultConfiguration;
 
 	private final Map<String, AppConfigurationEntry[]> mappedConfigurations;
 
@@ -71,14 +73,14 @@ public class InMemoryConfiguration extends Configuration {
 	 * {@link #getAppConfigurationEntry(String)}. Can be <code>null</code>.
 	 */
 	public InMemoryConfiguration(Map<String, AppConfigurationEntry[]> mappedConfigurations,
-			AppConfigurationEntry[] defaultConfiguration) {
+			AppConfigurationEntry @Nullable [] defaultConfiguration) {
 		Assert.notNull(mappedConfigurations, "mappedConfigurations cannot be null.");
 		this.mappedConfigurations = mappedConfigurations;
 		this.defaultConfiguration = defaultConfiguration;
 	}
 
 	@Override
-	public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+	public AppConfigurationEntry @Nullable [] getAppConfigurationEntry(String name) {
 		AppConfigurationEntry[] mappedResult = this.mappedConfigurations.get(name);
 		return (mappedResult != null) ? mappedResult : this.defaultConfiguration;
 	}

+ 3 - 0
core/src/main/java/org/springframework/security/authentication/jaas/memory/package-info.java

@@ -17,4 +17,7 @@
 /**
  * An in memory JAAS implementation.
  */
+@NullMarked
 package org.springframework.security.authentication.jaas.memory;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -17,4 +17,7 @@
 /**
  * An authentication provider for JAAS.
  */
+@NullMarked
 package org.springframework.security.authentication.jaas;
+
+import org.jspecify.annotations.NullMarked;

+ 3 - 3
core/src/main/java/org/springframework/security/authentication/ott/InMemoryOneTimeTokenService.java

@@ -22,7 +22,8 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.springframework.lang.NonNull;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.util.Assert;
 
 /**
@@ -41,7 +42,6 @@ public final class InMemoryOneTimeTokenService implements OneTimeTokenService {
 	private Clock clock = Clock.systemUTC();
 
 	@Override
-	@NonNull
 	public OneTimeToken generate(GenerateOneTimeTokenRequest request) {
 		String token = UUID.randomUUID().toString();
 		Instant expiresAt = this.clock.instant().plus(request.getExpiresIn());
@@ -52,7 +52,7 @@ public final class InMemoryOneTimeTokenService implements OneTimeTokenService {
 	}
 
 	@Override
-	public OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken) {
+	public @Nullable OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken) {
 		OneTimeToken ott = this.oneTimeTokenByToken.remove(authenticationToken.getTokenValue());
 		if (ott == null || isExpired(ott)) {
 			return null;

+ 7 - 4
core/src/main/java/org/springframework/security/authentication/ott/JdbcOneTimeTokenService.java

@@ -29,6 +29,7 @@ import java.util.function.Function;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.InitializingBean;
@@ -68,7 +69,7 @@ public final class JdbcOneTimeTokenService implements OneTimeTokenService, Dispo
 
 	private Clock clock = Clock.systemUTC();
 
-	private ThreadPoolTaskScheduler taskScheduler;
+	private @Nullable ThreadPoolTaskScheduler taskScheduler;
 
 	private static final String DEFAULT_CLEANUP_CRON = "@hourly";
 
@@ -144,7 +145,7 @@ public final class JdbcOneTimeTokenService implements OneTimeTokenService, Dispo
 	}
 
 	@Override
-	public OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken) {
+	public @Nullable OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken) {
 		Assert.notNull(authenticationToken, "authenticationToken cannot be null");
 
 		List<OneTimeToken> tokens = selectOneTimeToken(authenticationToken);
@@ -177,7 +178,7 @@ public final class JdbcOneTimeTokenService implements OneTimeTokenService, Dispo
 		this.jdbcOperations.update(DELETE_ONE_TIME_TOKEN_SQL, pss);
 	}
 
-	private ThreadPoolTaskScheduler createTaskScheduler(String cleanupCron) {
+	private @Nullable ThreadPoolTaskScheduler createTaskScheduler(String cleanupCron) {
 		if (cleanupCron == null) {
 			return null;
 		}
@@ -200,7 +201,9 @@ public final class JdbcOneTimeTokenService implements OneTimeTokenService, Dispo
 
 	@Override
 	public void afterPropertiesSet() throws Exception {
-		this.taskScheduler.afterPropertiesSet();
+		if (this.taskScheduler != null) {
+			this.taskScheduler.afterPropertiesSet();
+		}
 	}
 
 	@Override

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

@@ -20,6 +20,8 @@ import java.io.Serial;
 import java.util.Collection;
 import java.util.Collections;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.authentication.AbstractAuthenticationToken;
 import org.springframework.security.core.GrantedAuthority;
 
@@ -34,11 +36,11 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
 	@Serial
 	private static final long serialVersionUID = -8691636031126328365L;
 
-	private final Object principal;
+	private @Nullable final Object principal;
 
-	private String tokenValue;
+	private @Nullable String tokenValue;
 
-	public OneTimeTokenAuthenticationToken(Object principal, String tokenValue) {
+	public OneTimeTokenAuthenticationToken(@Nullable Object principal, String tokenValue) {
 		super(Collections.emptyList());
 		this.tokenValue = tokenValue;
 		this.principal = principal;
@@ -88,17 +90,17 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
 	 * Returns the one-time token value
 	 * @return
 	 */
-	public String getTokenValue() {
+	public @Nullable String getTokenValue() {
 		return this.tokenValue;
 	}
 
 	@Override
-	public Object getCredentials() {
+	public @Nullable Object getCredentials() {
 		return this.tokenValue;
 	}
 
 	@Override
-	public Object getPrincipal() {
+	public @Nullable Object getPrincipal() {
 		return this.principal;
 	}
 

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

@@ -16,8 +16,7 @@
 
 package org.springframework.security.authentication.ott;
 
-import org.springframework.lang.NonNull;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Interface for generating and consuming one-time tokens.
@@ -33,7 +32,6 @@ public interface OneTimeTokenService {
 	 * generate the token
 	 * @return the generated {@link OneTimeToken}, never {@code null}.
 	 */
-	@NonNull
 	OneTimeToken generate(GenerateOneTimeTokenRequest request);
 
 	/**
@@ -42,7 +40,6 @@ public interface OneTimeTokenService {
 	 * value to be consumed
 	 * @return the consumed {@link OneTimeToken} or {@code null} if the token is invalid
 	 */
-	@Nullable
-	OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken);
+	@Nullable OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken);
 
 }

+ 20 - 0
core/src/main/java/org/springframework/security/authentication/ott/package-info.java

@@ -0,0 +1,20 @@
+/*
+ * Copyright 2002-2025 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.
+ */
+
+@NullMarked
+package org.springframework.security.authentication.ott;
+
+import org.jspecify.annotations.NullMarked;

+ 20 - 0
core/src/main/java/org/springframework/security/authentication/ott/reactive/package-info.java

@@ -0,0 +1,20 @@
+/*
+ * Copyright 2002-2025 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.
+ */
+
+@NullMarked
+package org.springframework.security.authentication.ott.reactive;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -26,4 +26,7 @@
  * {@link org.springframework.security.authentication.AuthenticationProvider
  * AuthenticationProvider}s to which it delegates authentication requests.
  */
+@NullMarked
 package org.springframework.security.authentication;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -16,7 +16,7 @@
 
 package org.springframework.security.authentication.password;
 
-import org.springframework.lang.NonNull;
+import org.jspecify.annotations.Nullable;
 
 /**
  * An API for checking if a password has been compromised.
@@ -27,11 +27,12 @@ import org.springframework.lang.NonNull;
 public interface CompromisedPasswordChecker {
 
 	/**
-	 * Check whether the password is compromised
+	 * Check whether the password is compromised. If password is null, then the return
+	 * value must be false for {@link CompromisedPasswordDecision#isCompromised()} since a
+	 * null password represents no password (e.g. the user leverages Passkeys instead).
 	 * @param password the password to check
 	 * @return a non-null {@link CompromisedPasswordDecision}
 	 */
-	@NonNull
-	CompromisedPasswordDecision check(String password);
+	CompromisedPasswordDecision check(@Nullable String password);
 
 }

+ 5 - 2
core/src/main/java/org/springframework/security/authentication/password/ReactiveCompromisedPasswordChecker.java

@@ -16,6 +16,7 @@
 
 package org.springframework.security.authentication.password;
 
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Mono;
 
 /**
@@ -27,10 +28,12 @@ import reactor.core.publisher.Mono;
 public interface ReactiveCompromisedPasswordChecker {
 
 	/**
-	 * Check whether the password is compromised
+	 * Check whether the password is compromised. If password is null, then the return
+	 * value must be false for {@link CompromisedPasswordDecision#isCompromised()} since a
+	 * null password represents no password (e.g. the user leverages Passkeys instead).
 	 * @param password the password to check
 	 * @return a {@link Mono} containing the {@link CompromisedPasswordDecision}
 	 */
-	Mono<CompromisedPasswordDecision> check(String password);
+	Mono<CompromisedPasswordDecision> check(@Nullable String password);
 
 }

+ 20 - 0
core/src/main/java/org/springframework/security/authentication/password/package-info.java

@@ -0,0 +1,20 @@
+/*
+ * Copyright 2002-2025 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.
+ */
+
+@NullMarked
+package org.springframework.security.authentication.password;
+
+import org.jspecify.annotations.NullMarked;

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

@@ -18,7 +18,8 @@ package org.springframework.security.authorization;
 
 import java.util.function.Supplier;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.core.Authentication;
 
@@ -30,7 +31,7 @@ import org.springframework.security.core.Authentication;
  * @author Evgeniy Cheban
  */
 @FunctionalInterface
-public interface AuthorizationManager<T> {
+public interface AuthorizationManager<@Nullable T> {
 
 	/**
 	 * Determines if access should be granted for a specific authentication and object.
@@ -53,7 +54,6 @@ public interface AuthorizationManager<T> {
 	 * @return an {@link AuthorizationResult}
 	 * @since 6.4
 	 */
-	@Nullable
-	AuthorizationResult authorize(Supplier<Authentication> authentication, T object);
+	@Nullable AuthorizationResult authorize(Supplier<Authentication> authentication, T object);
 
 }

+ 8 - 5
core/src/main/java/org/springframework/security/authorization/AuthorizationObservationContext.java

@@ -17,6 +17,7 @@
 package org.springframework.security.authorization;
 
 import io.micrometer.observation.Observation;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.security.core.Authentication;
 import org.springframework.util.Assert;
@@ -29,11 +30,13 @@ import org.springframework.util.Assert;
  */
 public class AuthorizationObservationContext<T> extends Observation.Context {
 
-	private Authentication authentication;
+	// FIXME: Should we make this non-null?
+	private @Nullable Authentication authentication;
 
 	private final T object;
 
-	private AuthorizationResult authorizationResult;
+	// FIXME: Should we make this non-null?
+	private @Nullable AuthorizationResult authorizationResult;
 
 	public AuthorizationObservationContext(T object) {
 		Assert.notNull(object, "object cannot be null");
@@ -48,7 +51,7 @@ public class AuthorizationObservationContext<T> extends Observation.Context {
 	 * {@link Authentication}, this will return {@code null}.
 	 * @return any observed {@link Authentication}, {@code null} otherwise
 	 */
-	public Authentication getAuthentication() {
+	public @Nullable Authentication getAuthentication() {
 		return this.authentication;
 	}
 
@@ -73,7 +76,7 @@ public class AuthorizationObservationContext<T> extends Observation.Context {
 	 * @return the observed {@link AuthorizationResult}
 	 * @since 6.4
 	 */
-	public AuthorizationResult getAuthorizationResult() {
+	public @Nullable AuthorizationResult getAuthorizationResult() {
 		return this.authorizationResult;
 	}
 
@@ -82,7 +85,7 @@ public class AuthorizationObservationContext<T> extends Observation.Context {
 	 * @param authorizationResult the observed {@link AuthorizationResult}
 	 * @since 6.4
 	 */
-	public void setAuthorizationResult(AuthorizationResult authorizationResult) {
+	public void setAuthorizationResult(@Nullable AuthorizationResult authorizationResult) {
 		this.authorizationResult = authorizationResult;
 	}
 

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

@@ -16,6 +16,8 @@
 
 package org.springframework.security.authorization;
 
+import org.jspecify.annotations.Nullable;
+
 /**
  * A factory for wrapping arbitrary objects in authorization-related advice
  *
@@ -37,6 +39,6 @@ public interface AuthorizationProxyFactory {
 	 * @throws org.springframework.aop.framework.AopConfigException if a proxy cannot be
 	 * created
 	 */
-	<T> T proxy(T object);
+	<T> @Nullable T proxy(@Nullable T object);
 
 }

+ 5 - 3
core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java

@@ -22,6 +22,7 @@ import io.micrometer.observation.Observation;
 import io.micrometer.observation.ObservationConvention;
 import io.micrometer.observation.ObservationRegistry;
 import org.aopalliance.intercept.MethodInvocation;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.context.MessageSource;
 import org.springframework.context.MessageSourceAware;
@@ -62,7 +63,7 @@ public final class ObservationAuthorizationManager<T>
 	}
 
 	@Override
-	public AuthorizationResult authorize(Supplier<Authentication> authentication, T object) {
+	public @Nullable AuthorizationResult authorize(Supplier<Authentication> authentication, T object) {
 		AuthorizationObservationContext<T> context = new AuthorizationObservationContext<>(object);
 		Supplier<Authentication> wrapped = () -> {
 			context.setAuthentication(authentication.get());
@@ -109,12 +110,13 @@ public final class ObservationAuthorizationManager<T>
 	}
 
 	@Override
-	public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
+	public @Nullable Object handleDeniedInvocation(MethodInvocation methodInvocation,
+			AuthorizationResult authorizationResult) {
 		return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
 	}
 
 	@Override
-	public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
+	public @Nullable Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
 			AuthorizationResult authorizationResult) {
 		return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
 	}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff