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.