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

lprimak pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/shiro.git


The following commit(s) were added to refs/heads/main by this push:
     new 9371115fe [#2704] [#2710] Fixed Session fixation-related regressions 
(#2711)
9371115fe is described below

commit 9371115fe8626ef1b67cf38e23f5f6eed324f2cc
Author: Lenny Primak <[email protected]>
AuthorDate: Sat May 23 21:54:52 2026 -0400

    [#2704] [#2710] Fixed Session fixation-related regressions (#2711)
    
    * make sure subject's session gets fully cleared
    fix for #2704
    
    * bugfix: session attributes survive id rotation in native session mode
    fixes #2710
    
    * backport: JDK 11 compatibility
---
 .../apache/shiro/mgt/DefaultSecurityManager.java   | 18 +++++++++++-
 .../shiro/subject/support/DelegatingSubject.java   |  2 +-
 .../shiro/subject/DelegatingSubjectTest.java       | 33 ++++++++++++++++++++++
 3 files changed, 51 insertions(+), 2 deletions(-)

diff --git 
a/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java 
b/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java
index 6ca07fc6f..9d0c77674 100644
--- a/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java
+++ b/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java
@@ -35,12 +35,15 @@ import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.subject.Subject;
 import org.apache.shiro.subject.SubjectContext;
 import org.apache.shiro.subject.support.DefaultSubjectContext;
+import org.apache.shiro.subject.support.DelegatingSubject;
 import org.apache.shiro.util.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.Serializable;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * The Shiro framework's default concrete implementation of the {@link 
SecurityManager} interface,
@@ -302,7 +305,17 @@ public class DefaultSecurityManager extends 
SessionsSecurityManager {
      * @param subject Subject
      */
     protected void beforeSuccessfulLogin(Subject subject) {
-        stopSession(subject);
+        Session session = subject.getSession(false);
+        if (session != null) {
+            Map<Object, Object> attributes = new HashMap<>();
+            session.getAttributeKeys().forEach(key -> attributes.put(key, 
session.getAttribute(key)));
+            stopSession(subject);
+            var newSession = subject.getSession();
+            var keys = newSession.getAttributeKeys();
+            attributes.entrySet().stream()
+                    .filter(entry -> !keys.contains(entry.getKey()))
+                    .forEach(entry -> newSession.setAttribute(entry.getKey(), 
entry.getValue()));
+        }
     }
 
     protected void onSuccessfulLogin(AuthenticationToken token, 
AuthenticationInfo info, Subject subject) {
@@ -603,6 +616,9 @@ public class DefaultSecurityManager extends 
SessionsSecurityManager {
         Session s = subject.getSession(false);
         if (s != null) {
             s.stop();
+            if (subject instanceof DelegatingSubject) {
+                ((DelegatingSubject) subject).sessionStopped();
+            }
         }
     }
 
diff --git 
a/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java 
b/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
index b96c97d1b..f43917707 100644
--- a/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
+++ b/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
@@ -379,7 +379,7 @@ public class DelegatingSubject implements Subject {
         }
     }
 
-    private void sessionStopped() {
+    public void sessionStopped() {
         this.session = null;
     }
 
diff --git 
a/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java 
b/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java
index c9dfcc6ce..f38fabacb 100644
--- a/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java
+++ b/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java
@@ -36,6 +36,7 @@ import java.io.Serializable;
 import java.util.concurrent.Callable;
 
 import static org.apache.shiro.env.BasicIniEnvironment.INI_REALM_NAME;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.easymock.EasyMock.createNiceMock;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -156,6 +157,8 @@ public class DelegatingSubjectTest {
         //login as user1
         Subject subject = new Subject.Builder(sm).buildSubject();
         subject.login(new UsernamePasswordToken("user1", "user1"));
+        // duplicate login, test for 
https://github.com/apache/shiro/issues/2704
+        subject.login(new UsernamePasswordToken("user1", "user1"));
 
         assertFalse(subject.isRunAs());
         assertEquals("user1", subject.getPrincipal());
@@ -223,6 +226,36 @@ public class DelegatingSubjectTest {
         LifecycleUtils.destroy(sm);
     }
 
+    @Test
+    void sessionAttributesSurviveLoginSessionRotation() {
+        Ini ini = new Ini();
+        Ini.Section users = ini.addSection("users");
+        users.put("user1", "user1,role1");
+        users.put("user2", "user2,role2");
+        users.put("user3", "user3,role3");
+        SecurityManager sm = new BasicIniEnvironment(ini).getSecurityManager();
+        Subject subject = new Subject.Builder(sm).buildSubject();
+
+        subject.login(new UsernamePasswordToken("user1", "user1"));
+        subject.logout();
+
+        Session preLoginSession = subject.getSession(true);
+        preLoginSession.setAttribute("tenantId", "ACME");
+        Serializable preLoginSessionId = preLoginSession.getId();
+
+        subject.login(new UsernamePasswordToken("user1", "user1"));
+        assertThat(subject.isAuthenticated()).isTrue();
+
+        Session postLoginSession = subject.getSession(false);
+        assertThat(postLoginSession).isNotNull();
+
+        assertThat(preLoginSessionId).as("session ID should change on login 
(session fixation protection)")
+                .isNotEqualTo(postLoginSession.getId());
+        assertThat(postLoginSession.getAttribute("tenantId"))
+                .as("session attributes set before login must survive session 
rotation")
+                .isEqualTo("ACME");
+    }
+
     @Test
     void testToString() {
         // given

Reply via email to