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