WebSession supports changeSessionId
Issue: SPR-15571
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.web.server.session;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -31,6 +32,8 @@ import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||
import org.springframework.util.IdGenerator;
|
||||
import org.springframework.util.JdkIdGenerator;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebSession;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
@@ -44,10 +47,16 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultWebSessionManager}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class DefaultWebSessionManagerTests {
|
||||
|
||||
private static final Clock CLOCK = Clock.system(ZoneId.of("GMT"));
|
||||
|
||||
private static final IdGenerator idGenerator = new JdkIdGenerator();
|
||||
|
||||
|
||||
private DefaultWebSessionManager manager;
|
||||
|
||||
private TestWebSessionIdResolver idResolver;
|
||||
@@ -105,9 +114,10 @@ public class DefaultWebSessionManagerTests {
|
||||
|
||||
@Test
|
||||
public void existingSession() throws Exception {
|
||||
DefaultWebSession existing = new DefaultWebSession("1", Clock.systemDefaultZone(), s -> Mono.empty());
|
||||
DefaultWebSession existing = createDefaultWebSession();
|
||||
String id = existing.getId();
|
||||
this.manager.getSessionStore().storeSession(existing);
|
||||
this.idResolver.setIdsToResolve(Collections.singletonList("1"));
|
||||
this.idResolver.setIdsToResolve(Collections.singletonList(id));
|
||||
|
||||
WebSession actual = this.manager.getSession(this.exchange).block();
|
||||
assertNotNull(actual);
|
||||
@@ -116,10 +126,9 @@ public class DefaultWebSessionManagerTests {
|
||||
|
||||
@Test
|
||||
public void existingSessionIsExpired() throws Exception {
|
||||
Clock clock = Clock.systemDefaultZone();
|
||||
DefaultWebSession existing = new DefaultWebSession("1", clock, s -> Mono.empty());
|
||||
DefaultWebSession existing = createDefaultWebSession();
|
||||
existing.start();
|
||||
Instant lastAccessTime = Instant.now(clock).minus(Duration.ofMinutes(31));
|
||||
Instant lastAccessTime = Instant.now(CLOCK).minus(Duration.ofMinutes(31));
|
||||
existing = new DefaultWebSession(existing, lastAccessTime, s -> Mono.empty());
|
||||
this.manager.getSessionStore().storeSession(existing);
|
||||
this.idResolver.setIdsToResolve(Collections.singletonList("1"));
|
||||
@@ -129,16 +138,21 @@ public class DefaultWebSessionManagerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleSessions() throws Exception {
|
||||
DefaultWebSession existing = new DefaultWebSession("3", Clock.systemDefaultZone(), s -> Mono.empty());
|
||||
public void multipleSessionIds() throws Exception {
|
||||
DefaultWebSession existing = createDefaultWebSession();
|
||||
String id = existing.getId();
|
||||
this.manager.getSessionStore().storeSession(existing);
|
||||
this.idResolver.setIdsToResolve(Arrays.asList("1", "2", "3"));
|
||||
this.idResolver.setIdsToResolve(Arrays.asList("neither-this", "nor-that", id));
|
||||
|
||||
WebSession actual = this.manager.getSession(this.exchange).block();
|
||||
assertNotNull(actual);
|
||||
assertEquals(existing.getId(), actual.getId());
|
||||
}
|
||||
|
||||
private DefaultWebSession createDefaultWebSession() {
|
||||
return new DefaultWebSession(idGenerator, CLOCK, (s, session) -> Mono.empty(), s -> Mono.empty());
|
||||
}
|
||||
|
||||
|
||||
private static class TestWebSessionIdResolver implements WebSessionIdResolver {
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -34,19 +33,19 @@ import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
import org.springframework.web.server.WebSession;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* Integration tests for with a server-side session.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
@@ -64,12 +63,6 @@ public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTe
|
||||
this.restTemplate = new RestTemplate();
|
||||
}
|
||||
|
||||
private URI createUri(String pathAndQuery) throws URISyntaxException {
|
||||
boolean prefix = !StringUtils.hasText(pathAndQuery) || !pathAndQuery.startsWith("/");
|
||||
pathAndQuery = (prefix ? "/" + pathAndQuery : pathAndQuery);
|
||||
return new URI("http://localhost:" + port + pathAndQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHandler createHttpHandler() {
|
||||
this.sessionManager = new DefaultWebSessionManager();
|
||||
@@ -77,45 +70,46 @@ public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTe
|
||||
return WebHttpHandlerBuilder.webHandler(this.handler).sessionManager(this.sessionManager).build();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void createSession() throws Exception {
|
||||
RequestEntity<Void> request = RequestEntity.get(createUri("/")).build();
|
||||
RequestEntity<Void> request = RequestEntity.get(createUri()).build();
|
||||
ResponseEntity<Void> response = this.restTemplate.exchange(request, Void.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
String id = extractSessionId(response.getHeaders());
|
||||
assertNotNull(id);
|
||||
assertEquals(1, this.handler.getCount());
|
||||
assertEquals(1, this.handler.getSessionRequestCount());
|
||||
|
||||
request = RequestEntity.get(createUri("/")).header("Cookie", "SESSION=" + id).build();
|
||||
request = RequestEntity.get(createUri()).header("Cookie", "SESSION=" + id).build();
|
||||
response = this.restTemplate.exchange(request, Void.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNull(response.getHeaders().get("Set-Cookie"));
|
||||
assertEquals(2, this.handler.getCount());
|
||||
assertEquals(2, this.handler.getSessionRequestCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expiredSession() throws Exception {
|
||||
public void expiredSessionIsRecreated() throws Exception {
|
||||
|
||||
// First request: no session yet, new session created
|
||||
RequestEntity<Void> request = RequestEntity.get(createUri("/")).build();
|
||||
RequestEntity<Void> request = RequestEntity.get(createUri()).build();
|
||||
ResponseEntity<Void> response = this.restTemplate.exchange(request, Void.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
String id = extractSessionId(response.getHeaders());
|
||||
assertNotNull(id);
|
||||
assertEquals(1, this.handler.getCount());
|
||||
assertEquals(1, this.handler.getSessionRequestCount());
|
||||
|
||||
// Second request: same session
|
||||
request = RequestEntity.get(createUri("/")).header("Cookie", "SESSION=" + id).build();
|
||||
request = RequestEntity.get(createUri()).header("Cookie", "SESSION=" + id).build();
|
||||
response = this.restTemplate.exchange(request, Void.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNull(response.getHeaders().get("Set-Cookie"));
|
||||
assertEquals(2, this.handler.getCount());
|
||||
assertEquals(2, this.handler.getSessionRequestCount());
|
||||
|
||||
// Update lastAccessTime of the created session to -31 min
|
||||
// Now set the clock of the session back by 31 minutes
|
||||
WebSessionStore store = this.sessionManager.getSessionStore();
|
||||
DefaultWebSession session = (DefaultWebSession) store.retrieveSession(id).block();
|
||||
assertNotNull(session);
|
||||
@@ -124,13 +118,37 @@ public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTe
|
||||
store.storeSession(session);
|
||||
|
||||
// Third request: expired session, new session created
|
||||
request = RequestEntity.get(createUri("/")).header("Cookie", "SESSION=" + id).build();
|
||||
request = RequestEntity.get(createUri()).header("Cookie", "SESSION=" + id).build();
|
||||
response = this.restTemplate.exchange(request, Void.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
id = extractSessionId(response.getHeaders());
|
||||
assertNotNull("Expected new session id", id);
|
||||
assertEquals("Expected new session attribute", 1, this.handler.getCount());
|
||||
assertEquals(1, this.handler.getSessionRequestCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionId() throws Exception {
|
||||
|
||||
// First request: no session yet, new session created
|
||||
RequestEntity<Void> request = RequestEntity.get(createUri()).build();
|
||||
ResponseEntity<Void> response = this.restTemplate.exchange(request, Void.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
String oldId = extractSessionId(response.getHeaders());
|
||||
assertNotNull(oldId);
|
||||
assertEquals(1, this.handler.getSessionRequestCount());
|
||||
|
||||
// Second request: session id changes
|
||||
URI uri = new URI("http://localhost:" + this.port + "/?changeId");
|
||||
request = RequestEntity.get(uri).header("Cookie", "SESSION=" + oldId).build();
|
||||
response = this.restTemplate.exchange(request, Void.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
String newId = extractSessionId(response.getHeaders());
|
||||
assertNotNull("Expected new session id", newId);
|
||||
assertNotEquals(oldId, newId);
|
||||
assertEquals(2, this.handler.getSessionRequestCount());
|
||||
}
|
||||
|
||||
private String extractSessionId(HttpHeaders headers) {
|
||||
@@ -146,25 +164,33 @@ public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTe
|
||||
return null;
|
||||
}
|
||||
|
||||
private URI createUri() throws URISyntaxException {
|
||||
return new URI("http://localhost:" + this.port + "/");
|
||||
}
|
||||
|
||||
|
||||
private static class TestWebHandler implements WebHandler {
|
||||
|
||||
private AtomicInteger currentValue = new AtomicInteger();
|
||||
|
||||
|
||||
public int getCount() {
|
||||
public int getSessionRequestCount() {
|
||||
return this.currentValue.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange) {
|
||||
return exchange.getSession().map(session -> {
|
||||
Map<String, Object> map = session.getAttributes();
|
||||
int value = (map.get("counter") != null ? (int) map.get("counter") : 0);
|
||||
value++;
|
||||
map.put("counter", value);
|
||||
this.currentValue.set(value);
|
||||
return session;
|
||||
}).then();
|
||||
if (exchange.getRequest().getQueryParams().containsKey("changeId")) {
|
||||
return exchange.getSession().flatMap(session ->
|
||||
session.changeSessionId().doOnSuccess(aVoid -> updateSessionAttribute(session)));
|
||||
}
|
||||
return exchange.getSession().doOnSuccess(this::updateSessionAttribute).then();
|
||||
}
|
||||
|
||||
private void updateSessionAttribute(WebSession session) {
|
||||
int value = session.getAttributeOrDefault("counter", 0);
|
||||
session.getAttributes().put("counter", ++value);
|
||||
this.currentValue.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user