Align HttpSessionStrategy with WebSessionIdResolver
This commit simplifies `HttpSessionStrategy` API by aligning it with Spring Framework's `WebSessionIdResolver`. As a part of this, support for managing multiple users' sessions has been removed. Closes gh-275 Closes gh-362
This commit is contained in:
@@ -1,162 +0,0 @@
|
||||
= Spring Session - Multiple Sessions
|
||||
Rob Winch
|
||||
:toc:
|
||||
|
||||
This guide describes how to use Spring Session to manage multiple simultaneous browser sessions (i.e Google Accounts).
|
||||
|
||||
== Integrating with Spring Session
|
||||
|
||||
The steps to integrate with Spring Session are exactly the same as those outline in the link:httpsession.html[HttpSession Guide], so we will skip to running the sample application.
|
||||
|
||||
[[users-sample]]
|
||||
== users Sample Application
|
||||
|
||||
The users application demonstrates how to allow an application to manage multiple simultaneous browser sessions (i.e. Google Accounts).
|
||||
|
||||
=== Running the users Sample Application
|
||||
|
||||
You can run the sample by obtaining the {download-url}[source code] and invoking the following command:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
For the sample to work, you must http://redis.io/download[install Redis 2.8+] on localhost and run it with the default port (6379).
|
||||
Alternatively, you can update the `RedisConnectionFactory` to point to a Redis server.
|
||||
Another option is to use https://www.docker.com/[Docker] to run Redis on localhost. See https://hub.docker.com/_/redis/[Docker Redis repository] for detailed instructions.
|
||||
====
|
||||
|
||||
----
|
||||
$ ./gradlew :samples:users:tomcatRun
|
||||
----
|
||||
|
||||
You should now be able to access the application at http://localhost:8080/
|
||||
|
||||
=== Exploring the users Sample Application
|
||||
|
||||
Try using the application. Authenticate with the following information:
|
||||
|
||||
* **Username** _rob_
|
||||
* **Password** _rob_
|
||||
|
||||
Now click the **Login** button. You should now be authenticated as the user **rob**.
|
||||
|
||||
We can click on links and our user information is preserved.
|
||||
|
||||
* Click on the **Link** link in the navigation bar at the top
|
||||
* Observe we are still authenticated as **rob**
|
||||
|
||||
Let's add an another account.
|
||||
|
||||
* Return to the *Home* page
|
||||
* Click on the arrow next to *rob* in the upper right hand corner
|
||||
* Click **Add Account**
|
||||
|
||||
The log in form is displayed again. Authenticate with the following information:
|
||||
|
||||
* **Username** _luke_
|
||||
* **Password** _luke_
|
||||
|
||||
Now click the **Login** button. You should now be authenticated as the user **luke**.
|
||||
|
||||
We can click on links and our user information is preserved.
|
||||
|
||||
* Click on the **Link** link in the navigation bar at the top
|
||||
* Observe we are still authenticated as **luke**
|
||||
|
||||
Where did our original user go? Let's switch to our original account.
|
||||
|
||||
* Click on the arrow next to *luke* in the upper right hand corner.
|
||||
* Click on **Switch Account** -> *rob*
|
||||
|
||||
We are now using the session associated with *rob*.
|
||||
|
||||
== How does it work?
|
||||
|
||||
// tag::how-does-it-work[]
|
||||
|
||||
Let's take a look at how Spring Session keeps track of multiple sessions.
|
||||
|
||||
=== Managing a Single Session
|
||||
|
||||
Spring Session keeps track of the `HttpSession` by adding a value to a cookie named SESSION.
|
||||
For example, the SESSION cookie might have a value of:
|
||||
|
||||
7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
|
||||
|
||||
=== Adding a Session
|
||||
|
||||
We can add another session by requesting a URL that contains a special parameter in it.
|
||||
By default the parameter name is *_s*. For example, the following URL would create a new session:
|
||||
|
||||
http://localhost:8080/?_s=1
|
||||
|
||||
NOTE: The parameter value does not indicate the actual session id.
|
||||
This is important because we never want to allow the session id to be determined by a client to avoid https://www.owasp.org/index.php/Session_fixation[session fixation attacks].
|
||||
Additionally, we do not want the session id to be leaked since it is sent as a query parameter.
|
||||
Remember sensitive information should only be transmitted as a header or in the body of the request.
|
||||
|
||||
Rather than creating the URL ourselves, we can utilize the `HttpSessionManager` to do this for us.
|
||||
We can obtain the `HttpSessionManager` from the `HttpServletRequest` using the following:
|
||||
|
||||
.src/main/java/sample/UserAccountsFilter.java
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}javaconfig/users/src/main/java/sample/UserAccountsFilter.java[tags=HttpSessionManager]
|
||||
----
|
||||
|
||||
We can now use it to create a URL to add another session.
|
||||
|
||||
.src/main/java/sample/UserAccountsFilter.java
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{samples-dir}javaconfig/users/src/main/java/sample/UserAccountsFilter.java[tags=addAccountUrl]
|
||||
----
|
||||
|
||||
<1> We have an existing variable named `unauthenticatedAlias`.
|
||||
The value is an alias that points to an existing unauthenticated session.
|
||||
If no such session exists, the value is null.
|
||||
This ensures if we have an existing unauthenticated session that we use it instead of creating a new session.
|
||||
<2> If all of our sessions are already associated to a user, we create a new session alias.
|
||||
<3> If there is an existing session that is not associated to a user, we use its session alias.
|
||||
<4> Finally, we create the add account URL.
|
||||
The URL contains a session alias that either points to an existing unauthenticated session or is an alias that is unused thus signaling to create a new session associated to that alias.
|
||||
|
||||
Now our SESSION cookie looks something like this:
|
||||
|
||||
0 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e 1 1d526d4a-c462-45a4-93d9-84a39b6d44ad
|
||||
|
||||
Such that:
|
||||
|
||||
* There is a session with the id *7e8383a4-082c-4ffe-a4bc-c40fd3363c5e*
|
||||
** The alias for this session is *0*.
|
||||
For example, if the URL is http://localhost:8080/?_s=0 this alias would be used.
|
||||
** This is the default session.
|
||||
This means that if no session alias is specified, then this session is used.
|
||||
For example, if the URL is http://localhost:8080/ this session would be used.
|
||||
* There is a session with the id *1d526d4a-c462-45a4-93d9-84a39b6d44ad*
|
||||
** The alias for this session is *1*.
|
||||
If the session alias is *1*, then this session is used.
|
||||
For example, if the URL is http://localhost:8080/?_s=1 this alias would be used.
|
||||
|
||||
=== Automatic Session Alias Inclusion with encodeURL
|
||||
|
||||
The nice thing about specifying the session alias in the URL is that we can have multiple tabs open with different active sessions.
|
||||
The bad thing is that we need to include the session alias in every URL of our application.
|
||||
Fortunately, Spring Session will automatically include the session alias in any URL that passes through http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#encodeURL(java.lang.String)[HttpServletResponse#encodeURL(java.lang.String)]
|
||||
|
||||
This means that if you are using standard tag libraries the session alias is automatically included in the URL.
|
||||
For example, if we are currently using the session with the alias of *1*, then the following:
|
||||
|
||||
.src/main/webapp/index.jsp
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
include::{samples-dir}javaconfig/users/src/main/webapp/index.jsp[tags=link]
|
||||
----
|
||||
|
||||
will output a link of:
|
||||
|
||||
[source,html]
|
||||
----
|
||||
<a id="navLink" href="/link.jsp?_s=1">Link</a>
|
||||
----
|
||||
|
||||
// end::how-does-it-work[]
|
||||
@@ -18,7 +18,6 @@ Spring Session provides an API and implementations for managing a user's session
|
||||
* <<httpsession,HttpSession>> - allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way.
|
||||
Additional features include:
|
||||
** **Clustered Sessions** - Spring Session makes it trivial to support <<httpsession-redis,clustered sessions>> without being tied to an application container specific solution.
|
||||
** **Multiple Browser Sessions** - Spring Session supports <<httpsession-multi,managing multiple users' sessions>> in a single browser instance (i.e. multiple authenticated accounts similar to Google).
|
||||
** **RESTful APIs** - Spring Session allows providing session ids in headers to work with <<httpsession-rest,RESTful APIs>>
|
||||
|
||||
* <<websocket,WebSocket>> - provides the ability to keep the `HttpSession` alive when receiving WebSocket messages
|
||||
@@ -98,10 +97,6 @@ If you are looking to get started with Spring Session, the best place to start i
|
||||
| Demonstrates how to use Spring Session in a REST application to support authenticating with a header.
|
||||
| link:guides/java-rest.html[REST Guide]
|
||||
|
||||
| {gh-samples-url}javaconfig/users[Multiple Users]
|
||||
| Demonstrates how to use Spring Session to manage multiple simultaneous browser sessions (i.e Google Accounts).
|
||||
| link:guides/java-users.html[Multiple Users Guide]
|
||||
|
||||
|===
|
||||
|
||||
.Sample Applications using Spring XML based configuration
|
||||
@@ -144,7 +139,6 @@ This means that developers can switch the `HttpSession` implementation out with
|
||||
We have already mentioned that Spring Session provides transparent integration with `HttpSession`, but what benefits do we get out of this?
|
||||
|
||||
* **Clustered Sessions** - Spring Session makes it trivial to support <<httpsession-redis,clustered sessions>> without being tied to an application container specific solution.
|
||||
* **Multiple Browser Sessions** - Spring Session supports <<httpsession-multi,managing multiple users' sessions>> in a single browser instance (i.e. multiple authenticated accounts similar to Google).
|
||||
* **RESTful APIs** - Spring Session allows providing session ids in headers to work with <<httpsession-rest,RESTful APIs>>
|
||||
|
||||
[[httpsession-redis]]
|
||||
@@ -284,17 +278,6 @@ public class SessionRepositoryFilter implements Filter {
|
||||
By passing in a custom `HttpServletRequest` implementation into the `FilterChain` we ensure that anything invoked after our `Filter` uses the custom `HttpSession` implementation.
|
||||
This highlights why it is important that Spring Session's `SessionRepositoryFilter` must be placed before anything that interacts with the `HttpSession`.
|
||||
|
||||
[[httpsession-multi]]
|
||||
=== Multiple HttpSessions in Single Browser
|
||||
|
||||
Spring Session has the ability to support multiple sessions in a single browser instance.
|
||||
This provides the ability to support authenticating with multiple users in the same browser instance (i.e. Google Accounts).
|
||||
|
||||
NOTE: The <<samples,Manage Multiple Users Guide>> provides a complete working example of managing multiple users in the same browser instance.
|
||||
You can follow the basic steps for integration below, but you are encouraged to follow along with the detailed Manage Multiple Users Guide when integrating with your own application.
|
||||
|
||||
include::guides/java-users.adoc[tags=how-does-it-work,leveloffset=+1]
|
||||
|
||||
[[httpsession-rest]]
|
||||
=== HttpSession & RESTful APIs
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
dependencyManagement {
|
||||
dependencies {
|
||||
dependency 'org.webjars:bootstrap:3.3.6'
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
apply plugin: 'io.spring.convention.spring-sample-war'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-redis')
|
||||
compile "org.springframework:spring-web"
|
||||
compile "io.lettuce:lettuce-core"
|
||||
compile "org.webjars:bootstrap"
|
||||
compile "org.webjars:webjars-taglib"
|
||||
compile jstlDependencies
|
||||
compile slf4jDependencies
|
||||
compile "org.testcontainers:testcontainers"
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api"
|
||||
|
||||
testCompile "junit:junit"
|
||||
testCompile "org.springframework:spring-test"
|
||||
testCompile "org.assertj:assertj-core"
|
||||
|
||||
integrationTestCompile seleniumDependencies
|
||||
}
|
||||
|
||||
gretty {
|
||||
jvmArgs = ['-Dspring.profiles.active=embedded-redis']
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
|
||||
import sample.pages.HomePage;
|
||||
import sample.pages.LinkPage;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Pool Dolorier
|
||||
*/
|
||||
public class UserTests {
|
||||
|
||||
private static final String ROB = "rob";
|
||||
private static final String USERNAME = "username";
|
||||
private static final String LUKE = "luke";
|
||||
private static final String NAV_LINK = "navLink";
|
||||
private static final String HREF = "href";
|
||||
private static final String ADD_ACCOUNT = "addAccount";
|
||||
private static final String UN = "un";
|
||||
private static final String LOGOUT = "logout";
|
||||
private static final String ERROR = "error";
|
||||
|
||||
private WebDriver driver;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.driver = new HtmlUnitDriver();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
this.driver.quit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstVisitNotAuthenticated() {
|
||||
HomePage homePage = HomePage.go(this.driver);
|
||||
homePage.assertAt();
|
||||
homePage.assertUserNameEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidLogin() {
|
||||
HomePage homePage = HomePage.go(this.driver);
|
||||
String user = ROB;
|
||||
homePage.login(user, user + "invalid");
|
||||
WebElement errorMessage = homePage.getElementById(ERROR);
|
||||
homePage.assertAt();
|
||||
homePage.assertErrorInvalidAuthentication(errorMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyUsername() {
|
||||
HomePage homePage = HomePage.go(this.driver);
|
||||
homePage.login("", "");
|
||||
WebElement errorMessage = homePage.getElementById(ERROR);
|
||||
homePage.assertAt();
|
||||
homePage.assertErrorInvalidAuthentication(errorMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginSingleUser() {
|
||||
loginRob();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addAccount() {
|
||||
backHomeForAddLukeAccount();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logInSecondUser() {
|
||||
logInLukeAccount();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void followingLinksKeepsNewSession() {
|
||||
followingLukeLinkSession();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void switchAccountRob() {
|
||||
switchAccountRobHomePage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void followingLinksKeepsOriginalSession() {
|
||||
followingRobLinkSession();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void switchAccountLuke() {
|
||||
switchAccountLukeHomePage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logoutLuke() {
|
||||
logoutLukeAccount();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void switchBackRob() {
|
||||
switchBackRobHomePage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logoutRob() {
|
||||
logoutRobAccount();
|
||||
}
|
||||
|
||||
private HomePage loginRob() {
|
||||
HomePage home = HomePage.go(this.driver);
|
||||
|
||||
String user = ROB;
|
||||
home.login(user, user);
|
||||
WebElement username = home.getElementById(UN);
|
||||
assertThat(username.getText()).isEqualTo(user);
|
||||
return home;
|
||||
}
|
||||
|
||||
private HomePage backHomeForAddLukeAccount() {
|
||||
HomePage robHome = loginRob();
|
||||
|
||||
String addAccountLink = robHome.getContentAttributeByElementId(ADD_ACCOUNT, HREF);
|
||||
HomePage backHome = robHome.home(this.driver, addAccountLink);
|
||||
WebElement username = backHome.getElementById(USERNAME);
|
||||
assertThat(username.getText()).isEmpty();
|
||||
return backHome;
|
||||
}
|
||||
|
||||
private HomePage logInLukeAccount() {
|
||||
HomePage home = backHomeForAddLukeAccount();
|
||||
|
||||
String secondUser = LUKE;
|
||||
home.login(secondUser, secondUser);
|
||||
WebElement secondUserName = home.getElementById(UN);
|
||||
assertThat(secondUserName.getText()).isEqualTo(secondUser);
|
||||
return home;
|
||||
}
|
||||
|
||||
private LinkPage followingLukeLinkSession() {
|
||||
HomePage lukeHome = logInLukeAccount();
|
||||
|
||||
String navLink = lukeHome.getContentAttributeByElementId(NAV_LINK, HREF);
|
||||
LinkPage lukeLinkPage = lukeHome.linkPage(this.driver, navLink);
|
||||
lukeLinkPage.assertAt();
|
||||
WebElement username = lukeLinkPage.getElementById(UN);
|
||||
assertThat(username.getText()).isEqualTo(LUKE);
|
||||
return lukeLinkPage;
|
||||
}
|
||||
|
||||
private HomePage switchAccountRobHomePage() {
|
||||
LinkPage lukeLinkPage = followingLukeLinkSession();
|
||||
|
||||
String robSwitch = lukeLinkPage.getSwitchElementId(ROB);
|
||||
String switchLink = lukeLinkPage.getContentAttributeByElementId(robSwitch, HREF);
|
||||
HomePage robHome = lukeLinkPage.home(this.driver, switchLink);
|
||||
WebElement username = robHome.getElementById(UN);
|
||||
assertThat(username.getText()).isEqualTo(ROB);
|
||||
return robHome;
|
||||
}
|
||||
|
||||
private LinkPage followingRobLinkSession() {
|
||||
HomePage robHome = switchAccountRobHomePage();
|
||||
|
||||
String navLink = robHome.getContentAttributeByElementId(NAV_LINK, HREF);
|
||||
LinkPage robLinkPage = robHome.linkPage(this.driver, navLink);
|
||||
robLinkPage.assertAt();
|
||||
WebElement username = robLinkPage.getElementById(UN);
|
||||
assertThat(username.getText()).isEqualTo(ROB);
|
||||
return robLinkPage;
|
||||
}
|
||||
|
||||
private HomePage switchAccountLukeHomePage() {
|
||||
LinkPage robLinkPage = followingRobLinkSession();
|
||||
|
||||
String lukeSwitch = robLinkPage.getSwitchElementId(LUKE);
|
||||
String lukeSwitchLink = robLinkPage.getContentAttributeByElementId(lukeSwitch, HREF);
|
||||
HomePage lukeHome = robLinkPage.home(this.driver, lukeSwitchLink);
|
||||
WebElement username = lukeHome.getElementById(UN);
|
||||
assertThat(username.getText()).isEqualTo(LUKE);
|
||||
return lukeHome;
|
||||
}
|
||||
|
||||
private HomePage logoutLukeAccount() {
|
||||
HomePage lukeHome = switchAccountLukeHomePage();
|
||||
|
||||
String logoutLink = lukeHome.getContentAttributeByElementId(LOGOUT, HREF);
|
||||
HomePage home = lukeHome.home(this.driver, logoutLink);
|
||||
home.assertUserNameEmpty();
|
||||
return home;
|
||||
}
|
||||
|
||||
private HomePage switchBackRobHomePage() {
|
||||
HomePage homePage = logoutLukeAccount();
|
||||
|
||||
String robSwitch = homePage.getSwitchElementId(ROB);
|
||||
String robSwitchLink = homePage.getContentAttributeByElementId(robSwitch, HREF);
|
||||
HomePage robHome = homePage.home(this.driver, robSwitchLink);
|
||||
WebElement username = robHome.getElementById(UN);
|
||||
assertThat(username.getText()).isEqualTo(ROB);
|
||||
return robHome;
|
||||
}
|
||||
|
||||
private HomePage logoutRobAccount() {
|
||||
HomePage robHome = switchBackRobHomePage();
|
||||
|
||||
String logoutLink = robHome.getContentAttributeByElementId(LOGOUT, HREF);
|
||||
HomePage home = robHome.home(this.driver, logoutLink);
|
||||
home.assertUserNameEmpty();
|
||||
return home;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.pages;
|
||||
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.PageFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Pool Dolorier
|
||||
*/
|
||||
public abstract class BasePage {
|
||||
|
||||
private WebDriver driver;
|
||||
|
||||
public BasePage(WebDriver driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
public WebDriver getDriver() {
|
||||
return this.driver;
|
||||
}
|
||||
|
||||
public static void get(WebDriver driver, String get) {
|
||||
String baseUrl = "http://localhost:" + System.getProperty("app.port");
|
||||
driver.get(baseUrl + get);
|
||||
}
|
||||
|
||||
public static void getFrom(WebDriver driver, String resourceUrl) {
|
||||
driver.get(resourceUrl);
|
||||
}
|
||||
|
||||
public HomePage home(WebDriver driver, String resourceUrl) {
|
||||
getFrom(driver, resourceUrl);
|
||||
return PageFactory.initElements(driver, HomePage.class);
|
||||
}
|
||||
|
||||
public LinkPage linkPage(WebDriver driver, String resourceUrl) {
|
||||
getFrom(driver, resourceUrl);
|
||||
return PageFactory.initElements(driver, LinkPage.class);
|
||||
}
|
||||
|
||||
public String getSwitchElementId(String user) {
|
||||
return "switchAccount" + user;
|
||||
}
|
||||
|
||||
public WebElement getElementById(String id) {
|
||||
return this.driver.findElement(By.id(id));
|
||||
}
|
||||
|
||||
public String getContentAttributeByElementId(String id, String attribute) {
|
||||
WebElement element = getElementById(id);
|
||||
assertThat(element.getAttribute(attribute)).isNotEmpty();
|
||||
return element.getAttribute(attribute);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.pages;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
import org.openqa.selenium.support.PageFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Pool Dolorier
|
||||
*/
|
||||
public class HomePage extends BasePage {
|
||||
|
||||
@FindBy(name = "username")
|
||||
private WebElement username;
|
||||
|
||||
@FindBy(name = "password")
|
||||
private WebElement password;
|
||||
|
||||
@FindBy(css = "form[method='post']")
|
||||
private WebElement form;
|
||||
|
||||
public HomePage(WebDriver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public static HomePage go(WebDriver driver) {
|
||||
get(driver, "/");
|
||||
return PageFactory.initElements(driver, HomePage.class);
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Demonstrates Multi User Log In");
|
||||
}
|
||||
|
||||
public void assertUserNameEmpty() {
|
||||
assertThat(this.username.getText()).isEmpty();
|
||||
}
|
||||
|
||||
public void assertErrorInvalidAuthentication(WebElement errorMessage) {
|
||||
assertThat(errorMessage.getText()).isEqualTo("Invalid username / password. Please ensure the username is the same as the password.");
|
||||
}
|
||||
|
||||
public void login(String user, String password) {
|
||||
this.username.sendKeys(user);
|
||||
this.password.sendKeys(password);
|
||||
this.form.submit();
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.pages;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Pool Dolorier
|
||||
*/
|
||||
public class LinkPage extends BasePage {
|
||||
|
||||
public LinkPage(WebDriver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
public void assertAt() {
|
||||
assertThat(getDriver().getTitle()).isEqualTo("Linked Page");
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
|
||||
public class Account {
|
||||
private String username;
|
||||
|
||||
private String logoutUrl;
|
||||
|
||||
private String switchAccountUrl;
|
||||
|
||||
public Account(String username, String logoutUrl, String switchAccountUrl) {
|
||||
super();
|
||||
this.username = username;
|
||||
this.logoutUrl = logoutUrl;
|
||||
this.switchAccountUrl = switchAccountUrl;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public String getLogoutUrl() {
|
||||
return this.logoutUrl;
|
||||
}
|
||||
|
||||
public String getSwitchAccountUrl() {
|
||||
return this.switchAccountUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Import(EmbeddedRedisConfig.class)
|
||||
// tag::class[]
|
||||
@Configuration
|
||||
@EnableRedisHttpSession
|
||||
public class Config {
|
||||
|
||||
@Bean
|
||||
public LettuceConnectionFactory connectionFactory() {
|
||||
return new LettuceConnectionFactory();
|
||||
}
|
||||
}
|
||||
// end::class[]
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
|
||||
@Configuration
|
||||
@Profile("embedded-redis")
|
||||
public class EmbeddedRedisConfig {
|
||||
|
||||
private static final String REDIS_DOCKER_IMAGE = "redis:4.0.2";
|
||||
|
||||
@Bean(initMethod = "start")
|
||||
public GenericContainer redisContainer() {
|
||||
return new GenericContainer(REDIS_DOCKER_IMAGE) {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
try {
|
||||
this.dockerClient.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}.withExposedPorts(6379);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public LettuceConnectionFactory redisConnectionFactory() {
|
||||
return new LettuceConnectionFactory(redisContainer().getContainerIpAddress(),
|
||||
redisContainer().getFirstMappedPort());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class Initializer extends AbstractHttpSessionApplicationInitializer {
|
||||
|
||||
public Initializer() {
|
||||
super(Config.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterSessionRepositoryFilter(ServletContext servletContext) {
|
||||
appendFilters(servletContext, new UserAccountsFilter());
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@WebServlet("/login")
|
||||
public class LoginServlet extends HttpServlet {
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
String username = req.getParameter("username");
|
||||
String password = req.getParameter("password");
|
||||
|
||||
if (username != null && !"".equals(username) && username.equals(password)) {
|
||||
req.getSession().setAttribute("username", username);
|
||||
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
|
||||
resp.sendRedirect(url);
|
||||
}
|
||||
else {
|
||||
String url = resp.encodeRedirectURL(req.getContextPath() + "/?error");
|
||||
resp.sendRedirect(url);
|
||||
}
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -8157634860354132501L;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
@WebServlet("/logout")
|
||||
public class LogoutServlet extends HttpServlet {
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
HttpSession session = req.getSession(false);
|
||||
if (session != null) {
|
||||
session.invalidate();
|
||||
}
|
||||
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
|
||||
resp.sendRedirect(url);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 4061762524521437433L;
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.web.http.HttpSessionManager;
|
||||
|
||||
public class UserAccountsFilter implements Filter {
|
||||
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
|
||||
// tag::HttpSessionManager[]
|
||||
HttpSessionManager sessionManager = (HttpSessionManager) httpRequest
|
||||
.getAttribute(HttpSessionManager.class.getName());
|
||||
// end::HttpSessionManager[]
|
||||
SessionRepository<Session> repo = (SessionRepository<Session>) httpRequest
|
||||
.getAttribute(SessionRepository.class.getName());
|
||||
|
||||
String currentSessionAlias = sessionManager.getCurrentSessionAlias(httpRequest);
|
||||
Map<String, String> sessionIds = sessionManager.getSessionIds(httpRequest);
|
||||
String unauthenticatedAlias = null;
|
||||
|
||||
String contextPath = httpRequest.getContextPath();
|
||||
List<Account> accounts = new ArrayList<>();
|
||||
Account currentAccount = null;
|
||||
for (Map.Entry<String, String> entry : sessionIds.entrySet()) {
|
||||
String alias = entry.getKey();
|
||||
String sessionId = entry.getValue();
|
||||
|
||||
Session session = repo.findById(sessionId);
|
||||
if (session == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String username = session.getAttribute("username");
|
||||
if (username == null) {
|
||||
unauthenticatedAlias = alias;
|
||||
continue;
|
||||
}
|
||||
|
||||
String logoutUrl = sessionManager.encodeURL("./logout", alias);
|
||||
String switchAccountUrl = sessionManager.encodeURL("./", alias);
|
||||
Account account = new Account(username, logoutUrl, switchAccountUrl);
|
||||
if (currentSessionAlias.equals(alias)) {
|
||||
currentAccount = account;
|
||||
}
|
||||
else {
|
||||
accounts.add(account);
|
||||
}
|
||||
}
|
||||
|
||||
// tag::addAccountUrl[]
|
||||
String addAlias = unauthenticatedAlias == null ? // <1>
|
||||
sessionManager.getNewSessionAlias(httpRequest)
|
||||
: // <2>
|
||||
unauthenticatedAlias; // <3>
|
||||
String addAccountUrl = sessionManager.encodeURL(contextPath, addAlias); // <4>
|
||||
// end::addAccountUrl[]
|
||||
|
||||
httpRequest.setAttribute("currentAccount", currentAccount);
|
||||
httpRequest.setAttribute("addAccountUrl", addAccountUrl);
|
||||
httpRequest.setAttribute("accounts", accounts);
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- <logger name="org.springframework.security" level="DEBUG"/> -->
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -1,22 +0,0 @@
|
||||
/*!
|
||||
* IE10 viewport hack for Surface/desktop Windows 8 bug
|
||||
* Copyright 2014 Twitter, Inc.
|
||||
* Licensed under the Creative Commons Attribution 3.0 Unported License. For
|
||||
* details, see http://creativecommons.org/licenses/by/3.0/.
|
||||
*/
|
||||
|
||||
// See the Getting Started docs for more information:
|
||||
// http://getbootstrap.com/getting-started/#support-ie10-width
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
|
||||
var msViewportStyle = document.createElement('style')
|
||||
msViewportStyle.appendChild(
|
||||
document.createTextNode(
|
||||
'@-ms-viewport{width:auto!important}'
|
||||
)
|
||||
)
|
||||
document.querySelector('head').appendChild(msViewportStyle)
|
||||
}
|
||||
})();
|
||||
@@ -1,112 +0,0 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Demonstrates Multi User Log In</title>
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Static navbar -->
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="https://github.com/spring-projects/spring-session/">Spring Session</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<c:url value="/" var="homeUrl"/>
|
||||
<li class="active"><a id="navHome" href="${homeUrl}">Home</a></li>
|
||||
<li>
|
||||
<!-- tag::link[]
|
||||
-->
|
||||
<c:url value="/link.jsp" var="linkUrl"/>
|
||||
<a id="navLink" href="${linkUrl}">Link</a>
|
||||
<!-- end::link[]
|
||||
-->
|
||||
</li>
|
||||
</ul>
|
||||
<c:if test="${currentAccount != null or not empty accounts}">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a id="toggle" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><c:out value="${username}"/> <span class="caret"></span></a>
|
||||
<ul id="user-menu" class="dropdown-menu" role="menu">
|
||||
<c:if test="${currentAccount != null}">
|
||||
<li><a id="logout" href="${currentAccount.logoutUrl}">Log Out</a></li>
|
||||
<li><a id="addAccount" href="${addAccountUrl}">Add Account</a></li>
|
||||
</c:if>
|
||||
<c:if test="${not empty accounts}">
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header">Switch Account</li>
|
||||
<li class="divider"></li>
|
||||
</c:if>
|
||||
<c:forEach items="${accounts}" var="account">
|
||||
<c:set var="encodedUsername">
|
||||
<c:out value="${account.username}"/>
|
||||
</c:set>
|
||||
<li><a id="switchAccount${encodedUsername}" href="${account.switchAccountUrl}"><c:out value="${account.username}"/></a></li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</c:if>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
<h1>Description</h1>
|
||||
<p>This application demonstrates how to use Spring Session to authenticate as multiple users at a time. View authenticated users in the upper right corner.</p>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${username == null}">
|
||||
<h1>Please Log In</h1>
|
||||
<p>You are not currently authenticated with this session. You can authenticate with any username password combination that are equal. A few examples to try:</p>
|
||||
<ul>
|
||||
<li><b>Username</b> rob and <b>Password</b> rob</li>
|
||||
<li><b>Username</b> luke and <b>Password</b> luke</li>
|
||||
</ul>
|
||||
<c:if test="${param.error != null}">
|
||||
<div id="error" class="alert alert-danger">Invalid username / password. Please ensure the username is the same as the password.</div>
|
||||
</c:if>
|
||||
<c:url value="/login" var="loginUrl"/>
|
||||
<form action="${loginUrl}" method="post">
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input id="username" type="text" name="username"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input id="password" type="password" name="password"/>
|
||||
</div>
|
||||
<input type="submit" value="Login"/>
|
||||
</form>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<h1 id="un"><c:out value="${username}"/></h1>
|
||||
<p>You are authenticated as <b><c:out value="${username}"/></b>. Observe that you can <a href="${linkUrl}">navigate links</a> and the correct session is used. Using the links in the upper right corner you can:</p>
|
||||
<ul>
|
||||
<li>Log Out</li>
|
||||
<li>Switch Accounts</li>
|
||||
<li>Add Account</li>
|
||||
</ul>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
<!-- Bootstrap core JavaScript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<wj:locate path="jquery.min.js" relativeTo="META-INF/resources" var="jqueryLocation"/>
|
||||
<script src="<c:url value="${jqueryLocation}"/>"></script>
|
||||
<wj:locate path="bootstrap.min.js" relativeTo="META-INF/resources" var="bootstrapJsLocation"/>
|
||||
<script src="<c:url value="${bootstrapJsLocation}"/>"></script>
|
||||
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
|
||||
<script src="<c:url value="/assets/js/ie10-viewport-bug-workaround.js"/>"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,85 +0,0 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<%@ taglib prefix="wj" uri="http://www.webjars.org/tags" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Linked Page</title>
|
||||
<wj:locate path="bootstrap.min.css" relativeTo="META-INF/resources" var="bootstrapCssLocation"/>
|
||||
<link rel="stylesheet" href="<c:url value="${bootstrapCssLocation}"/>">
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Static navbar -->
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="https://github.com/spring-projects/spring-session/">Spring Session</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<c:url value="/" var="homeUrl"/>
|
||||
<li><a id="navHome" href="${homeUrl}">Home</a></li>
|
||||
<c:url value="/link.jsp" var="linkUrl"/>
|
||||
<li class="active"><a id="navLink" href="${linkUrl}">Link</a></li>
|
||||
|
||||
</ul>
|
||||
<c:if test="${currentAccount != null or not empty accounts}">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a id="toggle" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><c:out value="${username}"/> <span class="caret"></span></a>
|
||||
<ul id="user-menu" class="dropdown-menu" role="menu">
|
||||
<c:if test="${currentAccount != null}">
|
||||
<li><a id="logout" href="${currentAccount.logoutUrl}">Log Out</a></li>
|
||||
<li><a id="addAccount" href="${addAccountUrl}">Add Account</a></li>
|
||||
</c:if>
|
||||
<c:if test="${not empty accounts}">
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header">Switch Account</li>
|
||||
<li class="divider"></li>
|
||||
</c:if>
|
||||
<c:forEach items="${accounts}" var="account">
|
||||
<li><a id="switchAccount${account.username}" href="${account.switchAccountUrl}"><c:out value="${account.username}"/></a></li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</c:if>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div><!--/.container-fluid -->
|
||||
</nav>
|
||||
|
||||
<h1>Description</h1>
|
||||
<p>This page demonstrates how we keep track of the correct user session between links even when multiple tabs are open. Try opening another tab with a different user and browse. Then come back to the original tab and see the correct user is maintained.</p>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${username == null}">
|
||||
<h1>Please Log In</h1>
|
||||
<p>You are not currently authenticated with this session. <a href="${homeUrl}">Log In</a></p>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<h1 id="un"><c:out value="${username}"/></h1>
|
||||
<p>You are authenticated as <b><c:out value="${username}"/></b>. Observe that you can <a href="${linkUrl}">navigate links</a> and the correct session is used. Using the links in the upper right corner you can:</p>
|
||||
<ul>
|
||||
<li>Log Out</li>
|
||||
<li>Switch Accounts</li>
|
||||
<li>Add Account</li>
|
||||
</ul>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
<!-- Bootstrap core JavaScript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<wj:locate path="jquery.min.js" relativeTo="META-INF/resources" var="jqueryLocation"/>
|
||||
<script src="<c:url value="${jqueryLocation}"/>"></script>
|
||||
<wj:locate path="bootstrap.min.js" relativeTo="META-INF/resources" var="bootstrapJsLocation"/>
|
||||
<script src="<c:url value="${bootstrapJsLocation}"/>"></script>
|
||||
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
|
||||
<script src="<c:url value="/assets/js/ie10-viewport-bug-workaround.js"/>"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -42,7 +42,6 @@ import org.springframework.session.web.http.CookieHttpSessionStrategy;
|
||||
import org.springframework.session.web.http.CookieSerializer;
|
||||
import org.springframework.session.web.http.DefaultCookieSerializer;
|
||||
import org.springframework.session.web.http.HttpSessionStrategy;
|
||||
import org.springframework.session.web.http.MultiHttpSessionStrategy;
|
||||
import org.springframework.session.web.http.SessionEventHttpSessionListenerAdapter;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -127,16 +126,11 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
|
||||
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(
|
||||
sessionRepository);
|
||||
sessionRepositoryFilter.setServletContext(this.servletContext);
|
||||
if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) {
|
||||
sessionRepositoryFilter.setHttpSessionStrategy(
|
||||
(MultiHttpSessionStrategy) this.httpSessionStrategy);
|
||||
}
|
||||
else {
|
||||
sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);
|
||||
}
|
||||
sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);
|
||||
return sessionRepositoryFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
if (ClassUtils.isPresent(
|
||||
|
||||
@@ -16,20 +16,10 @@
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.web.http.CookieSerializer.CookieValue;
|
||||
@@ -68,222 +58,38 @@ import org.springframework.session.web.http.CookieSerializer.CookieValue;
|
||||
* Set-Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6; Expires=Thur, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly
|
||||
* </pre>
|
||||
*
|
||||
* <h2>Supporting Multiple Simultaneous Sessions</h2>
|
||||
*
|
||||
* <p>
|
||||
* By default multiple sessions are also supported. Once a session is established with the
|
||||
* browser, another session can be initiated by specifying a unique value for the
|
||||
* {@link #setSessionAliasParamName(String)}. For example, a request to:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* GET /messages/?_s=1416195761178 HTTP/1.1
|
||||
* Host: example.com
|
||||
* Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6
|
||||
* </pre>
|
||||
*
|
||||
* Will result in the following response:
|
||||
*
|
||||
* <pre>
|
||||
* HTTP/1.1 200 OK
|
||||
* Set-Cookie: SESSION="0 f81d4fae-7dec-11d0-a765-00a0c91e6bf6 1416195761178 8a929cde-2218-4557-8d4e-82a79a37876d"; Expires=Thur, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* To use the original session a request without the HTTP parameter u can be made. To use
|
||||
* the new session, a request with the HTTP parameter _s=1416195761178 can be used. By
|
||||
* default URLs will be rewritten to include the currently selected session.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Selecting Sessions</h2>
|
||||
*
|
||||
* <p>
|
||||
* Sessions can be managed by using the HttpSessionManager and SessionRepository. If you
|
||||
* are not using Spring in the rest of your application you can obtain a reference from
|
||||
* the HttpServletRequest attributes. An example is provided below:
|
||||
* </p>
|
||||
*
|
||||
* <code>
|
||||
* HttpSessionManager sessionManager =
|
||||
* (HttpSessionManager) req.getAttribute(HttpSessionManager.class.getName());
|
||||
* SessionRepository<Session> repo =
|
||||
* (SessionRepository<Session>) req.getAttribute(SessionRepository.class.getName());
|
||||
*
|
||||
* String currentSessionAlias = sessionManager.getCurrentSessionAlias(req);
|
||||
* Map<String, String> sessionIds = sessionManager.getSessionIds(req);
|
||||
* String newSessionAlias = String.valueOf(System.currentTimeMillis());
|
||||
*
|
||||
* String contextPath = req.getContextPath();
|
||||
* List<Account> accounts = new ArrayList<>();
|
||||
* Account currentAccount = null; for(Map.Entry<String, String> entry :
|
||||
* sessionIds.entrySet()) { String alias = entry.getKey(); String sessionId =
|
||||
* entry.getValue();
|
||||
* </code>
|
||||
*
|
||||
* Session session = repo.findById(sessionId); if(session == null) { continue; }
|
||||
*
|
||||
* String username = session.getAttribute("username"); if(username == null) {
|
||||
* newSessionAlias = alias; continue; }
|
||||
*
|
||||
* String logoutUrl = sessionManager.encodeURL("./logout", alias); String switchAccountUrl
|
||||
* = sessionManager.encodeURL("./", alias); Account account = new Account(username,
|
||||
* logoutUrl, switchAccountUrl); if(currentSessionAlias.equals(alias)) { currentAccount =
|
||||
* account; } else { accounts.add(account); } }
|
||||
*
|
||||
* req.setAttribute("currentAccount", currentAccount); req.setAttribute("addAccountUrl",
|
||||
* sessionManager.encodeURL(contextPath, newSessionAlias)); req.setAttribute("accounts",
|
||||
* accounts); }
|
||||
*
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
*/
|
||||
public final class CookieHttpSessionStrategy
|
||||
implements MultiHttpSessionStrategy, HttpSessionManager {
|
||||
/**
|
||||
* The default delimiter for both serialization and deserialization.
|
||||
*/
|
||||
private static final String DEFAULT_DELIMITER = " ";
|
||||
public final class CookieHttpSessionStrategy implements HttpSessionStrategy {
|
||||
|
||||
private static final String SESSION_IDS_WRITTEN_ATTR = CookieHttpSessionStrategy.class
|
||||
.getName().concat(".SESSIONS_WRITTEN_ATTR");
|
||||
|
||||
static final String DEFAULT_ALIAS = "0";
|
||||
|
||||
static final String DEFAULT_SESSION_ALIAS_PARAM_NAME = "_s";
|
||||
|
||||
private static final Pattern ALIAS_PATTERN = Pattern.compile("^[\\w-]{1,50}$");
|
||||
|
||||
private String sessionParam = DEFAULT_SESSION_ALIAS_PARAM_NAME;
|
||||
private static final String WRITTEN_SESSION_ID_ATTR = CookieHttpSessionStrategy.class
|
||||
.getName().concat(".WRITTEN_SESSION_ID_ATTR");
|
||||
|
||||
private CookieSerializer cookieSerializer = new DefaultCookieSerializer();
|
||||
|
||||
/**
|
||||
* The delimiter between a session alias and a session id when reading a cookie value.
|
||||
* The default value is " ".
|
||||
*/
|
||||
private String deserializationDelimiter = DEFAULT_DELIMITER;
|
||||
|
||||
/**
|
||||
* The delimiter between a session alias and a session id when writing a cookie value.
|
||||
* The default is " ".
|
||||
*/
|
||||
private String serializationDelimiter = DEFAULT_DELIMITER;
|
||||
|
||||
public String getRequestedSessionId(HttpServletRequest request) {
|
||||
Map<String, String> sessionIds = getSessionIds(request);
|
||||
String sessionAlias = getCurrentSessionAlias(request);
|
||||
return sessionIds.get(sessionAlias);
|
||||
}
|
||||
|
||||
public String getCurrentSessionAlias(HttpServletRequest request) {
|
||||
if (this.sessionParam == null) {
|
||||
return DEFAULT_ALIAS;
|
||||
}
|
||||
String u = request.getParameter(this.sessionParam);
|
||||
if (u == null) {
|
||||
return DEFAULT_ALIAS;
|
||||
}
|
||||
if (!ALIAS_PATTERN.matcher(u).matches()) {
|
||||
return DEFAULT_ALIAS;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
public String getNewSessionAlias(HttpServletRequest request) {
|
||||
Set<String> sessionAliases = getSessionIds(request).keySet();
|
||||
if (sessionAliases.isEmpty()) {
|
||||
return DEFAULT_ALIAS;
|
||||
}
|
||||
long lastAlias = Long.decode(DEFAULT_ALIAS);
|
||||
for (String alias : sessionAliases) {
|
||||
long selectedAlias = safeParse(alias);
|
||||
if (selectedAlias > lastAlias) {
|
||||
lastAlias = selectedAlias;
|
||||
}
|
||||
}
|
||||
return Long.toHexString(lastAlias + 1);
|
||||
}
|
||||
|
||||
private long safeParse(String hex) {
|
||||
try {
|
||||
return Long.decode("0x" + hex);
|
||||
}
|
||||
catch (NumberFormatException notNumber) {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public List<String> getRequestedSessionIds(HttpServletRequest request) {
|
||||
return this.cookieSerializer.readCookieValues(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewSession(Session session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
Set<String> sessionIdsWritten = getSessionIdsWritten(request);
|
||||
if (sessionIdsWritten.contains(session.getId())) {
|
||||
String sessionId = session.getId();
|
||||
if (sessionId.equals(request.getAttribute(WRITTEN_SESSION_ID_ATTR))) {
|
||||
return;
|
||||
}
|
||||
sessionIdsWritten.add(session.getId());
|
||||
|
||||
Map<String, String> sessionIds = getSessionIds(request);
|
||||
String sessionAlias = getCurrentSessionAlias(request);
|
||||
sessionIds.put(sessionAlias, session.getId());
|
||||
|
||||
String cookieValue = createSessionCookieValue(sessionIds);
|
||||
request.setAttribute(WRITTEN_SESSION_ID_ATTR, sessionId);
|
||||
this.cookieSerializer
|
||||
.writeCookieValue(new CookieValue(request, response, cookieValue));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set<String> getSessionIdsWritten(HttpServletRequest request) {
|
||||
Set<String> sessionsWritten = (Set<String>) request
|
||||
.getAttribute(SESSION_IDS_WRITTEN_ATTR);
|
||||
if (sessionsWritten == null) {
|
||||
sessionsWritten = new HashSet<>();
|
||||
request.setAttribute(SESSION_IDS_WRITTEN_ATTR, sessionsWritten);
|
||||
}
|
||||
return sessionsWritten;
|
||||
}
|
||||
|
||||
private String createSessionCookieValue(Map<String, String> sessionIds) {
|
||||
if (sessionIds.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
if (sessionIds.size() == 1 && sessionIds.keySet().contains(DEFAULT_ALIAS)) {
|
||||
return sessionIds.values().iterator().next();
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : sessionIds.entrySet()) {
|
||||
String alias = entry.getKey();
|
||||
String id = entry.getValue();
|
||||
|
||||
sb.append(alias);
|
||||
sb.append(this.serializationDelimiter);
|
||||
sb.append(id);
|
||||
sb.append(this.serializationDelimiter);
|
||||
}
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
return sb.toString();
|
||||
.writeCookieValue(new CookieValue(request, response, sessionId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidateSession(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
Map<String, String> sessionIds = getSessionIds(request);
|
||||
String requestedAlias = getCurrentSessionAlias(request);
|
||||
sessionIds.remove(requestedAlias);
|
||||
|
||||
String cookieValue = createSessionCookieValue(sessionIds);
|
||||
this.cookieSerializer
|
||||
.writeCookieValue(new CookieValue(request, response, cookieValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the HTTP parameter that is used to specify the session alias. If
|
||||
* the value is null, then only a single session is supported per browser.
|
||||
*
|
||||
* @param sessionAliasParamName the name of the HTTP parameter used to specify the
|
||||
* session alias. If null, then ony a single session is supported per browser.
|
||||
*/
|
||||
public void setSessionAliasParamName(String sessionAliasParamName) {
|
||||
this.sessionParam = sessionAliasParamName;
|
||||
this.cookieSerializer.writeCookieValue(new CookieValue(request, response, ""));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,151 +104,4 @@ public final class CookieHttpSessionStrategy
|
||||
this.cookieSerializer = cookieSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the delimiter between a session alias and a session id when deserializing a
|
||||
* cookie. The default is " " This is useful when using
|
||||
* <a href="https://tools.ietf.org/html/rfc6265">RFC 6265</a> for writing the cookies
|
||||
* which doesn't allow for spaces in the cookie values.
|
||||
*
|
||||
* @param delimiter the delimiter to set (i.e. "_ " will try a delimeter of either "_"
|
||||
* or " ")
|
||||
*/
|
||||
public void setDeserializationDelimiter(String delimiter) {
|
||||
this.deserializationDelimiter = delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the delimiter between a session alias and a session id when deserializing a
|
||||
* cookie. The default is " ". This is useful when using
|
||||
* <a href="https://tools.ietf.org/html/rfc6265">RFC 6265</a> for writing the cookies
|
||||
* which doesn't allow for spaces in the cookie values.
|
||||
*
|
||||
* @param delimiter the delimiter to set (i.e. "_")
|
||||
*/
|
||||
public void setSerializationDelimiter(String delimiter) {
|
||||
this.serializationDelimiter = delimiter;
|
||||
}
|
||||
|
||||
public Map<String, String> getSessionIds(HttpServletRequest request) {
|
||||
List<String> cookieValues = this.cookieSerializer.readCookieValues(request);
|
||||
String sessionCookieValue = cookieValues.isEmpty() ? ""
|
||||
: cookieValues.iterator().next();
|
||||
Map<String, String> result = new LinkedHashMap<>();
|
||||
StringTokenizer tokens = new StringTokenizer(sessionCookieValue,
|
||||
this.deserializationDelimiter);
|
||||
if (tokens.countTokens() == 1) {
|
||||
result.put(DEFAULT_ALIAS, tokens.nextToken());
|
||||
return result;
|
||||
}
|
||||
while (tokens.hasMoreTokens()) {
|
||||
String alias = tokens.nextToken();
|
||||
if (!tokens.hasMoreTokens()) {
|
||||
break;
|
||||
}
|
||||
String id = tokens.nextToken();
|
||||
result.put(alias, id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public HttpServletRequest wrapRequest(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
request.setAttribute(HttpSessionManager.class.getName(), this);
|
||||
return request;
|
||||
}
|
||||
|
||||
public HttpServletResponse wrapResponse(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
return new MultiSessionHttpServletResponse(response, request);
|
||||
}
|
||||
|
||||
public String encodeURL(String url, String sessionAlias) {
|
||||
String encodedSessionAlias = urlEncode(sessionAlias);
|
||||
int queryStart = url.indexOf("?");
|
||||
boolean isDefaultAlias = DEFAULT_ALIAS.equals(encodedSessionAlias);
|
||||
if (queryStart < 0) {
|
||||
return isDefaultAlias ? url
|
||||
: url + "?" + this.sessionParam + "=" + encodedSessionAlias;
|
||||
}
|
||||
String path = url.substring(0, queryStart);
|
||||
String query = url.substring(queryStart + 1, url.length());
|
||||
String replacement = isDefaultAlias ? "" : "$1" + encodedSessionAlias;
|
||||
query = query.replaceFirst("((^|&)" + this.sessionParam + "=)([^&]+)?",
|
||||
replacement);
|
||||
String sessionParamReplacement = String.format("%s=%s", this.sessionParam,
|
||||
encodedSessionAlias);
|
||||
|
||||
if (!isDefaultAlias && !query.contains(sessionParamReplacement)
|
||||
&& url.endsWith(query)) {
|
||||
// no existing alias
|
||||
if (!(query.endsWith("&") || query.length() == 0)) {
|
||||
query += "&";
|
||||
}
|
||||
query += sessionParamReplacement;
|
||||
}
|
||||
|
||||
return path + "?" + query;
|
||||
}
|
||||
|
||||
private String urlEncode(String value) {
|
||||
try {
|
||||
return URLEncoder.encode(value, "UTF-8");
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link CookieHttpSessionStrategy} aware {@link HttpServletResponseWrapper}.
|
||||
*/
|
||||
class MultiSessionHttpServletResponse extends HttpServletResponseWrapper {
|
||||
private final HttpServletRequest request;
|
||||
|
||||
MultiSessionHttpServletResponse(HttpServletResponse response,
|
||||
HttpServletRequest request) {
|
||||
super(response);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
private String getCurrentSessionAliasFromUrl(String url) {
|
||||
String currentSessionAliasFromUrl = null;
|
||||
int queryStart = url.indexOf("?");
|
||||
|
||||
if (queryStart >= 0) {
|
||||
String query = url.substring(queryStart + 1);
|
||||
Matcher matcher = Pattern
|
||||
.compile(String.format("%s=([^&]+)",
|
||||
CookieHttpSessionStrategy.this.sessionParam))
|
||||
.matcher(query);
|
||||
|
||||
if (matcher.find()) {
|
||||
currentSessionAliasFromUrl = matcher.group(1);
|
||||
}
|
||||
}
|
||||
|
||||
return currentSessionAliasFromUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeRedirectURL(String url) {
|
||||
String encodedUrl = super.encodeRedirectURL(url);
|
||||
String currentSessionAliasFromUrl = getCurrentSessionAliasFromUrl(encodedUrl);
|
||||
String alias = (currentSessionAliasFromUrl != null)
|
||||
? currentSessionAliasFromUrl : getCurrentSessionAlias(this.request);
|
||||
|
||||
return CookieHttpSessionStrategy.this.encodeURL(encodedUrl, alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeURL(String url) {
|
||||
String encodedUrl = super.encodeURL(url);
|
||||
String currentSessionAliasFromUrl = getCurrentSessionAliasFromUrl(encodedUrl);
|
||||
String alias = (currentSessionAliasFromUrl != null)
|
||||
? currentSessionAliasFromUrl : getCurrentSessionAlias(this.request);
|
||||
|
||||
return CookieHttpSessionStrategy.this.encodeURL(encodedUrl, alias);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -52,21 +55,27 @@ import org.springframework.session.Session;
|
||||
* </pre>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
*/
|
||||
public class HeaderHttpSessionStrategy implements HttpSessionStrategy {
|
||||
|
||||
private String headerName = "X-Auth-Token";
|
||||
|
||||
public String getRequestedSessionId(HttpServletRequest request) {
|
||||
return request.getHeader(this.headerName);
|
||||
@Override
|
||||
public List<String> getRequestedSessionIds(HttpServletRequest request) {
|
||||
String headerValue = request.getHeader(this.headerName);
|
||||
return headerValue != null ? Collections.singletonList(headerValue)
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewSession(Session session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
response.setHeader(this.headerName, session.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidateSession(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
response.setHeader(this.headerName, "");
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Allows managing a mapping of alias to the session id for having multiple active
|
||||
* sessions at the same time.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
public interface HttpSessionManager {
|
||||
|
||||
/**
|
||||
* Gets the current session's alias from the {@link HttpServletRequest}.
|
||||
*
|
||||
* @param request the {@link HttpServletRequest} to obtain the current session's alias
|
||||
* from.
|
||||
* @return the current sessions' alias. Cannot be null.
|
||||
*/
|
||||
String getCurrentSessionAlias(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Gets a mapping of the session alias to the session id from the
|
||||
* {@link HttpServletRequest}.
|
||||
*
|
||||
* @param request the {@link HttpServletRequest} to obtain the mapping from. Cannot be
|
||||
* null.
|
||||
* @return a mapping of the session alias to the session id from the
|
||||
* {@link HttpServletRequest}. Cannot be null.
|
||||
*/
|
||||
Map<String, String> getSessionIds(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Provides the ability to encode the URL for a given session alias.
|
||||
*
|
||||
* @param url the url to encode.
|
||||
* @param sessionAlias the session alias to encode.
|
||||
* @return the encoded URL
|
||||
*/
|
||||
String encodeURL(String url, String sessionAlias);
|
||||
|
||||
/**
|
||||
* Gets a new and unique Session alias. Typically this will be called to pass into
|
||||
* {@code HttpSessionManager#encodeURL(java.lang.String)}. For example:
|
||||
*
|
||||
* <code>
|
||||
* String newAlias = httpSessionManager.getNewSessionAlias(request);
|
||||
* String addAccountUrl = httpSessionManager.encodeURL("./", newAlias);
|
||||
* </code>
|
||||
*
|
||||
* @param request the {@link HttpServletRequest} to get a new alias from
|
||||
* @return Gets a new and unique Session alias.
|
||||
*/
|
||||
String getNewSessionAlias(HttpServletRequest request);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2016 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -25,6 +27,7 @@ import org.springframework.session.Session;
|
||||
* A strategy for mapping HTTP request and responses to a {@link Session}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface HttpSessionStrategy {
|
||||
@@ -36,10 +39,9 @@ public interface HttpSessionStrategy {
|
||||
*
|
||||
* @param request the {@link javax.servlet.http.HttpServletRequest} to obtain the
|
||||
* session id from. Cannot be null.
|
||||
* @return the {@link javax.servlet.http.HttpServletRequest} to obtain the session id
|
||||
* from.
|
||||
* @return the session ids
|
||||
*/
|
||||
String getRequestedSessionId(HttpServletRequest request);
|
||||
List<String> getRequestedSessionIds(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* This method is invoked when a new session is created and should inform a client
|
||||
@@ -76,4 +78,5 @@ public interface HttpSessionStrategy {
|
||||
* the {@link org.springframework.session.Session} Cannot be null.
|
||||
*/
|
||||
void onInvalidateSession(HttpServletRequest request, HttpServletResponse response);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Some {@link HttpSessionStrategy} may also want to further customize
|
||||
* {@link HttpServletRequest} and {@link HttpServletResponse} objects. For example,
|
||||
* {@link CookieHttpSessionStrategy} customizes how URL rewriting is done to select which
|
||||
* session should be used in the event multiple sessions are active.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
* @see CookieHttpSessionStrategy
|
||||
*/
|
||||
public interface MultiHttpSessionStrategy
|
||||
extends HttpSessionStrategy, RequestResponsePostProcessor {
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Allows customizing the {@link HttpServletRequest} and/or the
|
||||
* {@link HttpServletResponse}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface RequestResponsePostProcessor {
|
||||
|
||||
/**
|
||||
* Allows customizing the {@link HttpServletRequest}.
|
||||
*
|
||||
* @param request the original {@link HttpServletRequest}. Cannot be null.
|
||||
* @param response the original {@link HttpServletResponse}. This is NOT the result of
|
||||
* {@link #wrapResponse(HttpServletRequest, HttpServletResponse)} Cannot be null. .
|
||||
* @return a non-null {@link HttpServletRequest}
|
||||
*/
|
||||
HttpServletRequest wrapRequest(HttpServletRequest request,
|
||||
HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* Allows customizing the {@link HttpServletResponse}.
|
||||
*
|
||||
* @param request the original {@link HttpServletRequest}. This is NOT the result of
|
||||
* {@link #wrapRequest(HttpServletRequest, HttpServletResponse)}. Cannot be null.
|
||||
* @param response the original {@link HttpServletResponse}. Cannot be null.
|
||||
* @return a non-null {@link HttpServletResponse}
|
||||
*/
|
||||
HttpServletResponse wrapResponse(HttpServletRequest request,
|
||||
HttpServletResponse response);
|
||||
}
|
||||
@@ -51,10 +51,10 @@ import org.springframework.session.SessionRepository;
|
||||
*
|
||||
* <ul>
|
||||
* <li>The session id is looked up using
|
||||
* {@link HttpSessionStrategy#getRequestedSessionId(javax.servlet.http.HttpServletRequest)}
|
||||
* {@link HttpSessionStrategy#getRequestedSessionIds(javax.servlet.http.HttpServletRequest)}
|
||||
* . The default is to look in a cookie named SESSION.</li>
|
||||
* <li>The session id of newly created {@link org.springframework.session.Session}
|
||||
* is sent to the client using
|
||||
* <li>The session id of newly created {@link org.springframework.session.Session} is sent
|
||||
* to the client using
|
||||
* <li>The client is notified that the session id is no longer valid with
|
||||
* {@link HttpSessionStrategy#onInvalidateSession(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
|
||||
* </li>
|
||||
@@ -69,10 +69,11 @@ import org.springframework.session.SessionRepository;
|
||||
* @param <S> the {@link Session} type.
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
|
||||
public class SessionRepositoryFilter<S extends Session>
|
||||
extends OncePerRequestFilter {
|
||||
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
|
||||
|
||||
private static final String SESSION_LOGGER_NAME = SessionRepositoryFilter.class
|
||||
.getName().concat(".SESSION_LOGGER");
|
||||
|
||||
@@ -102,7 +103,7 @@ public class SessionRepositoryFilter<S extends Session>
|
||||
|
||||
private ServletContext servletContext;
|
||||
|
||||
private MultiHttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();
|
||||
private HttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
@@ -123,21 +124,6 @@ public class SessionRepositoryFilter<S extends Session>
|
||||
* @param httpSessionStrategy the {@link HttpSessionStrategy} to use. Cannot be null.
|
||||
*/
|
||||
public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
|
||||
if (httpSessionStrategy == null) {
|
||||
throw new IllegalArgumentException("httpSessionStrategy cannot be null");
|
||||
}
|
||||
this.httpSessionStrategy = new MultiHttpSessionStrategyAdapter(
|
||||
httpSessionStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link MultiHttpSessionStrategy} to be used. The default is a
|
||||
* {@link CookieHttpSessionStrategy}.
|
||||
*
|
||||
* @param httpSessionStrategy the {@link MultiHttpSessionStrategy} to use. Cannot be
|
||||
* null.
|
||||
*/
|
||||
public void setHttpSessionStrategy(MultiHttpSessionStrategy httpSessionStrategy) {
|
||||
if (httpSessionStrategy == null) {
|
||||
throw new IllegalArgumentException("httpSessionStrategy cannot be null");
|
||||
}
|
||||
@@ -155,13 +141,8 @@ public class SessionRepositoryFilter<S extends Session>
|
||||
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
|
||||
wrappedRequest, response);
|
||||
|
||||
HttpServletRequest strategyRequest = this.httpSessionStrategy
|
||||
.wrapRequest(wrappedRequest, wrappedResponse);
|
||||
HttpServletResponse strategyResponse = this.httpSessionStrategy
|
||||
.wrapResponse(wrappedRequest, wrappedResponse);
|
||||
|
||||
try {
|
||||
filterChain.doFilter(strategyRequest, strategyResponse);
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
}
|
||||
finally {
|
||||
wrappedRequest.commitSession();
|
||||
@@ -213,9 +194,13 @@ public class SessionRepositoryFilter<S extends Session>
|
||||
*/
|
||||
private final class SessionRepositoryRequestWrapper
|
||||
extends HttpServletRequestWrapper {
|
||||
|
||||
private Boolean requestedSessionIdValid;
|
||||
|
||||
private boolean requestedSessionInvalidated;
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
private SessionRepositoryRequestWrapper(HttpServletRequest request,
|
||||
@@ -262,6 +247,7 @@ public class SessionRepositoryFilter<S extends Session>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public String changeSessionId() {
|
||||
HttpSession session = getSession(false);
|
||||
@@ -313,25 +299,24 @@ public class SessionRepositoryFilter<S extends Session>
|
||||
return currentSession;
|
||||
}
|
||||
String requestedSessionId = getRequestedSessionId();
|
||||
if (requestedSessionId != null
|
||||
&& getAttribute(INVALID_SESSION_ID_ATTR) == null) {
|
||||
S session = getSession(requestedSessionId);
|
||||
if (session != null) {
|
||||
if (requestedSessionId != null) {
|
||||
if (getAttribute(INVALID_SESSION_ID_ATTR) == null) {
|
||||
S session = getSession(requestedSessionId);
|
||||
this.requestedSessionIdValid = true;
|
||||
currentSession = new HttpSessionWrapper(session, getServletContext());
|
||||
currentSession.setNew(false);
|
||||
setCurrentSession(currentSession);
|
||||
return currentSession;
|
||||
}
|
||||
else {
|
||||
// This is an invalid session id. No need to ask again if
|
||||
// request.getSession is invoked for the duration of this request
|
||||
if (SESSION_LOGGER.isDebugEnabled()) {
|
||||
SESSION_LOGGER.debug(
|
||||
"No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
|
||||
}
|
||||
setAttribute(INVALID_SESSION_ID_ATTR, "true");
|
||||
}
|
||||
else {
|
||||
// This is an invalid session id. No need to ask again if
|
||||
// request.getSession is invoked for the duration of this request
|
||||
if (SESSION_LOGGER.isDebugEnabled()) {
|
||||
SESSION_LOGGER.debug(
|
||||
"No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
|
||||
}
|
||||
setAttribute(INVALID_SESSION_ID_ATTR, "true");
|
||||
}
|
||||
if (!create) {
|
||||
return null;
|
||||
@@ -367,7 +352,10 @@ public class SessionRepositoryFilter<S extends Session>
|
||||
@Override
|
||||
public String getRequestedSessionId() {
|
||||
return SessionRepositoryFilter.this.httpSessionStrategy
|
||||
.getRequestedSessionId(this);
|
||||
.getRequestedSessionIds(this).stream()
|
||||
.filter(sessionId -> SessionRepositoryFilter.this.sessionRepository
|
||||
.findById(sessionId) != null)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,44 +378,7 @@ public class SessionRepositoryFilter<S extends Session>
|
||||
SessionRepositoryFilter.this.sessionRepository.deleteById(getId());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A delegating implementation of {@link MultiHttpSessionStrategy}.
|
||||
*/
|
||||
static class MultiHttpSessionStrategyAdapter implements MultiHttpSessionStrategy {
|
||||
private HttpSessionStrategy delegate;
|
||||
|
||||
/**
|
||||
* Create a new {@link MultiHttpSessionStrategyAdapter} instance.
|
||||
* @param delegate the delegate HTTP session strategy
|
||||
*/
|
||||
MultiHttpSessionStrategyAdapter(HttpSessionStrategy delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public String getRequestedSessionId(HttpServletRequest request) {
|
||||
return this.delegate.getRequestedSessionId(request);
|
||||
}
|
||||
|
||||
public void onNewSession(Session session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
this.delegate.onNewSession(session, request, response);
|
||||
}
|
||||
|
||||
public void onInvalidateSession(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
this.delegate.onInvalidateSession(request, response);
|
||||
}
|
||||
|
||||
public HttpServletRequest wrapRequest(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
return request;
|
||||
}
|
||||
|
||||
public HttpServletResponse wrapResponse(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
package org.springframework.session.config.annotation.web.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
@@ -35,8 +34,9 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.session.web.http.CookieSerializer;
|
||||
import org.springframework.session.web.http.CookieSerializer.CookieValue;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
@@ -46,6 +46,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -72,6 +73,9 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
@Autowired
|
||||
private SessionRepositoryFilter<? extends Session> sessionRepositoryFilter;
|
||||
|
||||
@Autowired
|
||||
private SessionRepository sessionRepository;
|
||||
|
||||
@Autowired
|
||||
private CookieSerializer cookieSerializer;
|
||||
|
||||
@@ -84,7 +88,9 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
public void usesReadSessionIds() throws Exception {
|
||||
String sessionId = "sessionId";
|
||||
given(this.cookieSerializer.readCookieValues(any(HttpServletRequest.class)))
|
||||
.willReturn(Arrays.asList(sessionId));
|
||||
.willReturn(Collections.singletonList(sessionId));
|
||||
given(this.sessionRepository.findById(anyString()))
|
||||
.willReturn(new MapSession(sessionId));
|
||||
|
||||
this.sessionRepositoryFilter.doFilter(this.request, this.response, this.chain);
|
||||
|
||||
@@ -93,6 +99,8 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
|
||||
@Test
|
||||
public void usesWrite() throws Exception {
|
||||
given(this.sessionRepository.findById(anyString())).willReturn(new MapSession());
|
||||
|
||||
this.sessionRepositoryFilter.doFilter(this.request, this.response,
|
||||
new MockFilterChain() {
|
||||
|
||||
@@ -116,8 +124,8 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public MapSessionRepository mapSessionRepository() {
|
||||
return new MapSessionRepository(new ConcurrentHashMap<>());
|
||||
public SessionRepository sessionRepository() {
|
||||
return mock(SessionRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.session.config.annotation.web.http;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.web.http.MultiHttpSessionStrategy;
|
||||
import org.springframework.session.web.http.SessionRepositoryFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringHttpSessionConfiguration} using a custom
|
||||
* {@link MultiHttpSessionStrategy}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@WebAppConfiguration
|
||||
public class EnableSpringHttpSessionCustomMultiHttpSessionStrategyTests {
|
||||
|
||||
@Autowired
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
@Autowired
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
private MockFilterChain chain;
|
||||
|
||||
@Autowired
|
||||
private SessionRepositoryFilter<? extends Session> sessionRepositoryFilter;
|
||||
|
||||
@Autowired
|
||||
private MultiHttpSessionStrategy strategy;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.chain = new MockFilterChain();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrapRequestAndResponseUsed() throws Exception {
|
||||
given(this.strategy.wrapRequest(any(HttpServletRequest.class),
|
||||
any(HttpServletResponse.class))).willReturn(this.request);
|
||||
given(this.strategy.wrapResponse(any(HttpServletRequest.class),
|
||||
any(HttpServletResponse.class))).willReturn(this.response);
|
||||
|
||||
this.sessionRepositoryFilter.doFilter(this.request, this.response, this.chain);
|
||||
|
||||
verify(this.strategy).wrapRequest(any(HttpServletRequest.class),
|
||||
any(HttpServletResponse.class));
|
||||
verify(this.strategy).wrapResponse(any(HttpServletRequest.class),
|
||||
any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
@EnableSpringHttpSession
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public MapSessionRepository mapSessionRepository() {
|
||||
return new MapSessionRepository(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MultiHttpSessionStrategy strategy() {
|
||||
return mock(MultiHttpSessionStrategy.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,10 +17,9 @@
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -32,7 +31,11 @@ import org.springframework.session.Session;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link CookieHttpSessionStrategy}.
|
||||
*/
|
||||
public class CookieHttpSessionStrategyTests {
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@@ -51,22 +54,22 @@ public class CookieHttpSessionStrategyTests {
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNull() throws Exception {
|
||||
assertThat(this.strategy.getRequestedSessionId(this.request)).isNull();
|
||||
assertThat(this.strategy.getRequestedSessionIds(this.request)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNull() throws Exception {
|
||||
setSessionCookie(this.session.getId());
|
||||
assertThat(this.strategy.getRequestedSessionId(this.request))
|
||||
.isEqualTo(this.session.getId());
|
||||
assertThat(this.strategy.getRequestedSessionIds(this.request))
|
||||
.isEqualTo(Collections.singletonList(this.session.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNullCustomCookieName() throws Exception {
|
||||
setCookieName("CUSTOM");
|
||||
setSessionCookie(this.session.getId());
|
||||
assertThat(this.strategy.getRequestedSessionId(this.request))
|
||||
.isEqualTo(this.session.getId());
|
||||
assertThat(this.strategy.getRequestedSessionIds(this.request))
|
||||
.isEqualTo(Collections.singletonList(this.session.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,46 +100,6 @@ public class CookieHttpSessionStrategyTests {
|
||||
assertThat(base64Decode(cookies[1].getValue())).isEqualTo(newSession.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionExistingSessionSameAlias() throws Exception {
|
||||
Session existing = new MapSession();
|
||||
setSessionCookie(existing.getId());
|
||||
this.strategy.onNewSession(this.session, this.request, this.response);
|
||||
assertThat(getSessionId()).isEqualTo(this.session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionExistingSessionNewAlias() throws Exception {
|
||||
Session existing = new MapSession();
|
||||
setSessionCookie(existing.getId());
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
|
||||
this.strategy.onNewSession(this.session, this.request, this.response);
|
||||
assertThat(getSessionId())
|
||||
.isEqualTo("0 " + existing.getId() + " new " + this.session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionExistingSessionNewAliasCustomDelimiter() throws Exception {
|
||||
this.strategy.setSerializationDelimiter("_");
|
||||
Session existing = new MapSession();
|
||||
setSessionCookie(existing.getId());
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
|
||||
this.strategy.onNewSession(this.session, this.request, this.response);
|
||||
assertThat(getSessionId())
|
||||
.isEqualTo("0_" + existing.getId() + "_new_" + this.session.getId());
|
||||
}
|
||||
|
||||
// gh-321
|
||||
@Test
|
||||
public void onNewSessionExplicitAlias() throws Exception {
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
|
||||
this.strategy.onNewSession(this.session, this.request, this.response);
|
||||
assertThat(getSessionId()).isEqualTo("new " + this.session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionCookiePath() throws Exception {
|
||||
this.request.setContextPath("/somethingunique");
|
||||
@@ -177,533 +140,12 @@ public class CookieHttpSessionStrategyTests {
|
||||
assertThat(getSessionId()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeleteSessionExistingSessionSameAlias() throws Exception {
|
||||
Session existing = new MapSession();
|
||||
setSessionCookie("0 " + existing.getId() + " new " + this.session.getId());
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
|
||||
this.strategy.onInvalidateSession(this.request, this.response);
|
||||
assertThat(getSessionId()).isEqualTo(existing.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeleteSessionExistingSessionNewAlias() throws Exception {
|
||||
Session existing = new MapSession();
|
||||
setSessionCookie("0 " + existing.getId() + " new " + this.session.getId());
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
|
||||
this.strategy.onInvalidateSession(this.request, this.response);
|
||||
assertThat(getSessionId()).isEqualTo(existing.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLNoExistingQuery() {
|
||||
assertThat(this.strategy.encodeURL("/url", "2")).isEqualTo("/url?_s=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLNoExistingQueryEmpty() {
|
||||
assertThat(this.strategy.encodeURL("/url?", "2")).isEqualTo("/url?_s=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryNoAlias() {
|
||||
assertThat(this.strategy.encodeURL("/url?a=b", "2")).isEqualTo("/url?a=b&_s=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasStart() {
|
||||
assertThat(this.strategy.encodeURL("/url?_s=1&y=z", "2"))
|
||||
.isEqualTo("/url?_s=2&y=z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasMiddle() {
|
||||
assertThat(this.strategy.encodeURL("/url?a=b&_s=1&y=z", "2"))
|
||||
.isEqualTo("/url?a=b&_s=2&y=z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasEnd() {
|
||||
assertThat(this.strategy.encodeURL("/url?a=b&_s=1", "2"))
|
||||
.isEqualTo("/url?a=b&_s=2");
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryParamEndsWithActualParamStart() {
|
||||
assertThat(this.strategy.encodeURL("/url?x_s=1&y=z", "2"))
|
||||
.isEqualTo("/url?x_s=1&y=z&_s=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryParamEndsWithActualParamMiddle() {
|
||||
assertThat(this.strategy.encodeURL("/url?a=b&x_s=1&y=z", "2"))
|
||||
.isEqualTo("/url?a=b&x_s=1&y=z&_s=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryParamEndsWithActualParamEnd() {
|
||||
assertThat(this.strategy.encodeURL("/url?a=b&x_s=1", "2"))
|
||||
.isEqualTo("/url?a=b&x_s=1&_s=2");
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@Test
|
||||
public void encodeURLNoExistingQueryDefaultAlias() {
|
||||
assertThat(this.strategy.encodeURL("/url", "0")).isEqualTo("/url");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLNoExistingQueryEmptyDefaultAlias() {
|
||||
assertThat(this.strategy.encodeURL("/url?", "0")).isEqualTo("/url?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryNoAliasDefaultAlias() {
|
||||
assertThat(this.strategy.encodeURL("/url?a=b", "0")).isEqualTo("/url?a=b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasStartDefaultAlias() {
|
||||
// relaxed constraint as result /url?&y=z does not hurt anything (ideally should
|
||||
// remove the &)
|
||||
assertThat(this.strategy.encodeURL("/url?_s=1&y=z", "0"))
|
||||
.doesNotContain("_s=0&_s=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasMiddleDefaultAlias() {
|
||||
assertThat(this.strategy.encodeURL("/url?a=b&_s=1&y=z", "0"))
|
||||
.isEqualTo("/url?a=b&y=z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasEndDefaultAlias() {
|
||||
assertThat(this.strategy.encodeURL("/url?a=b&_s=1", "0")).isEqualTo("/url?a=b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLWithSameAlias() {
|
||||
String url = String.format("/url?%s=1",
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME);
|
||||
assertThat(this.strategy.encodeURL(url, "1")).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLWithSameAliasOtherQueryParamsBefore() {
|
||||
String url = String.format("/url?a=b&%s=1",
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME);
|
||||
assertThat(this.strategy.encodeURL(url, "1")).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLWithSameAliasOtherQueryParamsAfter() {
|
||||
String url = String.format("/url?%s=1&a=b",
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME);
|
||||
assertThat(this.strategy.encodeURL(url, "1")).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLWithSameAliasOtherQueryParamsBeforeAndAfter() {
|
||||
String url = String.format("/url?a=b&%s=1&c=d",
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME);
|
||||
assertThat(this.strategy.encodeURL(url, "1")).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLMaliciousAlias() {
|
||||
assertThat(this.strategy.encodeURL("/url?a=b&_s=1",
|
||||
"\"> <script>alert('hi')</script>")).isEqualTo(
|
||||
"/url?a=b&_s=%22%3E+%3Cscript%3Ealert%28%27hi%27%29%3C%2Fscript%3E");
|
||||
}
|
||||
|
||||
// --- getCurrentSessionAlias
|
||||
|
||||
@Test
|
||||
public void getCurrentSessionAliasNull() {
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentSessionAliasNullParamName() {
|
||||
this.strategy.setSessionAliasParamName(null);
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "NOT USED");
|
||||
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
// protect against malicious users
|
||||
@Test
|
||||
public void getCurrentSessionAliasContainsQuote() {
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here\"this");
|
||||
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentSessionAliasContainsSingleQuote() {
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here'this");
|
||||
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentSessionAliasContainsSpace() {
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here this");
|
||||
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentSessionAliasContainsLt() {
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here<this");
|
||||
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentSessionAliasContainsGt() {
|
||||
this.strategy.setSessionAliasParamName(null);
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here>this");
|
||||
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentSessionAliasTooLong() {
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME,
|
||||
"012345678901234567890123456789012345678901234567890");
|
||||
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
// We want some sort of length restrictions, but want to ensure some sort of length
|
||||
// Technically no hard limit, but chose 50
|
||||
@Test
|
||||
public void getCurrentSessionAliasAllows50() {
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME,
|
||||
"01234567890123456789012345678901234567890123456789");
|
||||
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo("01234567890123456789012345678901234567890123456789");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentSession() {
|
||||
String expectedAlias = "1";
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME,
|
||||
expectedAlias);
|
||||
assertThat(this.strategy.getCurrentSessionAlias(this.request))
|
||||
.isEqualTo(expectedAlias);
|
||||
}
|
||||
|
||||
// --- getNewSessionAlias
|
||||
|
||||
@Test
|
||||
public void getNewSessionAliasNoSessions() {
|
||||
assertThat(this.strategy.getNewSessionAlias(this.request))
|
||||
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNewSessionAliasSingleSession() {
|
||||
setSessionCookie("abc");
|
||||
|
||||
assertThat(this.strategy.getNewSessionAlias(this.request)).isEqualTo("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNewSessionAlias2Sessions() {
|
||||
setCookieWithNSessions(2);
|
||||
|
||||
assertThat(this.strategy.getNewSessionAlias(this.request)).isEqualTo("2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNewSessionAlias9Sessions() {
|
||||
setCookieWithNSessions(9);
|
||||
|
||||
assertThat(this.strategy.getNewSessionAlias(this.request))
|
||||
.isEqualToIgnoringCase("9");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNewSessionAlias10Sessions() {
|
||||
setCookieWithNSessions(10);
|
||||
|
||||
assertThat(this.strategy.getNewSessionAlias(this.request))
|
||||
.isEqualToIgnoringCase("a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNewSessionAlias16Sessions() {
|
||||
setCookieWithNSessions(16);
|
||||
|
||||
assertThat(this.strategy.getNewSessionAlias(this.request))
|
||||
.isEqualToIgnoringCase("10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNewSessionAliasInvalidAlias() {
|
||||
setSessionCookie("0 1 $ b");
|
||||
|
||||
assertThat(this.strategy.getNewSessionAlias(this.request))
|
||||
.isEqualToIgnoringCase("1");
|
||||
}
|
||||
|
||||
// --- getSessionIds
|
||||
|
||||
@Test
|
||||
public void getSessionIdsNone() {
|
||||
assertThat(this.strategy.getSessionIds(this.request)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionIdsSingle() {
|
||||
String expectedId = "a";
|
||||
setSessionCookie(expectedId);
|
||||
|
||||
Map<String, String> sessionIds = this.strategy.getSessionIds(this.request);
|
||||
assertThat(sessionIds.size()).isEqualTo(1);
|
||||
assertThat(sessionIds.get("0")).isEqualTo(expectedId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionIdsMulti() {
|
||||
setSessionCookie("0 a 1 b");
|
||||
|
||||
Map<String, String> sessionIds = this.strategy.getSessionIds(this.request);
|
||||
assertThat(sessionIds.size()).isEqualTo(2);
|
||||
assertThat(sessionIds.get("0")).isEqualTo("a");
|
||||
assertThat(sessionIds.get("1")).isEqualTo("b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionIdsMultiCustomDelimeter() {
|
||||
this.strategy.setDeserializationDelimiter("_");
|
||||
setSessionCookie("0_a_1_b");
|
||||
|
||||
Map<String, String> sessionIds = this.strategy.getSessionIds(this.request);
|
||||
assertThat(sessionIds.size()).isEqualTo(2);
|
||||
assertThat(sessionIds.get("0")).isEqualTo("a");
|
||||
assertThat(sessionIds.get("1")).isEqualTo("b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionIdsMultiCustomDelimeterMigration() {
|
||||
this.strategy.setDeserializationDelimiter("_ ");
|
||||
this.strategy.setSerializationDelimiter("_");
|
||||
|
||||
// can parse the old way
|
||||
setSessionCookie("0 a 1 b");
|
||||
|
||||
Map<String, String> sessionIds = this.strategy.getSessionIds(this.request);
|
||||
assertThat(sessionIds.size()).isEqualTo(2);
|
||||
assertThat(sessionIds.get("0")).isEqualTo("a");
|
||||
assertThat(sessionIds.get("1")).isEqualTo("b");
|
||||
|
||||
// can parse the new way
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
setSessionCookie("0_a_1_b");
|
||||
|
||||
sessionIds = this.strategy.getSessionIds(this.request);
|
||||
assertThat(sessionIds.size()).isEqualTo(2);
|
||||
assertThat(sessionIds.get("0")).isEqualTo("a");
|
||||
assertThat(sessionIds.get("1")).isEqualTo("b");
|
||||
|
||||
// writes the new way
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
Session existing = new MapSession();
|
||||
setSessionCookie(existing.getId());
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
|
||||
this.strategy.onNewSession(this.session, this.request, this.response);
|
||||
assertThat(getSessionId())
|
||||
.isEqualTo("0_" + existing.getId() + "_new_" + this.session.getId());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionIdsDangling() {
|
||||
setSessionCookie("0 a 1 b noValue");
|
||||
|
||||
Map<String, String> sessionIds = this.strategy.getSessionIds(this.request);
|
||||
assertThat(sessionIds.size()).isEqualTo(2);
|
||||
assertThat(sessionIds.get("0")).isEqualTo("a");
|
||||
assertThat(sessionIds.get("1")).isEqualTo("b");
|
||||
}
|
||||
|
||||
// --- helper
|
||||
|
||||
@Test
|
||||
public void createSessionCookieValue() {
|
||||
assertThat(createSessionCookieValue(17)).isEqualToIgnoringCase(
|
||||
"0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 e 14 f 15 10 16");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeRedirectUrlWhereRedirectUrlDoesntContainAliasCurrentReqNoAlias() {
|
||||
String url = "http://www.somehost.com/some/path";
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedRedirectUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedRedirectUrl).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeRedirectUrlWhereRedirectUrlDoesntContainAliasCurrentReqHasAlias() {
|
||||
String url = "http://www.somehost.com/some/path";
|
||||
String alias = "1";
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, alias);
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedRedirectUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedRedirectUrl).isEqualTo(String.format("%s?%s=%s", url,
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, alias));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeRedirectUrlWhereRedirectUrlContainsAliasCurrentReqHasNoAlias() {
|
||||
String url = String.format("http://www.somehost.com/some/path?%s=5",
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME);
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "4");
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedRedirectUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedRedirectUrl).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeRedirectUrlWhereRedirectUrlDoesntContainAliasCurrentReqNoAliasWithOtherParams() {
|
||||
String url = "http://www.somehost.com/some/path?a=b";
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedRedirectUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedRedirectUrl).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeRedirectUrlWhereRedirectUrlDoesntContainAliasCurrentReqHasAliasWithOtherParams() {
|
||||
String url = "http://www.somehost.com/some/path?a=b";
|
||||
String alias = "1";
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, alias);
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedRedirectUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedRedirectUrl).isEqualTo(String.format("%s&%s=%s", url,
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, alias));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeRedirectUrlWhereRedirectUrlContainsAliasCurrentReqHasNoAliasWithOtherParams() {
|
||||
String url = String.format("http://www.somehost.com/some/path?a=b&%s=5&c=d",
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME);
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "4");
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedRedirectUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedRedirectUrl).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeUrlWhereRedirectUrlDoesntContainAliasCurrentReqNoAlias() {
|
||||
String url = "http://www.somehost.com/some/path";
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedUrl).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeUrlWhereRedirectUrlDoesntContainAliasCurrentReqHasAlias() {
|
||||
String url = "http://www.somehost.com/some/path";
|
||||
String alias = "1";
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, alias);
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedUrl).isEqualTo(String.format("%s?%s=%s", url,
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, alias));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeUrlWhereRedirectUrlContainsAliasCurrentReqHasNoAlias() {
|
||||
String url = String.format("http://www.somehost.com/some/path?%s=5",
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME);
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "4");
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedUrl).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeUrlWhereRedirectUrlDoesntContainAliasCurrentReqNoAliasWithOtherParams() {
|
||||
String url = "http://www.somehost.com/some/path?a=b";
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedUrl).isEqualTo(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeUrlWhereRedirectUrlDoesntContainAliasCurrentReqHasAliasWithOtherParams() {
|
||||
String url = "http://www.somehost.com/some/path?a=b";
|
||||
String alias = "1";
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, alias);
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedUrl).isEqualTo(String.format("%s&%s=%s", url,
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, alias));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseEncodeUrlWhereRedirectUrlContainsAliasCurrentReqHasNoAliasWithOtherParams() {
|
||||
String url = String.format("http://www.somehost.com/some/path?a=b&%s=5&c=d",
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME);
|
||||
this.request.setParameter(
|
||||
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "4");
|
||||
HttpServletResponse wrappedResponse = this.strategy.wrapResponse(this.request,
|
||||
this.response);
|
||||
String encodedUrl = wrappedResponse.encodeRedirectURL(url);
|
||||
assertThat(encodedUrl).isEqualTo(url);
|
||||
}
|
||||
|
||||
private void setCookieWithNSessions(long size) {
|
||||
setSessionCookie(createSessionCookieValue(size));
|
||||
}
|
||||
|
||||
private String createSessionCookieValue(long size) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -720,18 +162,18 @@ public class CookieHttpSessionStrategyTests {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void setCookieName(String cookieName) {
|
||||
private void setCookieName(String cookieName) {
|
||||
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
|
||||
cookieSerializer.setCookieName(cookieName);
|
||||
this.strategy.setCookieSerializer(cookieSerializer);
|
||||
this.cookieName = cookieName;
|
||||
}
|
||||
|
||||
public void setSessionCookie(String value) {
|
||||
private void setSessionCookie(String value) {
|
||||
this.request.setCookies(new Cookie(this.cookieName, base64Encode(value)));
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
private String getSessionId() {
|
||||
return base64Decode(this.response.getCookie(this.cookieName).getValue());
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -26,6 +28,9 @@ import org.springframework.session.Session;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HeaderHttpSessionStrategy}.
|
||||
*/
|
||||
public class HeaderSessionStrategyTests {
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
@@ -46,22 +51,22 @@ public class HeaderSessionStrategyTests {
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNull() throws Exception {
|
||||
assertThat(this.strategy.getRequestedSessionId(this.request)).isNull();
|
||||
assertThat(this.strategy.getRequestedSessionIds(this.request)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNull() throws Exception {
|
||||
setSessionId(this.session.getId());
|
||||
assertThat(this.strategy.getRequestedSessionId(this.request))
|
||||
.isEqualTo(this.session.getId());
|
||||
assertThat(this.strategy.getRequestedSessionIds(this.request))
|
||||
.isEqualTo(Collections.singletonList(this.session.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNullCustomHeaderName() throws Exception {
|
||||
setHeaderName("CUSTOM");
|
||||
setSessionId(this.session.getId());
|
||||
assertThat(this.strategy.getRequestedSessionId(this.request))
|
||||
.isEqualTo(this.session.getId());
|
||||
assertThat(this.strategy.getRequestedSessionIds(this.request))
|
||||
.isEqualTo(Collections.singletonList(this.session.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,16 +121,16 @@ public class HeaderSessionStrategyTests {
|
||||
this.strategy.setHeaderName(null);
|
||||
}
|
||||
|
||||
public void setHeaderName(String headerName) {
|
||||
private void setHeaderName(String headerName) {
|
||||
this.strategy.setHeaderName(headerName);
|
||||
this.headerName = headerName;
|
||||
}
|
||||
|
||||
public void setSessionId(String id) {
|
||||
private void setSessionId(String id) {
|
||||
this.request.addHeader(this.headerName, id);
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
private String getSessionId() {
|
||||
return this.response.getHeader(this.headerName);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,12 +60,12 @@ import org.springframework.test.util.ReflectionTestUtils;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
@@ -95,8 +95,7 @@ public class SessionRepositoryFilterTests {
|
||||
public void setup() throws Exception {
|
||||
this.sessions = new HashMap<>();
|
||||
this.sessionRepository = new MapSessionRepository(this.sessions);
|
||||
this.filter = new SessionRepositoryFilter<>(
|
||||
this.sessionRepository);
|
||||
this.filter = new SessionRepositoryFilter<>(this.sessionRepository);
|
||||
setupRequest();
|
||||
}
|
||||
|
||||
@@ -134,8 +133,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.setLastAccessedTime(Instant.EPOCH);
|
||||
this.sessionRepository = spy(this.sessionRepository);
|
||||
given(this.sessionRepository.createSession()).willReturn(session);
|
||||
this.filter = new SessionRepositoryFilter<>(
|
||||
this.sessionRepository);
|
||||
this.filter = new SessionRepositoryFilter<>(this.sessionRepository);
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
@@ -240,8 +238,7 @@ public class SessionRepositoryFilterTests {
|
||||
@Test
|
||||
public void doFilterServletContextExplicit() throws Exception {
|
||||
final ServletContext expectedContext = new MockServletContext();
|
||||
this.filter = new SessionRepositoryFilter<>(
|
||||
this.sessionRepository);
|
||||
this.filter = new SessionRepositoryFilter<>(this.sessionRepository);
|
||||
this.filter.setServletContext(expectedContext);
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@@ -431,8 +428,7 @@ public class SessionRepositoryFilterTests {
|
||||
return createSession();
|
||||
}
|
||||
};
|
||||
this.filter = new SessionRepositoryFilter<>(
|
||||
this.sessionRepository);
|
||||
this.filter = new SessionRepositoryFilter<>(this.sessionRepository);
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
@@ -568,7 +564,7 @@ public class SessionRepositoryFilterTests {
|
||||
ReflectionTestUtils.invokeMethod(wrappedRequest, "changeSessionId");
|
||||
fail("Exected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -674,7 +670,7 @@ public class SessionRepositoryFilterTests {
|
||||
sessionContext.getIds().nextElement();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (NoSuchElementException success) {
|
||||
catch (NoSuchElementException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -725,7 +721,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.invalidate();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -742,7 +738,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.getCreationTime();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -759,7 +755,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.getAttribute("attr");
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -776,7 +772,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.getValue("attr");
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -793,7 +789,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.getAttributeNames();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -810,7 +806,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.getValueNames();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -827,7 +823,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.setAttribute("a", "b");
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -844,7 +840,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.putValue("a", "b");
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -861,7 +857,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.removeAttribute("name");
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -878,7 +874,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.removeValue("name");
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -895,7 +891,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.isNew();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -912,7 +908,7 @@ public class SessionRepositoryFilterTests {
|
||||
session.getLastAccessedTime();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (IllegalStateException success) {
|
||||
catch (IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1055,8 +1051,9 @@ public class SessionRepositoryFilterTests {
|
||||
HttpServletResponse wrappedResponse) throws IOException {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.findById(id)).isNotNull();
|
||||
assertThat(
|
||||
SessionRepositoryFilterTests.this.sessionRepository.findById(id))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1070,8 +1067,9 @@ public class SessionRepositoryFilterTests {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
"Error");
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.findById(id)).isNotNull();
|
||||
assertThat(
|
||||
SessionRepositoryFilterTests.this.sessionRepository.findById(id))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1084,8 +1082,9 @@ public class SessionRepositoryFilterTests {
|
||||
HttpServletResponse wrappedResponse) throws IOException {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.sendRedirect("/");
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.findById(id)).isNotNull();
|
||||
assertThat(
|
||||
SessionRepositoryFilterTests.this.sessionRepository.findById(id))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1098,8 +1097,9 @@ public class SessionRepositoryFilterTests {
|
||||
HttpServletResponse wrappedResponse) throws IOException {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.flushBuffer();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.findById(id)).isNotNull();
|
||||
assertThat(
|
||||
SessionRepositoryFilterTests.this.sessionRepository.findById(id))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1112,8 +1112,9 @@ public class SessionRepositoryFilterTests {
|
||||
HttpServletResponse wrappedResponse) throws IOException {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.getOutputStream().flush();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.findById(id)).isNotNull();
|
||||
assertThat(
|
||||
SessionRepositoryFilterTests.this.sessionRepository.findById(id))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1126,8 +1127,9 @@ public class SessionRepositoryFilterTests {
|
||||
HttpServletResponse wrappedResponse) throws IOException {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.getOutputStream().close();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.findById(id)).isNotNull();
|
||||
assertThat(
|
||||
SessionRepositoryFilterTests.this.sessionRepository.findById(id))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1140,8 +1142,9 @@ public class SessionRepositoryFilterTests {
|
||||
HttpServletResponse wrappedResponse) throws IOException {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.getWriter().flush();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.findById(id)).isNotNull();
|
||||
assertThat(
|
||||
SessionRepositoryFilterTests.this.sessionRepository.findById(id))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1154,20 +1157,28 @@ public class SessionRepositoryFilterTests {
|
||||
HttpServletResponse wrappedResponse) throws IOException {
|
||||
String id = wrappedRequest.getSession().getId();
|
||||
wrappedResponse.getWriter().close();
|
||||
assertThat(SessionRepositoryFilterTests.this.sessionRepository
|
||||
.findById(id)).isNotNull();
|
||||
assertThat(
|
||||
SessionRepositoryFilterTests.this.sessionRepository.findById(id))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- MultiHttpSessionStrategyAdapter
|
||||
// --- HttpSessionStrategy
|
||||
|
||||
@Test
|
||||
public void doFilterAdapterGetRequestedSessionId() throws Exception {
|
||||
SessionRepository<MapSession> sessionRepository = spy(
|
||||
new MapSessionRepository(new ConcurrentHashMap<>()));
|
||||
|
||||
this.filter = new SessionRepositoryFilter<>(sessionRepository);
|
||||
this.filter.setHttpSessionStrategy(this.strategy);
|
||||
final String expectedId = "MultiHttpSessionStrategyAdapter-requested-id";
|
||||
given(this.strategy.getRequestedSessionId(any(HttpServletRequest.class)))
|
||||
.willReturn(expectedId);
|
||||
final String expectedId = "HttpSessionStrategy-requested-id";
|
||||
|
||||
given(this.strategy.getRequestedSessionIds(any(HttpServletRequest.class)))
|
||||
.willReturn(Collections.singletonList(expectedId));
|
||||
given(sessionRepository.findById(anyString()))
|
||||
.willReturn(new MapSession(expectedId));
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
@@ -1211,8 +1222,8 @@ public class SessionRepositoryFilterTests {
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest) this.chain.getRequest();
|
||||
String id = request.getSession().getId();
|
||||
given(this.strategy.getRequestedSessionId(any(HttpServletRequest.class)))
|
||||
.willReturn(id);
|
||||
given(this.strategy.getRequestedSessionIds(any(HttpServletRequest.class)))
|
||||
.willReturn(Collections.singletonList(id));
|
||||
setupRequest();
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@@ -1243,8 +1254,8 @@ public class SessionRepositoryFilterTests {
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest) this.chain.getRequest();
|
||||
String id = request.getSession().getId();
|
||||
given(this.strategy.getRequestedSessionId(any(HttpServletRequest.class)))
|
||||
.willReturn(id);
|
||||
given(this.strategy.getRequestedSessionIds(any(HttpServletRequest.class)))
|
||||
.willReturn(Collections.singletonList(id));
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
@@ -1340,52 +1351,7 @@ public class SessionRepositoryFilterTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setHttpSessionStrategyNull() {
|
||||
this.filter.setHttpSessionStrategy((HttpSessionStrategy) null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setMultiHttpSessionStrategyNull() {
|
||||
this.filter.setHttpSessionStrategy((MultiHttpSessionStrategy) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionFalseWithInvalidSessionIdShouldOnlyAskRepositoryOnce()
|
||||
throws ServletException, IOException {
|
||||
this.sessionRepository = spy(this.sessionRepository);
|
||||
this.filter = new SessionRepositoryFilter<>(
|
||||
this.sessionRepository);
|
||||
|
||||
final String nonExistantSessionId = "nonExistantSessionId";
|
||||
setSessionCookie(nonExistantSessionId);
|
||||
|
||||
doFilter(new DoInFilter() {
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest) {
|
||||
// Before first invocation
|
||||
assertThat(SessionRepositoryFilterTests.this.request
|
||||
.getAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR))
|
||||
.isNull();
|
||||
|
||||
// First call should go all the way through to the sessioRepository (it
|
||||
// will not find the session)
|
||||
HttpSession session = wrappedRequest.getSession(false);
|
||||
verify(SessionRepositoryFilterTests.this.sessionRepository, times(1))
|
||||
.findById(nonExistantSessionId);
|
||||
assertThat(session).isNull();
|
||||
assertThat(SessionRepositoryFilterTests.this.request
|
||||
.getAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR))
|
||||
.isNotNull();
|
||||
|
||||
// Second call should not reach the sessionRepository
|
||||
session = wrappedRequest.getSession(false);
|
||||
verify(SessionRepositoryFilterTests.this.sessionRepository, times(1))
|
||||
.findById(nonExistantSessionId); // still only called once
|
||||
assertThat(session).isNull();
|
||||
assertThat(SessionRepositoryFilterTests.this.request
|
||||
.getAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR))
|
||||
.isNotNull();
|
||||
}
|
||||
});
|
||||
this.filter.setHttpSessionStrategy(null);
|
||||
}
|
||||
|
||||
// --- helper methods
|
||||
@@ -1435,10 +1401,8 @@ public class SessionRepositoryFilterTests {
|
||||
nameToCookie.put(cookie.getName(), cookie);
|
||||
}
|
||||
}
|
||||
if (this.response.getCookies() != null) {
|
||||
for (Cookie cookie : this.response.getCookies()) {
|
||||
nameToCookie.put(cookie.getName(), cookie);
|
||||
}
|
||||
for (Cookie cookie : this.response.getCookies()) {
|
||||
nameToCookie.put(cookie.getName(), cookie);
|
||||
}
|
||||
Cookie[] nextRequestCookies = new ArrayList<>(nameToCookie.values())
|
||||
.toArray(new Cookie[0]);
|
||||
@@ -1448,7 +1412,6 @@ public class SessionRepositoryFilterTests {
|
||||
this.request.setCookies(nextRequestCookies);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private void doFilter(final DoInFilter doInFilter)
|
||||
throws ServletException, IOException {
|
||||
this.chain = new MockFilterChain(new HttpServlet() {
|
||||
@@ -1456,7 +1419,7 @@ public class SessionRepositoryFilterTests {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
throws ServletException, IOException {
|
||||
doInFilter.doFilter(request, response);
|
||||
}
|
||||
});
|
||||
@@ -1471,21 +1434,25 @@ public class SessionRepositoryFilterTests {
|
||||
return new String(Base64.getDecoder().decode(value));
|
||||
}
|
||||
|
||||
abstract class DoInFilter {
|
||||
private static class SessionRepositoryFilterDefaultOrder implements Ordered {
|
||||
|
||||
public int getOrder() {
|
||||
return SessionRepositoryFilter.DEFAULT_ORDER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private abstract class DoInFilter {
|
||||
|
||||
void doFilter(HttpServletRequest wrappedRequest,
|
||||
HttpServletResponse wrappedResponse)
|
||||
throws ServletException, IOException {
|
||||
throws ServletException, IOException {
|
||||
doFilter(wrappedRequest);
|
||||
}
|
||||
|
||||
void doFilter(HttpServletRequest wrappedRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
static class SessionRepositoryFilterDefaultOrder implements Ordered {
|
||||
public int getOrder() {
|
||||
return SessionRepositoryFilter.DEFAULT_ORDER;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user