I have a Spring Boot app v2.3.5.RELEASE and Spring Security v5.3.5.RELEASE with the configured security below :
security-config.xml
<beans:beans
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
<beans:bean id="sessionRegistry" name="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
<security:http auto-config='true' use-expressions="true">
<security:intercept-url pattern="/login"
access="permitAll" />
<security:intercept-url pattern="/**"
access="isFullyAuthenticated()" />
<security:form-login username-parameter="username"
password-parameter="password" />
<security:logout logout-url="/logout"
invalidate-session="true" />
<security:session-management>
<security:concurrency-control
max-sessions="1" error-if-maximum-exceeded="true"
session-registry-ref="sessionRegistry" />
</security:session-management>
</security:http>
<security:user-service>
<security:user name="mazen" password="{noop}mazen"
authorities="ROLE_User" />
</security:user-service>
</beans:beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
<!-- - Location of the XML file that defines the root application context
- Applied by ContextLoaderListener. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/*.xml
</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- - Loads the root application context of this web app at startup. -
The application context is then available via - WebApplicationContextUtils.getWebApplicationContext(servletContext). -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
So it is configured so that a user can have only 1 active session. If i try to login with a user and then close browser and retry logging in it says Maximum sessions of 1 for this principal exceeded, then the session is not expired after closing the browser. Session is expired only after logout. Is this a bug or i just need to implement this behaviour by myself? Thanks
Comment From: eleftherias
This is the expected behaviour. The server-side Spring application, which is responsible for invalidating the session, is not aware of when the browser is closed.
Have you considered setting error-if-maximum-exceeded="false"?
This strategy will expire any sessions that exceed the maximum number of permitted sessions, starting with the least recently used sessions.
In the scenario when a user logs in, closes the browser and then retries to login, they will be successfully logged in and their old session will be invalidated.