Skip to content

Commit 9371115

Browse files
authored
[#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
1 parent a7265a1 commit 9371115

3 files changed

Lines changed: 51 additions & 2 deletions

File tree

core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,15 @@
3535
import org.apache.shiro.subject.Subject;
3636
import org.apache.shiro.subject.SubjectContext;
3737
import org.apache.shiro.subject.support.DefaultSubjectContext;
38+
import org.apache.shiro.subject.support.DelegatingSubject;
3839
import org.apache.shiro.util.CollectionUtils;
3940
import org.slf4j.Logger;
4041
import org.slf4j.LoggerFactory;
4142

4243
import java.io.Serializable;
4344
import java.util.Collection;
45+
import java.util.HashMap;
46+
import java.util.Map;
4447

4548
/**
4649
* The Shiro framework's default concrete implementation of the {@link SecurityManager} interface,
@@ -302,7 +305,17 @@ public Subject login(Subject subject, AuthenticationToken token) throws Authenti
302305
* @param subject Subject
303306
*/
304307
protected void beforeSuccessfulLogin(Subject subject) {
305-
stopSession(subject);
308+
Session session = subject.getSession(false);
309+
if (session != null) {
310+
Map<Object, Object> attributes = new HashMap<>();
311+
session.getAttributeKeys().forEach(key -> attributes.put(key, session.getAttribute(key)));
312+
stopSession(subject);
313+
var newSession = subject.getSession();
314+
var keys = newSession.getAttributeKeys();
315+
attributes.entrySet().stream()
316+
.filter(entry -> !keys.contains(entry.getKey()))
317+
.forEach(entry -> newSession.setAttribute(entry.getKey(), entry.getValue()));
318+
}
306319
}
307320

308321
protected void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info, Subject subject) {
@@ -603,6 +616,9 @@ protected void stopSession(Subject subject) {
603616
Session s = subject.getSession(false);
604617
if (s != null) {
605618
s.stop();
619+
if (subject instanceof DelegatingSubject) {
620+
((DelegatingSubject) subject).sessionStopped();
621+
}
606622
}
607623
}
608624

core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ public void logout() {
379379
}
380380
}
381381

382-
private void sessionStopped() {
382+
public void sessionStopped() {
383383
this.session = null;
384384
}
385385

core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.concurrent.Callable;
3737

3838
import static org.apache.shiro.env.BasicIniEnvironment.INI_REALM_NAME;
39+
import static org.assertj.core.api.Assertions.assertThat;
3940
import static org.easymock.EasyMock.createNiceMock;
4041
import static org.junit.jupiter.api.Assertions.assertEquals;
4142
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -156,6 +157,8 @@ void testRunAs() {
156157
//login as user1
157158
Subject subject = new Subject.Builder(sm).buildSubject();
158159
subject.login(new UsernamePasswordToken("user1", "user1"));
160+
// duplicate login, test for https://github.com/apache/shiro/issues/2704
161+
subject.login(new UsernamePasswordToken("user1", "user1"));
159162

160163
assertFalse(subject.isRunAs());
161164
assertEquals("user1", subject.getPrincipal());
@@ -223,6 +226,36 @@ void testRunAs() {
223226
LifecycleUtils.destroy(sm);
224227
}
225228

229+
@Test
230+
void sessionAttributesSurviveLoginSessionRotation() {
231+
Ini ini = new Ini();
232+
Ini.Section users = ini.addSection("users");
233+
users.put("user1", "user1,role1");
234+
users.put("user2", "user2,role2");
235+
users.put("user3", "user3,role3");
236+
SecurityManager sm = new BasicIniEnvironment(ini).getSecurityManager();
237+
Subject subject = new Subject.Builder(sm).buildSubject();
238+
239+
subject.login(new UsernamePasswordToken("user1", "user1"));
240+
subject.logout();
241+
242+
Session preLoginSession = subject.getSession(true);
243+
preLoginSession.setAttribute("tenantId", "ACME");
244+
Serializable preLoginSessionId = preLoginSession.getId();
245+
246+
subject.login(new UsernamePasswordToken("user1", "user1"));
247+
assertThat(subject.isAuthenticated()).isTrue();
248+
249+
Session postLoginSession = subject.getSession(false);
250+
assertThat(postLoginSession).isNotNull();
251+
252+
assertThat(preLoginSessionId).as("session ID should change on login (session fixation protection)")
253+
.isNotEqualTo(postLoginSession.getId());
254+
assertThat(postLoginSession.getAttribute("tenantId"))
255+
.as("session attributes set before login must survive session rotation")
256+
.isEqualTo("ACME");
257+
}
258+
226259
@Test
227260
void testToString() {
228261
// given

0 commit comments

Comments
 (0)