javaspringspring-bootspring-mvcspring-security

How could we manage separate sessions in Spring Security via @PathVariable?


Imagine we have a customized way of authentication for the below URLs.

Path1: http://www.example.com/company1/home
Path2: http://www.example.com/company2/home
Path3: http://www.example.com/company3/home
.
.
.
PathX: http://www.example.com/companyX/home

And companies have different sets of data.

Now, I want spring security to create separate sessions for each of the above urls based on http://www.example.com/{company}/. So, when I login into different URLs in separate browser tabs, I can work with all of them. I want to keep all sessions alive and avoid session termination after the authentication with the other URLs.

How to achieve this?


Solution

  • You can achieve this behavior by overriding the behavior of DefaultCookieSerializer#getCookiePath method, and set your own cookiePath strategy

    Duplicate the DefaultCookieSerializer class and name it as MyCookieSerializer. (You can't override getCookiePath method in your child class because that method is private)

    public class MyCookieSerializer implements CookieSerializer {
        //..codes above
        private String getCookiePath(HttpServletRequest request) {
            if (this.cookiePath == null) {
                //SecurityUtils#getSiteName: this method just extract the company name from the path. You may have your own strategy
                String siteName = SecurityUtils.getSiteName(request.getServletPath());
                return siteName != null ? "/" + siteName + "/" : "/";
            }
            return this.cookiePath;
        }
        //..codes below
    }
    

    Create a bean of your MyCookieSerializer

    @EnableWebSecurity(debug = true)
    @Configuration
    @ComponentScan(basePackages = {
            "com.something"
    })
    @PropertySource( { "classpath:application.properties" } )
    public class WebSecurityConfig {
    
        @Value("${cookie.max.age}")
        private int cookieMaxAge;
    
    
        @Bean
        public CookieSerializer cookieSerializer() {
            MyCookieSerializer marlin = new MyCookieSerializer();
            marlin.setCookieMaxAge(cookieMaxAge);
            return marlin;
        }
    
    
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
            return http
                .sessionManagement(httpSecuritySessionManagementConfigurer -> {
                    httpSecuritySessionManagementConfigurer.sessionFixation().none();
                    httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
                    httpSecuritySessionManagementConfigurer.maximumSessions(-1);
    
                })
                    .authorizeHttpRequests(auth -> auth
                            .requestMatchers(
                                    antMatcher("/"),
                                    antMatcher("/css/**"),
                                    antMatcher("/fonts/**"),
                                    antMatcher("/js/**"),
                                    antMatcher("/images/**"),
                                    antMatcher("/*/")
                            ).permitAll()
                            .requestMatchers(antMatcher("/{id}/**")).authenticated())
                             //Your Authentication method codes (e.g formLogin, basic, etc)
                            .build();
    

    That's it.

    I explain here how this works:

    1. when the first user request http://www.example.com/company1/home on the first tab, a cookie with path /company1/ will be created and put in response by MyCookieSerializer. This is used by browser in subsequent requests coming from company1.
    2. When we open another tab and put http://www.example.com/company2/home, since the previous cookie path is set to /company1/, the session associated with that cookie won't send to the server during the request process of company2. Hence, when the server receive the request without session, it creates another session for company2 and put that in a cookie (again by CookieSerializer) during the response. and Company2 can also use that cookie for its subsequent requests.

    This works in all scenarios. For example if we logout company1 which is on tab1, company2 will still works and its session will remain active.

    I hope I explain the process properly for you.