Avoid spring-core in password encoder validation#19341
Open
seonwooj0810 wants to merge 1 commit into
Open
Conversation
`spring-core` is an `optional` dependency of `spring-security-crypto`, so it is not published in the POM and is not guaranteed to be on the classpath when the module is used standalone. 7.1.0 refactored `AbstractValidatingPasswordEncoder` to use `org.springframework.util.StringUtils#hasLength`, which causes `NoClassDefFoundError` on `BCryptPasswordEncoder#matches` (and other encoders extending it) for standalone users. Restore the inline null/empty checks the encoder used prior to that refactor so that the runtime hot path no longer reaches into `spring-core`. Closes spring-projectsgh-19317 Signed-off-by: seonwoo_jung <laborlawseon@kap.kr>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes gh-19317
Root cause
spring-security-cryptodeclaresorg.springframework:spring-coreas anoptionaldependency incrypto/spring-security-crypto.gradle, so it is not propagated through the published POM. The module is intentionally usable standalone — historically the runtime code paths only relied on JDK types.In 7.1.0 the validation block in
AbstractValidatingPasswordEncoder#matchesand#upgradeEncodingwas refactored to useorg.springframework.util.StringUtils#hasLength(commit 9323775, "Update javadoc and applyStringUtils#hasLength"). That introduced a hard reference from a hot path to aspring-coreclass. Standalone consumers ofspring-security-crypto(e.g. anyone callingBCryptPasswordEncoder#matches) now hit:The invariant being violated is the module-level one expressed by the gradle file: the runtime hot path of
spring-security-cryptomust not depend onspring-core.@org.springframework.lang.Contractis also referenced in this class but hasRetentionPolicy.CLASS, so it is not loaded at runtime and is not affected.Change
Replace the two
StringUtils.hasLength(...)calls with the inline== null || isEmpty()checks that were in place before 9323775, and drop the now-unused import. The semantics are identical (StringUtils.hasLength(cs)is defined ascs != null && cs.length() > 0).KeyStoreKeyFactoryalso usesStringUtils.getFilenameExtension, but its constructor already takesorg.springframework.core.io.Resource, so users of that class necessarily havespring-coreon the classpath and it is not a regression. This PR leaves it untouched.Test evidence
The existing
AbstractPasswordEncoderValidationTests(extended byAbstractValidatingPasswordEncoderTests,BCryptPasswordEncoderTests, etc.) already exercises the null/emptyrawPasswordandencodedPasswordcases on the affected branch, and continues to pass:I considered adding a dedicated regression test that asserts the class can load without
spring-core, but driving an isolated classloader from a test that itself runs under the project's full classpath is more invasive than the fix and harder to maintain — happy to add one if maintainers prefer.