You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2026/05/28 13:30:31 UTC

(camel) branch camel-4.18.x updated: [backport camel-4.18.x] CAMEL-23592: camel-shiro - align Exchange header constant names with Camel naming convention (#23552)

This is an automated email from the ASF dual-hosted git repository.

oscerd pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.18.x by this push:
     new 7fce68dcc8eb [backport camel-4.18.x] CAMEL-23592: camel-shiro - align Exchange header constant names with Camel naming convention (#23552)
7fce68dcc8eb is described below

commit 7fce68dcc8ebe731a6f53ff710e5cf6752a1897d
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Thu May 28 15:30:23 2026 +0200

    [backport camel-4.18.x] CAMEL-23592: camel-shiro - align Exchange header constant names with Camel naming convention (#23552)
    
    * CAMEL-23592: camel-shiro - align Exchange header constant names with Camel naming convention (#23506)
    
    Renames the three Exchange header string values in ShiroSecurityConstants
    that drive Shiro authentication (SHIRO_SECURITY_TOKEN,
    SHIRO_SECURITY_USERNAME, SHIRO_SECURITY_PASSWORD) to CamelShiroSecurity<Name>,
    following the convention used across the rest of the Camel component catalog
    and matching the pattern established in CAMEL-23526 (camel-cxf), CAMEL-23522
    (camel-mail), CAMEL-23461 (camel-aws-bedrock), CAMEL-23532
    (camel-vertx-websocket / camel-atmosphere-websocket / camel-iggy), and
    CAMEL-23576 (camel-jira).
    
    - SHIRO_SECURITY_TOKEN: "SHIRO_SECURITY_TOKEN" -> "CamelShiroSecurityToken"
    - SHIRO_SECURITY_USERNAME: "SHIRO_SECURITY_USERNAME" -> "CamelShiroSecurityUsername"
    - SHIRO_SECURITY_PASSWORD: "SHIRO_SECURITY_PASSWORD" -> "CamelShiroSecurityPassword"
    
    These headers carry credentials and a serialized authentication token, so
    filtering them at transport boundaries by default is particularly important.
    
    The Java field names are unchanged so routes referencing the constants
    symbolically continue to work; routes using the literal string values must be
    updated (documented in the 4.21 upgrade guide). No tests reference the
    literal values, and the shiro adoc documentation references the constants
    symbolically.
    
    Tracker: CAMEL-23577
    
    Reported by Claude Code on behalf of Andrea Cosentino
    
    Signed-off-by: Andrea Cosentino <an...@gmail.com>
    
    * CAMEL-23592: camel-itest - update ShiroOverJmsTest for renamed Shiro security headers
    
    After CAMEL-23592 the three Shiro security header values move into the
    Camel* namespace (CamelShiroSecurityToken etc.). The default
    JmsHeaderFilterStrategy filters every Camel* header at the transport
    boundary, which is the intended behavior for untrusted producers but
    strips the serialized authentication token from trusted Shiro-over-JMS
    routes that previously relied on the old non-Camel-prefixed value
    surviving the JMS round-trip.
    
    ShiroOverJmsTest is exactly such a trusted Shiro-over-JMS route and
    started failing on this backport branch's CI:
    
      mock://ShiroOverJmsTestError Received message count.
      Expected: <0> but was: <1>
    
    (the consumer-side ShiroSecurityPolicy could no longer read the token
    and the message ended up at the dead-letter mock). The same regression
    exists latently on main; the matching fix is in PR #23592.
    
    - ShiroOverJmsTest: configure a small subclass of JmsHeaderFilterStrategy
      on the JMS component that opts the three Shiro security headers back in
      (returns false from applyFilterToCamelHeaders /
      applyFilterToExternalHeaders for those headers, delegates to super for
      everything else). This is the pattern end-users will need.
    - 4.18 upgrade guide: extend the camel-shiro entry to document the new
      required HeaderFilterStrategy opt-in for trusted Shiro-over-transport
      routes, with a worked code example mirroring the one used in the test.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <no...@anthropic.com>
    Signed-off-by: Andrea Cosentino <an...@gmail.com>
    
    ---------
    
    Signed-off-by: Andrea Cosentino <an...@gmail.com>
    Co-authored-by: Claude Opus 4.7 (1M context) <no...@anthropic.com>
---
 .../shiro/security/ShiroSecurityConstants.java     |  6 +-
 .../ROOT/pages/camel-4x-upgrade-guide-4_18.adoc    | 65 ++++++++++++++++++++++
 .../apache/camel/itest/shiro/ShiroOverJmsTest.java | 35 ++++++++++++
 3 files changed, 103 insertions(+), 3 deletions(-)

diff --git a/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityConstants.java b/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityConstants.java
index b3b30e800efa..4f8e7d21e5ad 100644
--- a/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityConstants.java
+++ b/components/camel-shiro/src/main/java/org/apache/camel/component/shiro/security/ShiroSecurityConstants.java
@@ -21,9 +21,9 @@ package org.apache.camel.component.shiro.security;
  */
 public final class ShiroSecurityConstants {
 
-    public static final String SHIRO_SECURITY_TOKEN = "SHIRO_SECURITY_TOKEN";
-    public static final String SHIRO_SECURITY_USERNAME = "SHIRO_SECURITY_USERNAME";
-    public static final String SHIRO_SECURITY_PASSWORD = "SHIRO_SECURITY_PASSWORD";
+    public static final String SHIRO_SECURITY_TOKEN = "CamelShiroSecurityToken";
+    public static final String SHIRO_SECURITY_USERNAME = "CamelShiroSecurityUsername";
+    public static final String SHIRO_SECURITY_PASSWORD = "CamelShiroSecurityPassword";
 
     private ShiroSecurityConstants() {
     }
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
index 2553ea29377e..8a9a9c9fcf43 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
@@ -680,6 +680,71 @@ The generated Endpoint DSL header accessors on each component's
 
 
 
+=== camel-shiro - potential breaking change
+
+The three Exchange header constants in `ShiroSecurityConstants` that drive
+Shiro authentication used header values outside the `Camel` namespace
+(`SHIRO_SECURITY_TOKEN`, `SHIRO_SECURITY_USERNAME`, `SHIRO_SECURITY_PASSWORD`)
+and were therefore not filtered by the default `HeaderFilterStrategy`. They
+have been renamed to follow the Camel naming convention. The Java field names
+are unchanged; only the header string values have changed:
+
+[options="header"]
+|===
+| Constant | Previous value | New value
+| `ShiroSecurityConstants.SHIRO_SECURITY_TOKEN` | `SHIRO_SECURITY_TOKEN` | `CamelShiroSecurityToken`
+| `ShiroSecurityConstants.SHIRO_SECURITY_USERNAME` | `SHIRO_SECURITY_USERNAME` | `CamelShiroSecurityUsername`
+| `ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD` | `SHIRO_SECURITY_PASSWORD` | `CamelShiroSecurityPassword`
+|===
+
+These headers carry credentials and a serialized authentication token, so
+filtering them at transport boundaries by default is particularly important.
+
+Routes that reference the constants symbolically (for example
+`setHeader(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, ...)`) continue to
+work without changes. Routes that set the header by its literal string value
+(for example `setHeader("SHIRO_SECURITY_USERNAME", ...)`) must be updated to
+use the new value (`setHeader("CamelShiroSecurityUsername", ...)`).
+
+Because the three header values are now in the `Camel*` namespace, transports
+that filter Camel-internal headers by default (JMS, CXF, HTTP, etc.) will
+strip the serialized Shiro authentication token before publishing. This is
+the intended behavior for untrusted producers. Trusted Shiro-over-transport
+routes that previously relied on the token surviving the boundary must opt
+those three headers back in via a custom `HeaderFilterStrategy`, for example:
+
+[source,java]
+----
+public class ShiroFriendlyJmsHeaderFilterStrategy extends JmsHeaderFilterStrategy {
+    @Override
+    public boolean applyFilterToCamelHeaders(String name, Object value, Exchange ex) {
+        if (isShiroSecurityHeader(name)) {
+            return false;
+        }
+        return super.applyFilterToCamelHeaders(name, value, ex);
+    }
+
+    @Override
+    public boolean applyFilterToExternalHeaders(String name, Object value, Exchange ex) {
+        if (isShiroSecurityHeader(name)) {
+            return false;
+        }
+        return super.applyFilterToExternalHeaders(name, value, ex);
+    }
+
+    private static boolean isShiroSecurityHeader(String name) {
+        return ShiroSecurityConstants.SHIRO_SECURITY_TOKEN.equalsIgnoreCase(name)
+                || ShiroSecurityConstants.SHIRO_SECURITY_USERNAME.equalsIgnoreCase(name)
+                || ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD.equalsIgnoreCase(name);
+    }
+}
+
+jmsComponent.setHeaderFilterStrategy(new ShiroFriendlyJmsHeaderFilterStrategy());
+----
+
+A worked example is in `ShiroOverJmsTest` in the `camel-itest` module.
+
+
 === camel-web3j - potential breaking change
 
 The Exchange header constants in `Web3jConstants` have been renamed to follow the
diff --git a/tests/camel-itest/src/test/java/org/apache/camel/itest/shiro/ShiroOverJmsTest.java b/tests/camel-itest/src/test/java/org/apache/camel/itest/shiro/ShiroOverJmsTest.java
index ccd5e8b2661f..5a702f8e9da8 100644
--- a/tests/camel-itest/src/test/java/org/apache/camel/itest/shiro/ShiroOverJmsTest.java
+++ b/tests/camel-itest/src/test/java/org/apache/camel/itest/shiro/ShiroOverJmsTest.java
@@ -19,8 +19,10 @@ package org.apache.camel.itest.shiro;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.camel.Exchange;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.jms.JmsComponent;
+import org.apache.camel.component.jms.JmsHeaderFilterStrategy;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.component.shiro.security.ShiroSecurityConstants;
 import org.apache.camel.component.shiro.security.ShiroSecurityPolicy;
@@ -61,9 +63,42 @@ public class ShiroOverJmsTest extends CamelTestSupport {
 
         amq.setCamelContext(context);
 
+        // After CAMEL-23592 the Shiro security headers live in the CamelShiroSecurity* namespace,
+        // which the default JmsHeaderFilterStrategy filters at the transport boundary. For a
+        // trusted Shiro-over-JMS route, the operator must opt those three headers back in.
+        amq.setHeaderFilterStrategy(new ShiroFriendlyJmsHeaderFilterStrategy());
+
         registry.bind("jms", amq);
     }
 
+    /**
+     * Allows the three Shiro security headers (token, username, password) to cross the JMS transport boundary while
+     * still filtering every other Camel-internal header.
+     */
+    private static final class ShiroFriendlyJmsHeaderFilterStrategy extends JmsHeaderFilterStrategy {
+        @Override
+        public boolean applyFilterToCamelHeaders(String headerName, Object headerValue, Exchange exchange) {
+            if (isShiroSecurityHeader(headerName)) {
+                return false;
+            }
+            return super.applyFilterToCamelHeaders(headerName, headerValue, exchange);
+        }
+
+        @Override
+        public boolean applyFilterToExternalHeaders(String headerName, Object headerValue, Exchange exchange) {
+            if (isShiroSecurityHeader(headerName)) {
+                return false;
+            }
+            return super.applyFilterToExternalHeaders(headerName, headerValue, exchange);
+        }
+
+        private static boolean isShiroSecurityHeader(String headerName) {
+            return ShiroSecurityConstants.SHIRO_SECURITY_TOKEN.equalsIgnoreCase(headerName)
+                    || ShiroSecurityConstants.SHIRO_SECURITY_USERNAME.equalsIgnoreCase(headerName)
+                    || ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD.equalsIgnoreCase(headerName);
+        }
+    }
+
     @Override
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {