Skip to content

Add getName to SecurityFilterChain for trace logging#19162

Open
LivingLikeKrillin wants to merge 6 commits into
spring-projects:mainfrom
LivingLikeKrillin:gh-6274-securityfilterchain-getname
Open

Add getName to SecurityFilterChain for trace logging#19162
LivingLikeKrillin wants to merge 6 commits into
spring-projects:mainfrom
LivingLikeKrillin:gh-6274-securityfilterchain-getname

Conversation

@LivingLikeKrillin
Copy link
Copy Markdown

Closes gh-6274

Background

This issue has had two prior attempts at resolution:

Since then, in commit f46e56d (gh-15874), BeanNameAware was added to
DefaultSecurityFilterChain to improve error messages. The bean name is now
captured and surfaced through toString(), but the SecurityFilterChain
interface itself still has no name accessor, so consumers like
FilterChainProxy cannot include the matched chain identity in
"Securing/Secured" debug logs without instanceof checks.

Change

  • Add default @Nullable String getName() to SecurityFilterChain
    (returns null by default — fully backward compatible).
  • Override in DefaultSecurityFilterChain to return the bean name
    captured by the existing BeanNameAware integration.
  • Update FilterChainProxy.doFilterInternal to include [<name>] in
    DEBUG logs when a name is available; omit when null.

Commit-by-commit reviewability

The PR is split into six small commits so each can be reviewed in isolation:

  1. Add getName default to SecurityFilterChain — interface change only
  2. Override getName in DefaultSecurityFilterChain — bean-name override
  3. Extract matchChain helper in FilterChainProxy — pure refactor, no behavior change
  4. Include chain name in FilterChainProxy DEBUG log — the visible behavioral change
  5. Add WebTestUtils.findFilter regression test for FilterChainProxy
  6. Add getFilters(HttpServletRequest) regression test for CompositeFilterChainProxy

Happy to squash if the team prefers a single commit, but kept separate so the
"no behavior change" refactor (3) can be verified independently from the
behavioral change (4).

Preservation of the reflective getFilters(HttpServletRequest) contract

The existing private FilterChainProxy.getFilters(HttpServletRequest) is
preserved verbatim (name and signature). It is invoked reflectively by
WebTestUtils.findFilter at WebTestUtils.java:158 via
ReflectionTestUtils.invokeMethod(springSecurityFilterChain, "getFilters", request),
and an analogous private method exists on CompositeFilterChainProxy for the
same reason. The new matchChain helper is added alongside it, and
getFilters(HttpServletRequest) now delegates to matchChain. Two regression
tests lock this contract for both FilterChainProxy and
CompositeFilterChainProxy.

Why @Nullable String rather than Optional<String>

Return type is @Nullable String rather than Optional<String> for
consistency with the existing @Nullable beanName field on
DefaultSecurityFilterChain, and following Brian Goetz's guidance that
Optional is awkward as a property-style return type. The new method's
Javadoc explicitly marks the returned value as for diagnostic use only.

Out of scope (deliberately)

  • No setter on the interface. BeanNameAware covers the common path;
    custom implementations can return their own identity by overriding
    getName().
  • No reactive parity. SecurityWebFilterChain lacks BeanNameAware
    groundwork (no equivalent of f46e56de78 on the reactive side yet), so
    that change is meaningfully different. If this lands, I will open a
    separate issue for SecurityWebFilterChain parity.
  • No changes to DebugFilter or DefaultFilterChainValidator. Those
    consumers can adopt getName() in follow-up PRs once the interface
    change lands.

Alternative considered

If the team prefers to avoid an interface change, the same DEBUG output
can be achieved via an instanceof DefaultSecurityFilterChain cast in
FilterChainProxy. Happy to switch to that diff if a cast-based approach
is preferred.

Testing

  • SecurityFilterChainTests — interface default returns null.
  • DefaultSecurityFilterChainTestsgetName() returns the bean name when
    set; null otherwise.
  • FilterChainProxyTests — DEBUG log includes [<name>] suffix when the
    matched chain has a name; suffix omitted when null. Uses logback
    ListAppender for log capture.
  • WebTestUtilsTests — regression test for the reflective contract on
    plain FilterChainProxy.
  • CompositeFilterChainProxyTests — regression test for the same contract
    on WebSecurityConfiguration.CompositeFilterChainProxy (the package-private
    composite path used by Spring Boot annotation-based config).

Notes

  • No try/catch around getName() in FilterChainProxy, consistent with how
    matches() and getFilters() are already invoked. Diagnostics should
    not silently swallow exceptions.
  • Existing log scrapers using prefix matching (startsWith("Securing"))
    are unaffected. End-anchored regexes (^Securing .* /\S+$) may need
    adjustment.

Closes spring-projectsgh-6274

Signed-off-by: Jooyoung Jung <143606756+LivingLikeKrillin@users.noreply.github.com>
Returns the bean name captured via existing BeanNameAware integration.

Issue spring-projectsgh-6274

Signed-off-by: Jooyoung Jung <143606756+LivingLikeKrillin@users.noreply.github.com>
No behavior change. Adds a private matchChain(HttpServletRequest)
that returns the matched SecurityFilterChain rather than just its
filters. The existing private getFilters(HttpServletRequest)
(reflectively invoked by WebTestUtils.findFilter) is preserved
and now delegates to matchChain.

Issue spring-projectsgh-6274

Signed-off-by: Jooyoung Jung <143606756+LivingLikeKrillin@users.noreply.github.com>
Appends '[<chainName>]' suffix to the 'Securing' and 'Secured' DEBUG
log lines when the matched chain's getName() returns a non-null value.
The suffix is omitted when getName() returns null, preserving the
existing log format for unnamed chains.

Adds ch.qos.logback:logback-classic as a testImplementation dependency
so FilterChainProxyTests can use logback's ListAppender to assert the
captured log output.

Issue spring-projectsgh-6274

Signed-off-by: Jooyoung Jung <143606756+LivingLikeKrillin@users.noreply.github.com>
Locks down the reflective contract that WebTestUtils relies on
(invoking private FilterChainProxy.getFilters(HttpServletRequest)
via ReflectionTestUtils). Prevents accidental rename in future
refactors.

Issue spring-projectsgh-6274

Signed-off-by: Jooyoung Jung <143606756+LivingLikeKrillin@users.noreply.github.com>
…rChainProxy

Locks the reflective contract that WebTestUtils.findFilter relies on
(invoking the package-private subclass's private getFilters method
via ReflectionTestUtils). Mirrors the FilterChainProxy regression
test at the subclass level. Bypasses WebTestUtils.findFilter directly
because that method is package-private and CompositeFilterChainProxy
lives in a different package.

Issue spring-projectsgh-6274

Signed-off-by: Jooyoung Jung <143606756+LivingLikeKrillin@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Give SecurityFilterChain a name for tracing!

2 participants