From bd119ac41167132e33b1799bc34fc1ed5c3c45c4 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:28:57 -0600 Subject: [PATCH] Implement Equals and HashCode Internally, RequestMatcher is sometimes used as a key to a HashMap. Accordingly, each implementation should implement equals and hashCode. Closes gh-17842 --- .../util/matcher/MediaTypeRequestMatcher.java | 21 +++++++++++++++++++ .../util/matcher/NegatedRequestMatcher.java | 18 ++++++++++++++++ .../matcher/RequestHeaderRequestMatcher.java | 19 +++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/web/src/main/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcher.java b/web/src/main/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcher.java index 478951fb70..f59ff8bb1f 100644 --- a/web/src/main/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcher.java +++ b/web/src/main/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcher.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; import jakarta.servlet.http.HttpServletRequest; @@ -251,6 +252,26 @@ public final class MediaTypeRequestMatcher implements RequestMatcher { this.ignoredMediaTypes = ignoredMediaTypes; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof MediaTypeRequestMatcher that)) { + return false; + } + return Objects.equals(this.contentNegotiationStrategy.getClass(), that.contentNegotiationStrategy.getClass()) + && Objects.equals(this.useEquals, that.useEquals) + && Objects.equals(this.matchingMediaTypes, that.matchingMediaTypes) + && Objects.equals(this.ignoredMediaTypes, that.ignoredMediaTypes); + } + + @Override + public int hashCode() { + return Objects.hash(this.contentNegotiationStrategy.getClass(), this.useEquals, this.matchingMediaTypes, + this.ignoredMediaTypes); + } + @Override public String toString() { return "MediaTypeRequestMatcher [contentNegotiationStrategy=" + this.contentNegotiationStrategy diff --git a/web/src/main/java/org/springframework/security/web/util/matcher/NegatedRequestMatcher.java b/web/src/main/java/org/springframework/security/web/util/matcher/NegatedRequestMatcher.java index 52f869b2a7..158b2511c2 100644 --- a/web/src/main/java/org/springframework/security/web/util/matcher/NegatedRequestMatcher.java +++ b/web/src/main/java/org/springframework/security/web/util/matcher/NegatedRequestMatcher.java @@ -16,6 +16,8 @@ package org.springframework.security.web.util.matcher; +import java.util.Objects; + import jakarta.servlet.http.HttpServletRequest; import org.springframework.util.Assert; @@ -47,6 +49,22 @@ public class NegatedRequestMatcher implements RequestMatcher { return !this.requestMatcher.matches(request); } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof NegatedRequestMatcher that)) { + return false; + } + return Objects.equals(this.requestMatcher, that.requestMatcher); + } + + @Override + public int hashCode() { + return Objects.hash(this.requestMatcher); + } + @Override public String toString() { return "Not [" + this.requestMatcher + "]"; diff --git a/web/src/main/java/org/springframework/security/web/util/matcher/RequestHeaderRequestMatcher.java b/web/src/main/java/org/springframework/security/web/util/matcher/RequestHeaderRequestMatcher.java index e1571f625c..3d07b18302 100644 --- a/web/src/main/java/org/springframework/security/web/util/matcher/RequestHeaderRequestMatcher.java +++ b/web/src/main/java/org/springframework/security/web/util/matcher/RequestHeaderRequestMatcher.java @@ -16,6 +16,8 @@ package org.springframework.security.web.util.matcher; +import java.util.Objects; + import jakarta.servlet.http.HttpServletRequest; import org.jspecify.annotations.Nullable; @@ -91,6 +93,23 @@ public final class RequestHeaderRequestMatcher implements RequestMatcher { return this.expectedHeaderValue.equals(actualHeaderValue); } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof RequestHeaderRequestMatcher that)) { + return false; + } + return Objects.equals(this.expectedHeaderName, that.expectedHeaderName) + && Objects.equals(this.expectedHeaderValue, that.expectedHeaderValue); + } + + @Override + public int hashCode() { + return Objects.hash(this.expectedHeaderName, this.expectedHeaderValue); + } + @Override public String toString() { return "RequestHeaderRequestMatcher [expectedHeaderName=" + this.expectedHeaderName + ", expectedHeaderValue="