hooch
hooch

Reputation: 1175

Spring - How to add a session attribute only once per session

In my Spring Boot application I add the user name as a custom session attribute. I did this so in the Tomcat manager I can see who is currently using the app:

enter image description here

I implemented javax.servlet.Filter to add the user name. But is this the recommended way to do that? After all, the attribute gets added on every single request. Does Spring provide a callback which is executed only once per session?

Upvotes: 1

Views: 2185

Answers (3)

Brice Roncace
Brice Roncace

Reputation: 10650

I wondered this too.

What's the Spring-way to add session attributes (specifically related to Spring Security authentication)?

In my case, I registered a bean listening for Spring Security's InteractiveAuthenticationSuccessEvent and SessionDestroyedEvent events. These events fire without any explicit configuration in a default Spring Boot environment.

See https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#web.security:

The basic features you get by default in a web application are:

  • . . .
  • A DefaultAuthenticationEventPublisher for publishing authentication events.

This way I could add "username" as a session attribute immediately after a user logons and remove that attribute when the security session (security context) is destroyed:

@Component
public class SessionStoreUsernameAuthEventHandler {

  @EventListener
  public void audit(InteractiveAuthenticationSuccessEvent e) {
    getSession().ifPresent(s -> s.setAttribute("username", e.getAuthentication().getName()));
  }

  @EventListener
  public void audit(SessionDestroyedEvent e) {
    getSession().ifPresent(s -> s.removeAttribute("username"));
  }
  
  private static Optional<HttpServletRequest> getCurrentRequest() {
    return Optional.ofNullable(RequestContextHolder.getRequestAttributes())
      .filter(ServletRequestAttributes.class::isInstance)
      .map(ServletRequestAttributes.class::cast)
      .map(ServletRequestAttributes::getRequest);
  }

  private static Optional<HttpSession> getSession() {
    return getCurrentRequest().map(HttpServletRequest::getSession);
  }
}

Upvotes: 0

Ken Chan
Ken Chan

Reputation: 90447

Once you add an attribute to HttpSession , that attribute will keep existing in the session provided that the session does not expiry or you do not remove it from the session. So I don't see the point of implementing a Filter to add an attribute to the HttpSession for every request as this attribute is already exist in the session since you add it.

Besides , what you are doing is already done by Spring Security (p.s. in the SecurityContextPersistenceFilter) and I would not do it one more time by myself. Actually that session attribute SPRING_SECURITY_CONTEXT is the same object as we use SecurityContextHolder.getContext() to access the current login user information.

That means if the Authentication object that is set into the SecurityContextHolder has its toString() implemented to print the username (such as this), you could also see the username in the SPRING_SECURITY_CONTEXT session attribute in Tomcat Manager.

Upvotes: 1

Xander YzWich
Xander YzWich

Reputation: 141

Spring MVC has tools for dealing with sessions and the @SessionAttributes section of this tutorial actually discusses registering a bean with Session scope so that it is called for each new session.

more info here

Upvotes: 0

Related Questions