Add support for multiple sessions in a browser
Fixes gh-49
This commit is contained in:
20
samples/users/build.gradle
Normal file
20
samples/users/build.gradle
Normal file
@@ -0,0 +1,20 @@
|
||||
apply from: JAVA_GRADLE
|
||||
apply from: TOMCAT_GRADLE
|
||||
|
||||
tasks.findByPath("artifactoryPublish")?.enabled = false
|
||||
sonarRunner {
|
||||
skipProject = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-session-data-redis'),
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"redis.embedded:embedded-redis:0.2",
|
||||
jstlDependencies
|
||||
|
||||
providedCompile "javax.servlet:javax.servlet-api:$servletApiVersion"
|
||||
|
||||
testCompile 'junit:junit:4.11'
|
||||
|
||||
integrationTestCompile gebDependencies
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package sample
|
||||
|
||||
import geb.spock.*
|
||||
import sample.pages.HomePage;
|
||||
import sample.pages.LinkPage;
|
||||
import spock.lang.Stepwise
|
||||
import pages.*
|
||||
|
||||
/**
|
||||
* Tests the demo that supports multiple sessions
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Stepwise
|
||||
class UserTests extends GebReportingSpec {
|
||||
def 'first visit not authenticated'() {
|
||||
when:
|
||||
to HomePage
|
||||
then:
|
||||
form
|
||||
!username
|
||||
}
|
||||
|
||||
def 'login single user'() {
|
||||
setup:
|
||||
def user = 'rob'
|
||||
when:
|
||||
login(user, user)
|
||||
then:
|
||||
username == user
|
||||
}
|
||||
|
||||
def 'add account'() {
|
||||
when:
|
||||
addAccount.click(HomePage)
|
||||
then:
|
||||
form
|
||||
!username
|
||||
}
|
||||
|
||||
def 'log in second user'() {
|
||||
setup:
|
||||
def user = 'luke'
|
||||
when:
|
||||
login(user, user)
|
||||
then:
|
||||
username == user
|
||||
}
|
||||
|
||||
def 'following links keeps new session'() {
|
||||
when:
|
||||
navLink.click(LinkPage)
|
||||
then:
|
||||
username == 'luke'
|
||||
}
|
||||
|
||||
def 'switch account rob'() {
|
||||
setup:
|
||||
def user = 'rob'
|
||||
when:
|
||||
switchAccount(user)
|
||||
then:
|
||||
username == user
|
||||
}
|
||||
|
||||
def 'following links keeps original session'() {
|
||||
when:
|
||||
navLink.click(LinkPage)
|
||||
then:
|
||||
username == 'rob'
|
||||
}
|
||||
|
||||
def 'switch account luke'() {
|
||||
setup:
|
||||
def user = 'luke'
|
||||
when:
|
||||
switchAccount(user)
|
||||
then:
|
||||
username == user
|
||||
}
|
||||
|
||||
def 'logout luke'() {
|
||||
when:
|
||||
logout.click(HomePage)
|
||||
then:
|
||||
!username
|
||||
}
|
||||
|
||||
def 'switch back rob'() {
|
||||
setup:
|
||||
def user = 'rob'
|
||||
when:
|
||||
switchAccount(user)
|
||||
then:
|
||||
username == user
|
||||
}
|
||||
|
||||
def 'logout rob'() {
|
||||
when:
|
||||
logout.click(HomePage)
|
||||
then:
|
||||
!username
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The home page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class HomePage extends Page {
|
||||
static url = ''
|
||||
static at = { assert driver.title == 'Demonstrates Multi User Log In'; true}
|
||||
static content = {
|
||||
navLink { $('#navLink') }
|
||||
form { $('form') }
|
||||
username(required:false) { $('#un').text() }
|
||||
logout(required:false) { $('#logout') }
|
||||
addAccount(required:false) { $('#addAccount') }
|
||||
submit { $('input[type=submit]') }
|
||||
login(required:false) { user, pass ->
|
||||
form.username = user
|
||||
form.password = pass
|
||||
submit.click(HomePage)
|
||||
}
|
||||
switchAccount{ un ->
|
||||
$("#switchAccount${un}").click(HomePage)
|
||||
}
|
||||
attributes { moduleList AttributeRow, $("table tr").tail() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package sample.pages
|
||||
|
||||
import geb.*
|
||||
|
||||
/**
|
||||
* The Links Page
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class LinkPage extends Page {
|
||||
static url = ''
|
||||
static at = { assert driver.title == 'Linked Page'; true}
|
||||
static content = {
|
||||
form { $('#navLinks') }
|
||||
username(required:false) { $('#un').text() }
|
||||
switchAccount{ un ->
|
||||
$("#switchAccount${un}").click(HomePage)
|
||||
}
|
||||
}
|
||||
}
|
||||
44
samples/users/src/main/java/sample/Account.java
Normal file
44
samples/users/src/main/java/sample/Account.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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 username;
|
||||
}
|
||||
|
||||
public String getLogoutUrl() {
|
||||
return logoutUrl;
|
||||
}
|
||||
|
||||
public String getSwitchAccountUrl() {
|
||||
return switchAccountUrl;
|
||||
}
|
||||
|
||||
}
|
||||
36
samples/users/src/main/java/sample/Config.java
Normal file
36
samples/users/src/main/java/sample/Config.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package sample;
|
||||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
*/
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Import(EmbeddedRedisConfiguration.class)
|
||||
@Configuration
|
||||
@EnableRedisHttpSession
|
||||
public class Config {
|
||||
|
||||
@Bean
|
||||
public JedisConnectionFactory connectionFactory() {
|
||||
return new JedisConnectionFactory();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package sample;
|
||||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
*/
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import redis.clients.jedis.Protocol;
|
||||
import redis.embedded.RedisServer;
|
||||
|
||||
/**
|
||||
* Runs an embedded Redis instance. This is only necessary since we do not want
|
||||
* users to have to setup a Redis instance. In a production environment, this
|
||||
* would not be used since a Redis Server would be setup.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
public class EmbeddedRedisConfiguration {
|
||||
|
||||
@Bean
|
||||
public RedisServerBean redisServer() {
|
||||
return new RedisServerBean();
|
||||
}
|
||||
|
||||
class RedisServerBean implements InitializingBean, DisposableBean {
|
||||
private RedisServer redisServer;
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
redisServer = new RedisServer(Protocol.DEFAULT_PORT);
|
||||
redisServer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
if(redisServer != null) {
|
||||
redisServer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
samples/users/src/main/java/sample/Initializer.java
Normal file
36
samples/users/src/main/java/sample/Initializer.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package sample;
|
||||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
*/
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
43
samples/users/src/main/java/sample/LoginServlet.java
Normal file
43
samples/users/src/main/java/sample/LoginServlet.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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 && username.equals(password)) {
|
||||
req.getSession().setAttribute("username", username);
|
||||
String url = resp.encodeRedirectURL(req.getContextPath() + "/");
|
||||
resp.sendRedirect(url);
|
||||
}
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -8157634860354132501L;
|
||||
}
|
||||
42
samples/users/src/main/java/sample/LogoutServlet.java
Normal file
42
samples/users/src/main/java/sample/LogoutServlet.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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;
|
||||
}
|
||||
95
samples/users/src/main/java/sample/UserAccountsFilter.java
Normal file
95
samples/users/src/main/java/sample/UserAccountsFilter.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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 {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
|
||||
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();
|
||||
|
||||
Session session = repo.getSession(sessionId);
|
||||
if(session == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String username = (String) 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);
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
}
|
||||
0
samples/users/src/main/webapp/META-INF/MANIFEST.MF
Normal file
0
samples/users/src/main/webapp/META-INF/MANIFEST.MF
Normal file
5
samples/users/src/main/webapp/assets/bootstrap.min.css
vendored
Normal file
5
samples/users/src/main/webapp/assets/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7
samples/users/src/main/webapp/assets/js/bootstrap.min.js
vendored
Normal file
7
samples/users/src/main/webapp/assets/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,22 @@
|
||||
/*!
|
||||
* 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)
|
||||
}
|
||||
})();
|
||||
4
samples/users/src/main/webapp/assets/js/jquery.min.js
vendored
Normal file
4
samples/users/src/main/webapp/assets/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
97
samples/users/src/main/webapp/index.jsp
Normal file
97
samples/users/src/main/webapp/index.jsp
Normal file
@@ -0,0 +1,97 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Demonstrates Multi User Log In</title>
|
||||
<link rel="stylesheet" href="assets/bootstrap.min.css">
|
||||
<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>
|
||||
<c:url value="/link.jsp" var="linkUrl"/>
|
||||
<li><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 href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><c:out value="${username}"/> <span class="caret"></span></a>
|
||||
<ul 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 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: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="text" 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 -->
|
||||
<script src="./assets/js/jquery.min.js"></script>
|
||||
<script src="./assets/js/bootstrap.min.js"></script>
|
||||
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
|
||||
<script src="./assets/js/ie10-viewport-bug-workaround.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
81
samples/users/src/main/webapp/link.jsp
Normal file
81
samples/users/src/main/webapp/link.jsp
Normal file
@@ -0,0 +1,81 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Linked Page</title>
|
||||
<link rel="stylesheet" href="assets/bootstrap.min.css">
|
||||
<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 href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><c:out value="${username}"/> <span class="caret"></span></a>
|
||||
<ul 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 -->
|
||||
<script src="./assets/js/jquery.min.js"></script>
|
||||
<script src="./assets/js/bootstrap.min.js"></script>
|
||||
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
|
||||
<script src="./assets/js/ie10-viewport-bug-workaround.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,4 +3,5 @@ rootProject.name = 'spring-session-build'
|
||||
include 'spring-session'
|
||||
include 'spring-session-data-redis'
|
||||
include 'samples:web'
|
||||
include 'samples:users'
|
||||
include 'samples:websocket'
|
||||
|
||||
@@ -15,28 +15,38 @@
|
||||
*/
|
||||
package org.springframework.session.web.http;
|
||||
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link HttpSessionStrategy} that uses a cookie to obtain the session from. Specifically, this implementation will
|
||||
* allow specifying a cookie name using {@link CookieHttpSessionStrategy#setCookieName(String)}. The default is "SESSION".
|
||||
* A {@link HttpSessionStrategy} that uses a cookie to obtain the session from.
|
||||
* Specifically, this implementation will allow specifying a cookie name using
|
||||
* {@link CookieHttpSessionStrategy#setCookieName(String)}. The default is
|
||||
* "SESSION".
|
||||
*
|
||||
* When a session is created, the HTTP response will have a cookie with the specified cookie name and the value of the
|
||||
* session id. The cookie will be marked as a session cookie, use the context path for the path of the cookie, marked as
|
||||
* HTTPOnly, and if {@link javax.servlet.http.HttpServletRequest#isSecure()} returns true, the cookie will be marked as
|
||||
* secure. For example:
|
||||
* When a session is created, the HTTP response will have a cookie with the
|
||||
* specified cookie name and the value of the session id. The cookie will be
|
||||
* marked as a session cookie, use the context path for the path of the cookie,
|
||||
* marked as HTTPOnly, and if
|
||||
* {@link javax.servlet.http.HttpServletRequest#isSecure()} returns true, the
|
||||
* cookie will be marked as secure. For example:
|
||||
*
|
||||
* <pre>
|
||||
* HTTP/1.1 200 OK
|
||||
* Set-Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6; Path=/context-root; Secure; HttpOnly
|
||||
* </pre>
|
||||
*
|
||||
* The client should now include the session in each request by specifying the same cookie in their request. For example:
|
||||
* The client should now include the session in each request by specifying the
|
||||
* same cookie in their request. For example:
|
||||
*
|
||||
* <pre>
|
||||
* GET /messages/ HTTP/1.1
|
||||
@@ -44,46 +54,194 @@ import javax.servlet.http.HttpServletResponse;
|
||||
* Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6
|
||||
* </pre>
|
||||
*
|
||||
* When the session is invalidated, the server will send an HTTP response that expires the cookie. For example:
|
||||
* When the session is invalidated, the server will send an HTTP response that
|
||||
* expires the cookie. For example:
|
||||
*
|
||||
* <pre>
|
||||
* HTTP/1.1 200 OK
|
||||
* 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/?u=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
|
||||
* u=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();
|
||||
*
|
||||
* Session session = repo.getSession(sessionId);
|
||||
* if(session == null) {
|
||||
* continue;
|
||||
* }
|
||||
*
|
||||
* String username = (String) 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);
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class CookieHttpSessionStrategy implements HttpSessionStrategy {
|
||||
public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy, HttpSessionManager {
|
||||
static final String DEFAULT_ALIAS = "0";
|
||||
|
||||
static final String DEFAULT_SESSION_ALIAS_PARAM_NAME = "u";
|
||||
|
||||
private String cookieName = "SESSION";
|
||||
|
||||
private String sessionParam = DEFAULT_SESSION_ALIAS_PARAM_NAME;
|
||||
|
||||
@Override
|
||||
public String getRequestedSessionId(HttpServletRequest request) {
|
||||
Cookie session = getCookie(request, cookieName);
|
||||
return session == null ? null : session.getValue();
|
||||
Map<String,String> sessionIds = getSessionIds(request);
|
||||
String sessionAlias = getCurrentSessionAlias(request);
|
||||
return sessionIds.get(sessionAlias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentSessionAlias(HttpServletRequest request) {
|
||||
if(sessionParam == null) {
|
||||
return DEFAULT_ALIAS;
|
||||
}
|
||||
String u = request.getParameter(sessionParam);
|
||||
if(u == null) {
|
||||
return DEFAULT_ALIAS;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
|
||||
Cookie cookie = new Cookie(cookieName, session.getId());
|
||||
cookie.setHttpOnly(true);
|
||||
cookie.setSecure(request.isSecure());
|
||||
cookie.setPath(cookiePath(request));
|
||||
response.addCookie(cookie);
|
||||
Map<String,String> sessionIds = getSessionIds(request);
|
||||
String sessionAlias = getCurrentSessionAlias(request);
|
||||
sessionIds.put(sessionAlias, session.getId());
|
||||
Cookie sessionCookie = createSessionCookie(request, sessionIds);
|
||||
response.addCookie(sessionCookie);
|
||||
}
|
||||
|
||||
// TODO set the domain?
|
||||
private Cookie createSessionCookie(HttpServletRequest request,
|
||||
Map<String, String> sessionIds) {
|
||||
Cookie sessionCookie = new Cookie(cookieName,"");
|
||||
sessionCookie.setHttpOnly(true);
|
||||
sessionCookie.setSecure(request.isSecure());
|
||||
sessionCookie.setPath(cookiePath(request));
|
||||
// TODO set domain?
|
||||
|
||||
if(sessionIds.isEmpty()) {
|
||||
sessionCookie.setMaxAge(0);
|
||||
return sessionCookie;
|
||||
}
|
||||
|
||||
if(sessionIds.size() == 1) {
|
||||
String cookieValue = sessionIds.values().iterator().next();
|
||||
sessionCookie.setValue(cookieValue);
|
||||
return sessionCookie;
|
||||
}
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for(Map.Entry<String,String> entry : sessionIds.entrySet()) {
|
||||
String alias = entry.getKey();
|
||||
String id = entry.getValue();
|
||||
|
||||
buffer.append(alias);
|
||||
buffer.append(" ");
|
||||
buffer.append(id);
|
||||
buffer.append(" ");
|
||||
}
|
||||
buffer.deleteCharAt(buffer.length()-1);
|
||||
|
||||
sessionCookie.setValue(buffer.toString());
|
||||
return sessionCookie;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
|
||||
Cookie sessionCookie = new Cookie(cookieName,"");
|
||||
sessionCookie.setMaxAge(0);
|
||||
sessionCookie.setHttpOnly(true);
|
||||
sessionCookie.setSecure(request.isSecure());
|
||||
sessionCookie.setPath(cookiePath(request));
|
||||
Map<String,String> sessionIds = getSessionIds(request);
|
||||
String requestedAlias = getCurrentSessionAlias(request);
|
||||
sessionIds.remove(requestedAlias);
|
||||
|
||||
Cookie sessionCookie = createSessionCookie(request, sessionIds);
|
||||
response.addCookie(sessionCookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the cookie to be used
|
||||
* @param cookieName
|
||||
@@ -116,4 +274,78 @@ public final class CookieHttpSessionStrategy implements HttpSessionStrategy {
|
||||
private static String cookiePath(HttpServletRequest request) {
|
||||
return request.getContextPath() + "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,String> getSessionIds(HttpServletRequest request) {
|
||||
Cookie session = getCookie(request, cookieName);
|
||||
String sessionCookieValue = session == null ? "" : session.getValue();
|
||||
Map<String,String> result = new LinkedHashMap<String,String>();
|
||||
StringTokenizer tokens = new StringTokenizer(sessionCookieValue, " ");
|
||||
if(tokens.countTokens() == 1) {
|
||||
result.put(DEFAULT_ALIAS, tokens.nextToken());
|
||||
return result;
|
||||
}
|
||||
while(tokens.hasMoreTokens()) {
|
||||
String alias = tokens.nextToken();
|
||||
String id = tokens.nextToken();
|
||||
result.put(alias, id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServletRequest wrapRequest(HttpServletRequest request, HttpServletResponse response) {
|
||||
request.setAttribute(HttpSessionManager.class.getName(), this);
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServletResponse wrapResponse(HttpServletRequest request, HttpServletResponse response) {
|
||||
return new MultiSessionHttpServletResponse(response, request);
|
||||
}
|
||||
|
||||
class MultiSessionHttpServletResponse extends HttpServletResponseWrapper {
|
||||
private final HttpServletRequest request;
|
||||
|
||||
public MultiSessionHttpServletResponse(HttpServletResponse response, HttpServletRequest request) {
|
||||
super(response);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeRedirectURL(String url) {
|
||||
url = super.encodeRedirectURL(url);
|
||||
return CookieHttpSessionStrategy.this.encodeURL(url, getCurrentSessionAlias(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeURL(String url) {
|
||||
url = super.encodeURL(url);
|
||||
|
||||
String alias = getCurrentSessionAlias(request);
|
||||
return CookieHttpSessionStrategy.this.encodeURL(url, alias);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeURL(String url, String sessionAlias) {
|
||||
int queryStart = url.indexOf("?");
|
||||
boolean isDefaultAlias = DEFAULT_ALIAS.equals(sessionAlias);
|
||||
if(queryStart < 0) {
|
||||
return isDefaultAlias ? url : url + "?" + sessionParam + "=" + sessionAlias;
|
||||
}
|
||||
String path = url.substring(0, queryStart);
|
||||
String query = url.substring(queryStart + 1, url.length());
|
||||
String replacement = isDefaultAlias ? "" : "$1"+sessionAlias;
|
||||
query = query.replaceFirst( "((^|&)" + sessionParam + "=)([^&]+)?", replacement);
|
||||
if(!isDefaultAlias && url.endsWith(query)) {
|
||||
// no existing alias
|
||||
if(!(query.endsWith("&") || query.length() == 0)) {
|
||||
query += "&";
|
||||
}
|
||||
query += sessionParam + "=" + sessionAlias;
|
||||
}
|
||||
|
||||
return path + "?" + query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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>
|
||||
*
|
||||
* @see CookieHttpSessionStrategy
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface MultiHttpSessionStrategy extends HttpSessionStrategy, RequestResponsePostProcessor {
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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);
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package org.springframework.session.web.http;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -53,13 +54,15 @@ import java.util.Set;
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerRequestFilter implements Ordered {
|
||||
public static final String SESSION_REPOSITORY_ATTR = SessionRepository.class.getName();
|
||||
|
||||
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 50;
|
||||
|
||||
private int order = DEFAULT_ORDER;
|
||||
|
||||
private final SessionRepository<S> sessionRepository;
|
||||
|
||||
private HttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();
|
||||
private MultiHttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
@@ -77,15 +80,31 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
|
||||
* @param httpSessionStrategy the {@link HttpSessionStrategy} to use. Cannot be null.
|
||||
*/
|
||||
public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
|
||||
Assert.notNull(httpSessionStrategy,"httpSessionIdStrategy 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) {
|
||||
Assert.notNull(httpSessionStrategy,"httpSessionIdStrategy cannot be null");
|
||||
this.httpSessionStrategy = httpSessionStrategy;
|
||||
}
|
||||
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
request.setAttribute(SESSION_REPOSITORY_ATTR, sessionRepository);
|
||||
|
||||
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
|
||||
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,response);
|
||||
|
||||
HttpServletRequest strategyRequest = httpSessionStrategy.wrapRequest(wrappedRequest, wrappedResponse);
|
||||
HttpServletResponse strategyResponse = httpSessionStrategy.wrapResponse(wrappedRequest, wrappedResponse);
|
||||
|
||||
try {
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
filterChain.doFilter(strategyRequest, strategyResponse);
|
||||
} finally {
|
||||
wrappedRequest.commitSession();
|
||||
}
|
||||
@@ -145,7 +164,9 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
|
||||
} else {
|
||||
S session = wrappedSession.session;
|
||||
sessionRepository.save(session);
|
||||
httpSessionStrategy.onNewSession(session, this, response);
|
||||
if(!requestedValidSession) {
|
||||
httpSessionStrategy.onNewSession(session, this, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,4 +367,38 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
static class MultiHttpSessionStrategyAdapter implements MultiHttpSessionStrategy {
|
||||
private HttpSessionStrategy delegate;
|
||||
|
||||
public MultiHttpSessionStrategyAdapter(HttpSessionStrategy delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public String getRequestedSessionId(HttpServletRequest request) {
|
||||
return delegate.getRequestedSessionId(request);
|
||||
}
|
||||
|
||||
public void onNewSession(Session session, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
delegate.onNewSession(session, request, response);
|
||||
}
|
||||
|
||||
public void onInvalidateSession(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
delegate.onInvalidateSession(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServletRequest wrapRequest(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpServletResponse wrapResponse(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,99 +13,238 @@ import org.springframework.session.web.http.CookieHttpSessionStrategy;
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
public class CookieHttpSessionStrategyTests {
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
private CookieHttpSessionStrategy strategy;
|
||||
private String cookieName;
|
||||
private Session session;
|
||||
private CookieHttpSessionStrategy strategy;
|
||||
private String cookieName;
|
||||
private Session session;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
cookieName = "SESSION";
|
||||
session = new MapSession();
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
strategy = new CookieHttpSessionStrategy();
|
||||
}
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
cookieName = "SESSION";
|
||||
session = new MapSession();
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
strategy = new CookieHttpSessionStrategy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNull() throws Exception {
|
||||
assertThat(strategy.getRequestedSessionId(request)).isNull();
|
||||
}
|
||||
@Test
|
||||
public void getRequestedSessionIdNull() throws Exception {
|
||||
assertThat(strategy.getRequestedSessionId(request)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNull() throws Exception {
|
||||
setSessionId(session.getId());
|
||||
assertThat(strategy.getRequestedSessionId(request)).isEqualTo(session.getId());
|
||||
}
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNull() throws Exception {
|
||||
setSessionId(session.getId());
|
||||
assertThat(strategy.getRequestedSessionId(request)).isEqualTo(session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNullCustomCookieName() throws Exception {
|
||||
setCookieName("CUSTOM");
|
||||
setSessionId(session.getId());
|
||||
assertThat(strategy.getRequestedSessionId(request)).isEqualTo(session.getId());
|
||||
}
|
||||
@Test
|
||||
public void getRequestedSessionIdNotNullCustomCookieName() throws Exception {
|
||||
setCookieName("CUSTOM");
|
||||
setSessionId(session.getId());
|
||||
assertThat(strategy.getRequestedSessionId(request)).isEqualTo(session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSession() throws Exception {
|
||||
strategy.onNewSession(session, request, response);
|
||||
assertThat(getSessionId()).isEqualTo(session.getId());
|
||||
}
|
||||
@Test
|
||||
public void onNewSession() throws Exception {
|
||||
strategy.onNewSession(session, request, response);
|
||||
assertThat(getSessionId()).isEqualTo(session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionCookiePath() throws Exception {
|
||||
request.setContextPath("/somethingunique");
|
||||
strategy.onNewSession(session, request, response);
|
||||
@Test
|
||||
public void onNewSessionExistingSessionSameAlias() throws Exception {
|
||||
Session existing = new MapSession();
|
||||
setSessionId(existing.getId());
|
||||
strategy.onNewSession(session, request, response);
|
||||
assertThat(getSessionId()).isEqualTo(session.getId());
|
||||
}
|
||||
|
||||
Cookie sessionCookie = response.getCookie(cookieName);
|
||||
assertThat(sessionCookie.getPath()).isEqualTo(request.getContextPath() + "/");
|
||||
}
|
||||
@Test
|
||||
public void onNewSessionExistingSessionNewAlias() throws Exception {
|
||||
Session existing = new MapSession();
|
||||
setSessionId(existing.getId());
|
||||
request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
|
||||
strategy.onNewSession(session, request, response);
|
||||
assertThat(getSessionId()).isEqualTo("0 " + existing.getId() + " new " + session.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onNewSessionCustomCookieName() throws Exception {
|
||||
setCookieName("CUSTOM");
|
||||
strategy.onNewSession(session, request, response);
|
||||
assertThat(getSessionId()).isEqualTo(session.getId());
|
||||
}
|
||||
@Test
|
||||
public void onNewSessionCookiePath() throws Exception {
|
||||
request.setContextPath("/somethingunique");
|
||||
strategy.onNewSession(session, request, response);
|
||||
|
||||
@Test
|
||||
public void onDeleteSession() throws Exception {
|
||||
strategy.onInvalidateSession(request, response);
|
||||
assertThat(getSessionId()).isEmpty();
|
||||
}
|
||||
Cookie sessionCookie = response.getCookie(cookieName);
|
||||
assertThat(sessionCookie.getPath()).isEqualTo(request.getContextPath() + "/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeleteSessionCookiePath() throws Exception {
|
||||
request.setContextPath("/somethingunique");
|
||||
strategy.onInvalidateSession(request, response);
|
||||
@Test
|
||||
public void onNewSessionCustomCookieName() throws Exception {
|
||||
setCookieName("CUSTOM");
|
||||
strategy.onNewSession(session, request, response);
|
||||
assertThat(getSessionId()).isEqualTo(session.getId());
|
||||
}
|
||||
|
||||
Cookie sessionCookie = response.getCookie(cookieName);
|
||||
assertThat(sessionCookie.getPath()).isEqualTo(request.getContextPath() + "/");
|
||||
}
|
||||
@Test
|
||||
public void onDeleteSession() throws Exception {
|
||||
strategy.onInvalidateSession(request, response);
|
||||
assertThat(getSessionId()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeleteSessionCustomCookieName() throws Exception {
|
||||
setCookieName("CUSTOM");
|
||||
strategy.onInvalidateSession(request, response);
|
||||
assertThat(getSessionId()).isEmpty();
|
||||
}
|
||||
@Test
|
||||
public void onDeleteSessionCookiePath() throws Exception {
|
||||
request.setContextPath("/somethingunique");
|
||||
strategy.onInvalidateSession(request, response);
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setCookieNameNull() throws Exception {
|
||||
strategy.setCookieName(null);
|
||||
}
|
||||
Cookie sessionCookie = response.getCookie(cookieName);
|
||||
assertThat(sessionCookie.getPath()).isEqualTo(request.getContextPath() + "/");
|
||||
}
|
||||
|
||||
public void setCookieName(String cookieName) {
|
||||
strategy.setCookieName(cookieName);
|
||||
this.cookieName = cookieName;
|
||||
}
|
||||
@Test
|
||||
public void onDeleteSessionCustomCookieName() throws Exception {
|
||||
setCookieName("CUSTOM");
|
||||
strategy.onInvalidateSession(request, response);
|
||||
assertThat(getSessionId()).isEmpty();
|
||||
}
|
||||
|
||||
public void setSessionId(String id) {
|
||||
request.setCookies(new Cookie(cookieName, id));
|
||||
}
|
||||
@Test
|
||||
public void onDeleteSessionExistingSessionSameAlias() throws Exception {
|
||||
Session existing = new MapSession();
|
||||
setSessionId("0 " + existing.getId() + " new " + session.getId());
|
||||
strategy.onInvalidateSession(request, response);
|
||||
assertThat(getSessionId()).isEqualTo(session.getId());
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return response.getCookie(cookieName).getValue();
|
||||
}
|
||||
@Test
|
||||
public void onDeleteSessionExistingSessionNewAlias() throws Exception {
|
||||
Session existing = new MapSession();
|
||||
setSessionId("0 " + existing.getId() + " new " + session.getId());
|
||||
request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
|
||||
strategy.onInvalidateSession(request, response);
|
||||
assertThat(getSessionId()).isEqualTo(existing.getId());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void setCookieNameNull() throws Exception {
|
||||
strategy.setCookieName(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLNoExistingQuery() {
|
||||
assertThat(strategy.encodeURL("/url", "2")).isEqualTo("/url?u=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLNoExistingQueryEmpty() {
|
||||
assertThat(strategy.encodeURL("/url?", "2")).isEqualTo("/url?u=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryNoAlias() {
|
||||
assertThat(strategy.encodeURL("/url?a=b", "2")).isEqualTo("/url?a=b&u=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasStart() {
|
||||
assertThat(strategy.encodeURL("/url?u=1&y=z", "2")).isEqualTo("/url?u=2&y=z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasMiddle() {
|
||||
assertThat(strategy.encodeURL("/url?a=b&u=1&y=z", "2")).isEqualTo("/url?a=b&u=2&y=z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasEnd() {
|
||||
assertThat(strategy.encodeURL("/url?a=b&u=1", "2")).isEqualTo("/url?a=b&u=2");
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryParamEndsWithActualParamStart() {
|
||||
assertThat(strategy.encodeURL("/url?xu=1&y=z", "2")).isEqualTo("/url?xu=1&y=z&u=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryParamEndsWithActualParamMiddle() {
|
||||
assertThat(strategy.encodeURL("/url?a=b&xu=1&y=z", "2")).isEqualTo("/url?a=b&xu=1&y=z&u=2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryParamEndsWithActualParamEnd() {
|
||||
assertThat(strategy.encodeURL("/url?a=b&xu=1", "2")).isEqualTo("/url?a=b&xu=1&u=2");
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@Test
|
||||
public void encodeURLNoExistingQueryDefaultAlias() {
|
||||
assertThat(strategy.encodeURL("/url", "0")).isEqualTo("/url");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLNoExistingQueryEmptyDefaultAlias() {
|
||||
assertThat(strategy.encodeURL("/url?", "0")).isEqualTo("/url?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryNoAliasDefaultAlias() {
|
||||
assertThat(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(strategy.encodeURL("/url?u=1&y=z", "0")).doesNotContain("u=0&u=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasMiddleDefaultAlias() {
|
||||
assertThat(strategy.encodeURL("/url?a=b&u=1&y=z", "0")).isEqualTo("/url?a=b&y=z");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURLExistingQueryExistingAliasEndDefaultAlias() {
|
||||
assertThat(strategy.encodeURL("/url?a=b&u=1", "0")).isEqualTo("/url?a=b");
|
||||
}
|
||||
|
||||
// --- getCurrentSessionAlias
|
||||
|
||||
@Test
|
||||
public void getCurrentSessionAliasNull() {
|
||||
assertThat(strategy.getCurrentSessionAlias(request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentSessionAliasNullParamName() {
|
||||
strategy.setSessionAliasParamName(null);
|
||||
request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "NOT USED");
|
||||
|
||||
assertThat(strategy.getCurrentSessionAlias(request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentSession() {
|
||||
String expectedAlias = "1";
|
||||
request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, expectedAlias);
|
||||
assertThat(strategy.getCurrentSessionAlias(request)).isEqualTo(expectedAlias);
|
||||
}
|
||||
|
||||
|
||||
// --- helper
|
||||
|
||||
public void setCookieName(String cookieName) {
|
||||
strategy.setCookieName(cookieName);
|
||||
this.cookieName = cookieName;
|
||||
}
|
||||
|
||||
public void setSessionId(String id) {
|
||||
request.setCookies(new Cookie(cookieName, id));
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return response.getCookie(cookieName).getValue();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package org.springframework.session.web.http;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
@@ -20,15 +22,23 @@ import javax.servlet.http.HttpSessionContext;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.session.ExpiringSession;
|
||||
import org.springframework.session.MapSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.session.SessionRepository;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class SessionRepositoryFilterTests<S extends ExpiringSession> {
|
||||
@Mock
|
||||
private HttpSessionStrategy strategy;
|
||||
|
||||
private SessionRepository<ExpiringSession> sessionRepository;
|
||||
|
||||
private SessionRepositoryFilter<ExpiringSession> filter;
|
||||
@@ -838,6 +848,72 @@ public class SessionRepositoryFilterTests<S extends ExpiringSession> {
|
||||
});
|
||||
}
|
||||
|
||||
// --- MultiHttpSessionStrategyAdapter
|
||||
|
||||
@Test
|
||||
public void doFilterAdapterGetRequestedSessionId() throws Exception {
|
||||
filter.setHttpSessionStrategy(strategy);
|
||||
final String expectedId = "MultiHttpSessionStrategyAdapter-requested-id";
|
||||
when(strategy.getRequestedSessionId(any(HttpServletRequest.class))).thenReturn(expectedId);
|
||||
|
||||
doFilter(new DoInFilter(){
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest, HttpServletResponse wrappedResponse) throws IOException {
|
||||
String actualId = wrappedRequest.getRequestedSessionId();
|
||||
assertThat(actualId).isEqualTo(expectedId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterAdapterOnNewSession() throws Exception {
|
||||
filter.setHttpSessionStrategy(strategy);
|
||||
|
||||
doFilter(new DoInFilter(){
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest, HttpServletResponse wrappedResponse) throws IOException {
|
||||
wrappedRequest.getSession();
|
||||
}
|
||||
});
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest) chain.getRequest();
|
||||
Session session = sessionRepository.getSession(request.getSession().getId());
|
||||
verify(strategy).onNewSession(eq(session), any(HttpServletRequest.class),any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterAdapterOnInvalidate() throws Exception {
|
||||
filter.setHttpSessionStrategy(strategy);
|
||||
|
||||
doFilter(new DoInFilter(){
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest, HttpServletResponse wrappedResponse) throws IOException {
|
||||
wrappedRequest.getSession().getId();
|
||||
}
|
||||
});
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest) chain.getRequest();
|
||||
String id = request.getSession().getId();
|
||||
when(strategy.getRequestedSessionId(any(HttpServletRequest.class))).thenReturn(id);
|
||||
|
||||
doFilter(new DoInFilter(){
|
||||
@Override
|
||||
public void doFilter(HttpServletRequest wrappedRequest, HttpServletResponse wrappedResponse) throws IOException {
|
||||
wrappedRequest.getSession().invalidate();
|
||||
}
|
||||
});
|
||||
|
||||
verify(strategy).onInvalidateSession(any(HttpServletRequest.class),any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
// --- order
|
||||
|
||||
public void order() {
|
||||
int expected = 5;
|
||||
filter.setOrder(expected);
|
||||
assertThat(filter.getOrder()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
// --- helper methods
|
||||
|
||||
private void assertNewSession() {
|
||||
|
||||
Reference in New Issue
Block a user