6

I have this sample application:

package com.example.session;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class DemoRedisDataSessionApplication {

    @Configuration
    @EnableWebSecurity
    @EnableRedisHttpSession(redisNamespace = "demo-redis-data-session")
    public static class AppConfiguration extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().withUser("user").password("0000").roles("USER");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin().and()
                    .authorizeRequests().antMatchers("/ping").permitAll().and()
                    .authorizeRequests().anyRequest().fullyAuthenticated();
        }

    }

    @RestController
    public static class AppController {
        @GetMapping("/ping")
        public String ping() {
            return "pong";
        }

        @GetMapping("/secured")
        public String secured() {
            return "secured";
        }
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoRedisDataSessionApplication.class, args);
    }

}

When I hit /secured I get 302 redirected to the /login form, which is what I expect if I am not logged in, but I get some unwanted entries in Redis:

127.0.0.1:6379> keys * 1) "spring:session:demo-redis-data-session:sessions:expires:dbb124b9-c37d-454c-8d67-409f28cb88a6" 2) "spring:session:demo-redis-data-session:expirations:1515426060000" 3) "spring:session:demo-redis-data-session:sessions:dbb124b9-c37d-454c-8d67-409f28cb88a6"

I don't want to create this data for every anonymous user (read crawler), so is there a way to prevent these Redis entries when hitting a secured endpoint/page with an anonymous user?

Additional data used for this sample project

 docker-compose.yml

version: "2" services: redis: image: redis ports: - "6379:6379"

Spring Boot version

1.5.9.RELEASE

Vedran Pavic
  • 2,339
  • 2
  • 27
  • 31
Fabio Maffioletti
  • 820
  • 1
  • 8
  • 17

1 Answers1

0

This is not the optimal solution since it creates only one session for all crawlers, but at least I don't get Redis full of unwanted session.

import lombok.extern.log4j.Log4j;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.web.http.CookieHttpSessionStrategy;
import org.springframework.session.web.http.MultiHttpSessionStrategy;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Log4j
@Component
public class CrawlerManagerSessionStrategyWrapper implements MultiHttpSessionStrategy {

    private CookieHttpSessionStrategy delegate;
    private volatile String crawlerSessionId;

    public CrawlerManagerSessionStrategyWrapper() {
        this.delegate = new CookieHttpSessionStrategy();
    }

    public String getRequestedSessionId(HttpServletRequest request) {
        String sessionId = getSessionIdForCrawler(request);
        if (sessionId != null)
            return sessionId;
        else {
            return delegate.getRequestedSessionId(request);
        }
    }

    public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
        delegate.onNewSession(session, request, response);
        if (isCrawler(request)) {
            crawlerSessionId = session.getId();
        }
    }

    public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
        delegate.onInvalidateSession(request, response);
    }

    public HttpServletRequest wrapRequest(HttpServletRequest request, HttpServletResponse response) {
        return request;
    }

    public HttpServletResponse wrapResponse(HttpServletRequest request, HttpServletResponse response) {
        return response;
    }

    private String getSessionIdForCrawler(HttpServletRequest request) {
        if (isCrawler(request)) {
            SessionRepository<Session> repo = (SessionRepository<Session>) request.getAttribute(SessionRepository.class.getName());
            if (crawlerSessionId != null && repo != null) {
                Session session = repo.getSession(crawlerSessionId);
                if (session != null) {
                    return crawlerSessionId;
                }
            }
        }
        return null;
    }

    private boolean isCrawler(HttpServletRequest request) {
        // Here goes the logic to understand if the request comes from a crawler, for example by checking the user agent.
        return true;
    }

}

The only thing to implement is the isCrawler method to state if the request comes from a crawler.

Fabio Maffioletti
  • 820
  • 1
  • 8
  • 17