Skip to content

Instantly share code, notes, and snippets.

@BohdanLevchenko
Created December 28, 2019 12:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BohdanLevchenko/74d78b1634185402cf0453fe843bc31e to your computer and use it in GitHub Desktop.
Save BohdanLevchenko/74d78b1634185402cf0453fe843bc31e to your computer and use it in GitHub Desktop.
package io.tomcat.sessionfreeze;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
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.web.bind.annotation.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.security.Principal;
@SpringBootApplication
public class SessionfreezeApplication {
public static void main(String[] args) {
SpringApplication.run(SessionfreezeApplication.class, args);
}
@EnableWebSecurity
static class Security extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().and().csrf().disable().authorizeRequests().mvcMatchers("/**").authenticated();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
}
}
@RestController
@RequestMapping("/")
static class HomeController {
@GetMapping
public String index(Principal principal, HttpSession session) {
return "Hello " + principal.getName() + ". This is tracked resource.";
}
}
@RestController
@RequestMapping("/untracked")
static class UntrackedController {
@GetMapping
public String index(Principal principal) {
return "Hello " + principal.getName() + ". This is untracked resource";
}
}
@Bean
TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addContextValves(new TestValve());
return factory;
}
public static class TestValve extends ValveBase {
private final Log log = LogFactory.getLog(getClass());
private Session getSession(final Manager manager, String requestedSessionId) {
if (manager == null) {
return null;
}
if (requestedSessionId != null) {
Session session;
try {
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
return session;
}
}
return null;
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
final Manager manager = request.getContext().getManager();
final Session session = getSession(manager, request.getRequestedSessionId());
final boolean proxied = session instanceof WrappedSession;
final String target = request.getRequestURL().toString();
if (session != null) {
int timeIdle = (int) (session.getIdleTimeInternal() / 1000L);
log.debug("Session time idle: " + timeIdle);
}
final boolean normalUrl = !(target.contains("untracked") || target.contains("favicon"));
if (normalUrl && proxied) {
log.debug("Switching back to normal session for url: " + target);
final Session original = ((WrappedSession) session).unwrap();
original.access();
manager.remove(session);
manager.add(original);
getNext().invoke(request, response);
return;
}
if (session != null && !normalUrl) {
if (!(proxied)) {
final Session decorator = (Session) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[] {Session.class, WrappedSession.class}, (proxy, method, args) -> {
if (method.getName().equalsIgnoreCase("access")) {
log.debug("NOOP[access()]");
return null;
}
if (method.getName().equalsIgnoreCase("endAccess")) {
log.debug("NOOP[endAccess()]");
return null;
}
if (method.getName().equalsIgnoreCase("unwrap")) {
return session;
}
return method.invoke(session, args);
});
log.debug("Stop tracking last access time for url: " + target);
manager.remove(session);
manager.add(decorator);
}
}
getNext().invoke(request, response);
}
}
interface WrappedSession {
Session unwrap();
}
@Bean
EmbeddedServletContainerCustomizer customizer() {
return container -> container.setSessionTimeout(65);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment