From 05830d568172d0f65d78d10418d93ca435a2145b Mon Sep 17 00:00:00 2001 From: Andrey Litvitski Date: Sat, 11 Oct 2025 18:42:33 +0300 Subject: [PATCH 1/2] Mark `GrantedAuthority#getAuthority` as `@Nullable` Closes: gh-17999 Signed-off-by: Andrey Litvitski --- .../AllRequiredFactorsAuthorizationManager.java | 2 +- .../authorization/AuthorityReactiveAuthorizationManager.java | 5 +++-- .../org/springframework/security/core/GrantedAuthority.java | 4 +++- .../core/authority/mapping/SimpleAuthorityMapper.java | 5 ++++- .../web/servlet/response/SecurityMockMvcResultMatchers.java | 3 ++- .../DelegatingMissingAuthorityAccessDeniedHandler.java | 3 ++- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/springframework/security/authorization/AllRequiredFactorsAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AllRequiredFactorsAuthorizationManager.java index 9506f475fa5..8691b0333cb 100644 --- a/core/src/main/java/org/springframework/security/authorization/AllRequiredFactorsAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/AllRequiredFactorsAuthorizationManager.java @@ -99,7 +99,7 @@ public FactorAuthorizationDecision authorize(Supplier currentFactors) { Optional matchingAuthority = currentFactors.stream() - .filter((authority) -> authority.getAuthority().equals(requiredFactor.getAuthority())) + .filter((authority) -> Objects.equals(authority.getAuthority(), requiredFactor.getAuthority())) .findFirst(); if (!matchingAuthority.isPresent()) { return RequiredFactorError.createMissing(requiredFactor); diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java index f9661578284..d04e9efbfd9 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java @@ -17,6 +17,7 @@ package org.springframework.security.authorization; import java.util.List; +import java.util.Objects; import reactor.core.publisher.Mono; @@ -47,8 +48,8 @@ public Mono authorize(Mono authentication, // @formatter:off return authentication.filter(Authentication::isAuthenticated) .flatMapIterable(Authentication::getAuthorities) - .map(GrantedAuthority::getAuthority) - .any((grantedAuthority) -> this.authorities.stream().anyMatch((authority) -> authority.getAuthority().equals(grantedAuthority))) + .mapNotNull(GrantedAuthority::getAuthority) + .any((grantedAuthority) -> this.authorities.stream().anyMatch((authority) -> Objects.equals(authority.getAuthority(), grantedAuthority))) .map((granted) -> ((AuthorizationResult) new AuthorityAuthorizationDecision(granted, this.authorities))) .defaultIfEmpty(new AuthorityAuthorizationDecision(false, this.authorities)); // @formatter:on diff --git a/core/src/main/java/org/springframework/security/core/GrantedAuthority.java b/core/src/main/java/org/springframework/security/core/GrantedAuthority.java index 143b254b854..cc2b379acda 100644 --- a/core/src/main/java/org/springframework/security/core/GrantedAuthority.java +++ b/core/src/main/java/org/springframework/security/core/GrantedAuthority.java @@ -18,6 +18,8 @@ import java.io.Serializable; +import org.jspecify.annotations.Nullable; + import org.springframework.security.authorization.AuthorizationManager; /** @@ -46,6 +48,6 @@ public interface GrantedAuthority extends Serializable { * granted authority cannot be expressed as a String with sufficient * precision). */ - String getAuthority(); + @Nullable String getAuthority(); } diff --git a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java index 69f85d0b668..218e760d922 100644 --- a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java +++ b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java @@ -64,7 +64,10 @@ public void afterPropertiesSet() { public Set mapAuthorities(Collection authorities) { HashSet mapped = new HashSet<>(authorities.size()); for (GrantedAuthority authority : authorities) { - mapped.add(mapAuthority(authority.getAuthority())); + String authorityStr = authority.getAuthority(); + if (authorityStr != null) { + mapped.add(mapAuthority(authorityStr)); + } } if (this.defaultAuthority != null) { mapped.add(this.defaultAuthority); diff --git a/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java b/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java index c668a665d34..1b1e73badd0 100644 --- a/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java +++ b/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java @@ -281,7 +281,8 @@ public AuthenticatedMatcher withRoles(String rolePrefix, String[] roles) { for (String role : roles) { withPrefix.add(new SimpleGrantedAuthority(rolePrefix + role)); } - this.ignoreAuthorities = (authority) -> !authority.getAuthority().startsWith(rolePrefix); + this.ignoreAuthorities = (authority) -> (authority.getAuthority() != null + && !authority.getAuthority().startsWith(rolePrefix)); return withAuthorities(withPrefix); } diff --git a/web/src/main/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandler.java b/web/src/main/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandler.java index a4f42b9c86a..5c110affc8c 100644 --- a/web/src/main/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandler.java +++ b/web/src/main/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandler.java @@ -162,7 +162,8 @@ private List authorityErrors(AccessDeniedExce if (authorizationResult instanceof AuthorityAuthorizationDecision authorityDecision) { // @formatter:off return authorityDecision.getAuthorities().stream() - .map((grantedAuthority) -> { + .filter((ga) -> ga.getAuthority() != null) + .map((grantedAuthority) -> { String authority = grantedAuthority.getAuthority(); if (authority.startsWith("FACTOR_")) { RequiredFactor required = RequiredFactor.withAuthority(authority).build(); From 3f9f8f31ef913931b48501f36e483cdcaae98de9 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:33:27 -0600 Subject: [PATCH 2/2] Clarify Nullability in Granted Authority Lambda Issue gh-17999 --- .../authorization/AuthorityReactiveAuthorizationManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java index d04e9efbfd9..cc25cc5f9f9 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java @@ -18,7 +18,9 @@ import java.util.List; import java.util.Objects; +import java.util.function.Function; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Mono; import org.springframework.security.core.Authentication; @@ -48,7 +50,7 @@ public Mono authorize(Mono authentication, // @formatter:off return authentication.filter(Authentication::isAuthenticated) .flatMapIterable(Authentication::getAuthorities) - .mapNotNull(GrantedAuthority::getAuthority) + .mapNotNull((Function) GrantedAuthority::getAuthority) .any((grantedAuthority) -> this.authorities.stream().anyMatch((authority) -> Objects.equals(authority.getAuthority(), grantedAuthority))) .map((granted) -> ((AuthorizationResult) new AuthorityAuthorizationDecision(granted, this.authorities))) .defaultIfEmpty(new AuthorityAuthorizationDecision(false, this.authorities));