Attempting to Align my Shiro Sessions with My Vaadin Sessions

I am attempting to align the lifecycle of my Shiro sessions with that of my Vaadin sessions, so as to:

  1. Overcome a reported Session Fixation issue with Shiro.

  2. Resolve an issue I’m seeing when I open multiple Vaadin sessions to my application. If one session is logged in, all subsequent new sessions behave as already authenticated. If one session logs out, all other sessions become logged out.

Here’s the repo of the project where I’m attempting to resolve these issues, and here’s the stack trace I’m getting when I attempt to run it:

2024-05-17 15:21:50.782  INFO 3680 --- [  restartedMain] c.v.f.s.f.s.FullDependenciesScanner      : Visited 123 classes. Took 24 ms.
Vaadin application has been deployed and started to the context path "/".
2024-05-17 15:21:50.856  WARN 3680 --- [  restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vaadinConfiguration': Invocation of init method failed; nested exception is java.lang.NullPointerException
2024-05-17 15:21:50.858  INFO 3680 --- [  restartedMain] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2024-05-17 15:21:50.876  INFO 3680 --- [  restartedMain] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2024-05-17 15:21:50.915 ERROR 3680 --- [  restartedMain] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vaadinConfiguration': Invocation of init method failed; nested exception is java.lang.NullPointerException
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:160) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:929) ~[spring-context-5.3.31.jar:5.3.31]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:591) ~[spring-context-5.3.31.jar:5.3.31]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.18.jar:2.7.18]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[spring-boot-2.7.18.jar:2.7.18]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409) ~[spring-boot-2.7.18.jar:2.7.18]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.18.jar:2.7.18]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) ~[spring-boot-2.7.18.jar:2.7.18]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289) ~[spring-boot-2.7.18.jar:2.7.18]
	at org.vaadin.example.Application.main(Application.java:29) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) ~[spring-boot-devtools-2.7.18.jar:2.7.18]
Caused by: java.lang.NullPointerException: null
	at org.vaadin.example.shiro.VaadinConfiguration.init(VaadinConfiguration.java:26) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333) ~[spring-beans-5.3.31.jar:5.3.31]
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157) ~[spring-beans-5.3.31.jar:5.3.31]
	... 23 common frames omitted

The offending line is line 28 of my VaadinConfiguration class, at which point my VaadinService object appears to be null. This is stopping me from testing my fix, and I’m not sure what I should change to make my VaadinService object non-null in my VaadinConfiguration’s init() method, so I am open to suggestions.

VaadinService.getCurrent() is available only in the context of a Vaadin request.
You can try to refactor your configuration class as a VaadinServiceInitListener bean, so you can get the VaadinService instance once it is ready

I don’t understand why you want to register your destroy listener. It’s a spring bean and is auto registered. If you don’t trust that mechanism; delete your VaadinConfiguration anyway and create the following class as bean

Edit; looks like Marco had the same link in mind :sweat_smile:

1 Like

BTW, storing plain text password in memory should be avoided. IIRC Shiro has built in authentication and hashing components that you could probably use by configuring the security framework

Hi Guys! After testing some more, I’m seeing my issue differently. Starting from this version of my test application, what I’m seeing is:

If I open a second or third tab in the same browser to the application after I’ve logged in from the first tab, the later tabs show up as already logged in as the same user. If I open a different browser to the application, it shows up as not logged in, which is what users of our production app see when they log in from different locations. If, after logging in from the second browser as a different user from the one I used in the first browser, if I open more tabs in the second browser, those tabs show up logged in as the second user. My test app shares this issue with the Apache Shiro Spring Boot Web demo app, so I know it’s not just me.

Our production app using PrimeFaces doesn’t behave this way. Instead, each tab within the same browser shows up as not logged in, which is pretty useful for local testing. Is there a simple Vaadin app modification I can do, so that when each browser tab opens to the app, the new tabs can all default to the non-logged-in state? Is that some kind of cache-clearing thing when the app opens?

Instead of storing the user authentication without the session (tab-wide) store it in another container like UI - even tho I would highly suggest not to go that route. Users nowadays expect that they can use your application in multiple tabs without re-login.

Thanks for this suggestion. I will give storing the authentication in the UI container a try, and I will present it as an option to my team. If you’re right about users currently expecting multiple tabs to be logged in, that explains why the Apache Shiro demo app also behaves that way.

I recalled that we had an internal project to test Flow and Shiro integration, but it was for a Java application without Spring.

It is new published on GitHub: GitHub - vaadin/skeleton-starter-flow at spikes/shiro-security

You may take a look and see if there is something that could be helpful for your project.

Thanks for this tip Marco. I’ll take a look at this and see if anything jumps out at me as a possible way to get our new app to behave like our current app does from one tab to the next within a browser. Meanwhile, upon discussion with my team, they seem inclined toward de-prioritizing this as an issue next to many other functional areas still remaining to be incorporated into our new app.