

Table of Contents
1. Overview
Application development always comes with new ideas. Here is an example of manage users multiple accounts in single browser same like google. Google provides to the facility to login multiple google account in the same browser where a user can switch from one account to another account after adding account.
For user authentication and authorization in java web application development spring security provides so many functionalities using that our application development will be easy and fast. So here is an example of spring security multiple users sessions or account in a single browser.
How multiple user session in single browser work?
While working with multiple user session in the same browser at time cookie will be created with name SESSION
.
- cookies values while create first session, here 0 is session identifier.
0 a7426ded-96e0-48c1-8e64-8b705f49076a
- While creating the second session in same browser at the value of that cookie as bellow: here 0 is first session cookies identifier, 1 is second session cookies identifier.
0 a7426ded-96e0-48c1-8e64-8b705f49076a 1 9b00d6fb-9efb-4b7a-8705-650caa2f966b
- Session identifier must be passed in each request like
_s=o
or_s=1
based on that spring security will identify which user request come and based on that we can manage our business logic.
2. Example
Technology Stack
- Spring boot
- Spring Session Management
- Spring Security
- Redis Server
- Tomcat 8
- Maven
- Java 8
NOTE: In this example we have used Redis to persist session information and our Redis server is running in
6379
port.

spring security multiple users sessions in single browser – Project Structure
2.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring-boot-example</groupId> <artifactId>spring-security-multiple-users-sessions-in-single-browser</artifactId> <version>1.0-SNAPSHOT</version> <description>spring security multiple users sessions in single browser</description> <!-- Inherit defaults from Spring Boot --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> </parent> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/com.lambdaworks/lettuce --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <!-- for tomcat web container--> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <!--starter require for spring boot spring security--> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>biz.paluch.redis</groupId> <artifactId>lettuce</artifactId> <version>3.5.0.Final</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <!--for jsp compilation need provide scope runtime or provided because it available in tomcat --> <artifactId>tomcat-embed-jasper</artifactId> <scope>runtime</scope> <!-- in my case provided not working so write runtime--> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>1.3.1.RELEASE</version> <type>pom</type> </dependency> <dependency> <groupId>biz.paluch.redis</groupId> <artifactId>lettuce</artifactId> <version>3.5.0.Final</version> </dependency> </dependencies> <!-- Package as an executable jar --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2.2 application.properties
spring.mvc.view.prefix: /WEB-INF/jsp/ spring.mvc.view.suffix: .jsp
2.3 SpringBootConfig
package com.javadeveloperzone; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; /** * Created by JavaDeveloperZone on 19-07-2017. */ @SpringBootApplication @ComponentScan // Using a root package also allows the @ComponentScan annotation to be used without needing to specify a basePackage attribute public class SpringBootConfig { public static void main(String[] args) throws Exception { SpringApplication.run(SpringBootConfig.class, args); // it wil start application } }
2.4 SecurityConfiguration
- Here successHandler has been overridden because to add _s parameter in landing page url.
httpServletRequest .getAttribute(HttpSessionManager.class.getName())
will returnHttpSessionManager.
HttpSessionManager
contains information session alias.
package com.javadeveloperzone; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.session.web.http.HttpSessionManager; /** * Created by Java Developer Zone on 15-11-2017. */ @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired // here is configuration related to spring boot basic authentication public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() // static users .withUser("zone1").password("mypassword").roles("USER") .and() .withUser("zone2").password("mypassword").roles("USER") .and() .withUser("zone3").password("mypassword").roles("USER") .and() .withUser("zone4").password("mypassword").roles("USER") .and() .withUser("zone5").password("mypassword").roles("USER"); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .successHandler((httpServletRequest, httpServletResponse, authentication) -> { // login success handler HttpSessionManager httpSessionManager = (HttpSessionManager) httpServletRequest .getAttribute(HttpSessionManager.class.getName()); String url = httpSessionManager .encodeURL("loginSuccess", httpSessionManager.getCurrentSessionAlias(httpServletRequest)); // on login success add session alias in url httpServletResponse.sendRedirect(url); }) .failureUrl("/loginFailed") // on login failed redirect to this user .loginPage("/login") // login page .permitAll(); } }
2.5 HttpSessionConfig
JedisConnectionFactory
bean has been created forRedis
configuration.Redis
used by spring session to persist session information usingRedis
- Here
EmbeddedServletContainerCustomizer
for cookies, because Tomcat 8.5 does not allow space in cookies name. Click here for more details
package com.javadeveloperzone; import org.apache.tomcat.util.http.LegacyCookieProcessor; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer; /** * Created by Java Developer Zone on 13-11-2017. */ @Configuration @EnableRedisHttpSession public class HttpSessionConfig extends AbstractHttpSessionApplicationInitializer { @Bean public JedisConnectionFactory connectionFactory() { return new JedisConnectionFactory(); // redis configuration } @Bean public EmbeddedServletContainerCustomizer customizer() { // allowed space in cookies name - https://javadeveloperzone.com/common-error/java-lang-illegalargumentexception-invalid-character-32-present-cookie-value/ return container -> { if (container instanceof TomcatEmbeddedServletContainerFactory) { TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container; tomcat.addContextCustomizers(context -> context.setCookieProcessor(new LegacyCookieProcessor())); } }; } }
2.6 SpringBootExampleController
login controller
: before landing to login page we have generated new sessionalias
so that session alias will be used when user login success identification.
package com.javadeveloperzone.controller; import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.core.userdetails.User; import org.springframework.session.Session; import org.springframework.session.SessionRepository; import org.springframework.session.web.http.HttpSessionManager; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import java.security.Principal; /** * Created by Java Developer Zone on 19-07-2017. */ @Controller public class SpringBootExampleController { @RequestMapping(value = "login") public String login(HttpServletRequest httpRequest,ModelMap map) { HttpSessionManager sessionManager = (HttpSessionManager) httpRequest.getAttribute(HttpSessionManager.class.getName()); String addAlias = sessionManager.getNewSessionAlias(httpRequest); // it will create new alis used when new user login map.put("s",addAlias); map.put("currentLoginUsers",getAssociatedUsers(httpRequest)); return "login"; } @RequestMapping(value = "loginSuccess") public String loginSuccess(HttpServletRequest request,Principal pricipal,ModelMap modelMap){ Integer integer =(Integer) request.getSession().getAttribute("hitCounter"); if(integer==null){ integer=new Integer(0); integer++; request.getSession().setAttribute("hitCounter",integer); }else{ integer++; request.getSession().setAttribute("hitCounter",integer); } HttpSessionManager httpSessionManager = (HttpSessionManager)request.getAttribute(HttpSessionManager.class.getName()); modelMap.put("currentLoginUsers",getAssociatedUsers(request)); modelMap.put("currentLoginUser",pricipal.getName()); modelMap.put("currentLoginUserKey",httpSessionManager.getCurrentSessionAlias(request) ); return "welcome"; } public java.util.Map<String,String> getAssociatedUsers(HttpServletRequest request){ // it will return all Associated accounts from request come java.util.Map<String,String> currentLoginUSer=new java.util.HashMap<>(); SessionRepository<Session> repo = (SessionRepository<Session>) request.getAttribute(SessionRepository.class.getName()); HttpSessionManager httpSessionManager = (HttpSessionManager)request.getAttribute(HttpSessionManager.class.getName()); java.util.Map<String,String> stringStringMap= httpSessionManager.getSessionIds(request); // it will return all session id of same browser from request comes java.util.Set<String> keys = stringStringMap.keySet(); for(String key:keys) { // logic to get user information from session id Session session = repo.getSession(stringStringMap.get(key)); if(session!=null) { SecurityContextImpl securityContext = ((SecurityContextImpl) session .getAttribute("SPRING_SECURITY_CONTEXT")); // get spring security context if(securityContext!=null) { User user = (User) securityContext.getAuthentication().getPrincipal(); // get user name from spring security context currentLoginUSer.put(key, user.getUsername()); } } } return currentLoginUSer; } @RequestMapping(value = "loginFailed") public String loginFailed(){ return "loginFailed"; } }
2.7 login.jsp
- action=’/login?_s=${s}’ each action must contains _s (
sessionalias
)to identify session, Thissessionalias
has been generated fromlogin controller
as mention inSpringBootExampleController
<%-- Created by IntelliJ IDEA. User: Java Developer Zone Date: 18-03-2017 Time: 07:34 To change this template use File | Settings | File Templates. --%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>spring security multiple users sessions in single browser</title> </head> <body> <form name='f' action='/login?_s=${s}' method='POST'> User: <input type='text' name='username' value=''> Password: <input type='password' name='password'/> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> <input name="submit" type="submit" value="Login"/> </form> <c:forEach items="${currentLoginUsers}" var="currentLoginUsers"> <ul> <li> Switch To : <a href="loginSuccess?_s=${currentLoginUsers.key}"><c:out value="${currentLoginUsers.value}"/></a></li> </ul> </c:forEach> </body> </html>
2.8 welcome.jsp
<%-- Created by IntelliJ IDEA. User: Java Developer Zone Date: 18-03-2017 Time: 07:34 To change this template use File | Settings | File Templates. --%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>spring security multiple users sessions in single browser</title> </head> <body> Hi, ${currentLoginUser} (<a href="/logout?_s=${currentLoginUserKey}">Logout</a> ) <br/> <br/> <b> ${currentLoginUser}'s Hit Counter : </b>${sessionScope.hitCounter} <hr> <c:forEach items="${currentLoginUsers}" var="currentLoginUsers"> <ul> <li> Switch To : <a href="loginSuccess?_s=${currentLoginUsers.key}"><c:out value="${currentLoginUsers.value}"/></a> </li> </ul> </c:forEach> <hr> <br> <a href="login?_s=1">Add Another Account</a>
2.9 loginFailed.jsp
<%-- Created by IntelliJ IDEA. User: Java Developer Zone Date: 18-03-2017 Time: 07:34 To change this template use File | Settings | File Templates. --%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>spring security multiple users sessions in single browser</title> </head> <body> Login Failed. </body> </html>
2.10 Demo:
Step 1 – Login 1st User (Add one account)

Step 1-Login 1st User – Add one account
Step 1.1 : After login
Here you can see, I have passed _s parameter in url, _s parameter is compulsory in each URL so that spring security can identify that from which session or account request comes based on that we can write business logic.

Step 1.1 – After Login
Step 2 – Add Another Account
Here screen for add another account, While first user will be login as it is. So two user can login in application at same time in same browser.

Step 2 – Add Another Account
Step 3 – List of Active Session in Current Browser
Here is more logic will be clear:
- Both users can login in the same browser.
- Based on _s parameter in URL we can identify user information.
- The user can switch account from one account to another account.

Step 3 – List of Active Session in Current Browser
Step 4 : Check Cookies in the browser
- How cookies will be store in browser while creating multiple session in same browser.

Cookies Information
3. References
4. Source Code