From 56ba7d2a1a01ef11d017b0bae3e231055470952f Mon Sep 17 00:00:00 2001 From: lprimak Date: Thu, 20 Aug 2026 05:34:26 -0500 Subject: [PATCH] bugfix: fix security context handling in JAX-RS support for methods isSecure() and getAuthenticationScheme() - was throwing stack overflow errors --- .../shiro/testing/jaxrs/WhoamiResource.java | 17 +++++++++++++++++ .../shiro/testing/jaxrs/NoIniJaxRsIT.java | 13 +++++++++++++ .../shiro/web/jaxrs/ShiroSecurityContext.java | 12 ++++-------- .../SubjectPrincipalRequestFilterTest.groovy | 2 +- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiResource.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiResource.java index eff0860b93..9a48d492d1 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiResource.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiResource.java @@ -22,9 +22,11 @@ import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.SecurityContext; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.lang.ShiroException; @@ -39,6 +41,10 @@ public class WhoamiResource { RolesAllowedBean rolesAllowedBean; @Inject TestApplication testApplication; + @Context + SecurityContext securityContext; + + public record SecurityDetails(String principal, boolean isSecure, String authScheme) { } @GET @Path("whoami") @@ -75,6 +81,17 @@ public Response permit(@QueryParam("user") String user, @QueryParam("password") return check(rolesAllowedBean::permit, rolesAllowedBean::permit, user, password); } + @GET + @Path("securityContext") + @Produces(APPLICATION_JSON) + public Response securityContext(@QueryParam("user") String user, @QueryParam("password") String password) { + return check(() -> Response.ok(new SecurityDetails( + securityContext.getUserPrincipal().getName(), + securityContext.isSecure(), + securityContext.getAuthenticationScheme() + )).build(), () -> Response.status(Status.UNAUTHORIZED).build(), user, password); + } + private T check(Supplier happy, Supplier sad, String user, String password) { try { return new Subject.Builder() diff --git a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jaxrs/NoIniJaxRsIT.java b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jaxrs/NoIniJaxRsIT.java index 7d93b110bb..5dc1918b93 100644 --- a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jaxrs/NoIniJaxRsIT.java +++ b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jaxrs/NoIniJaxRsIT.java @@ -27,8 +27,10 @@ import org.apache.shiro.testing.cdi.ComponentInjectionIT; import static org.apache.shiro.testing.cdi.ComponentInjectionIT.TESTABLE_MODE; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; +import org.apache.shiro.testing.jaxrs.WhoamiResource.SecurityDetails; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.OperateOnDeployment; import org.jboss.arquillian.junit5.ArquillianExtension; @@ -130,6 +132,17 @@ void deny() { .request().get().getStatus()); } + @Test + @OperateOnDeployment(TESTABLE_MODE) + void securityContext() { + var securityDetails = createWebTarget("whoami/securityContext", Credentials.builder() + .username("powerful").password("awesome").send(true).build()) + .request().get().readEntity(SecurityDetails.class); + assertThat(securityDetails.principal()).isEqualTo("powerful"); + assertThat(securityDetails.isSecure()).isFalse(); + assertThat(securityDetails.authScheme()).isNull(); + } + @Deployment(name = TESTABLE_MODE) public static WebArchive createDeployment() { return ComponentInjectionIT.createDeployment("no-ini-jaxrs.war"); diff --git a/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/ShiroSecurityContext.java b/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/ShiroSecurityContext.java index d716107dc6..cbc7ca833b 100644 --- a/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/ShiroSecurityContext.java +++ b/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/ShiroSecurityContext.java @@ -33,12 +33,9 @@ * @since 1.4 */ public class ShiroSecurityContext implements SecurityContext { - - private final ContainerRequestContext containerRequestContext; private final SecurityContext originalSecurityContext; public ShiroSecurityContext(ContainerRequestContext containerRequestContext) { - this.containerRequestContext = containerRequestContext; this.originalSecurityContext = containerRequestContext.getSecurityContext(); } @@ -69,12 +66,12 @@ public boolean isUserInRole(String role) { @Override public boolean isSecure() { - return containerRequestContext.getSecurityContext().isSecure(); + return originalSecurityContext.isSecure(); } @Override public String getAuthenticationScheme() { - return containerRequestContext.getSecurityContext().getAuthenticationScheme(); + return originalSecurityContext.getAuthenticationScheme(); } private Subject getSubject() { @@ -85,9 +82,8 @@ private Subject getSubject() { /** * Java Principal wrapper around any Shiro Principal object.s */ - private class ObjectPrincipal implements Principal { - - private Object object; + private static class ObjectPrincipal implements Principal { + private final Object object; ObjectPrincipal(Object object) { this.object = object; diff --git a/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/SubjectPrincipalRequestFilterTest.groovy b/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/SubjectPrincipalRequestFilterTest.groovy index a18ae52412..4bb386c3cb 100644 --- a/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/SubjectPrincipalRequestFilterTest.groovy +++ b/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/SubjectPrincipalRequestFilterTest.groovy @@ -48,7 +48,7 @@ class SubjectPrincipalRequestFilterTest { filter.filter(requestContext) verify requestContext, originalSecurityContext - assertSame requestContext, contextCapture.value.containerRequestContext + assertNotSame requestContext, contextCapture.value.originalSecurityContext } }