flow
flow copied to clipboard
Nullpointer exception in flow server
Description
In my logs I see a nullpointerexception after logging out (I think session expiration):
13 mei 2024 10:33:33,490 ERROR com.vaadin.flow.server.DefaultErrorHandler:105 -
java.lang.NullPointerException: Cannot invoke "com.vaadin.flow.server.VaadinSession.getState()" because the return value of "com.vaadin.flow.component.UI.getSession()" is null
at com.vaadin.flow.server.communication.MetadataWriter.createMetadata(MetadataWriter.java:69) ~[flow-server-24.3.2.jar:24.3.2]
at com.vaadin.flow.server.communication.UidlWriter.createUidl(UidlWriter.java:162) ~[flow-server-24.3.2.jar:24.3.2]
at com.vaadin.flow.server.communication.UidlRequestHandler.createUidl(UidlRequestHandler.java:155) ~[flow-server-24.3.2.jar:24.3.2]
at com.vaadin.flow.server.communication.UidlRequestHandler.writeUidl(UidlRequestHandler.java:145) ~[flow-server-24.3.2.jar:24.3.2]
at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:115) ~[flow-server-24.3.2.jar:24.3.2]
at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40) ~[flow-server-24.3.2.jar:24.3.2]
at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1573) ~[flow-server-24.3.2.jar:24.3.2]
at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:398) ~[flow-server-24.3.2.jar:24.3.2]
at com.vaadin.flow.spring.SpringServlet.service(SpringServlet.java:106) ~[vaadin-spring-24.3.2.jar:?]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614) ~[jakarta.servlet-api-6.0.0.jar!/:6.0.0]
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:257) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchToServlet(ServletInitialHandler.java:192) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.spec.RequestDispatcherImpl.forwardImpl(RequestDispatcherImpl.java:223) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.spec.RequestDispatcherImpl.forwardImplSetup(RequestDispatcherImpl.java:147) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.spec.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:110) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at org.springframework.web.servlet.mvc.ServletForwardingController.handleRequestInternal(ServletForwardingController.java:141) ~[spring-webmvc-6.1.5.jar:6.1.5]
at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:178) ~[spring-webmvc-6.1.5.jar:6.1.5]
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:51) ~[spring-webmvc-6.1.5.jar:6.1.5]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.5.jar:6.1.5]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.5.jar:6.1.5]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.5.jar:6.1.5]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.1.5.jar:6.1.5]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:547) ~[jakarta.servlet-api-6.0.0.jar!/:6.0.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.5.jar:6.1.5]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614) ~[jakarta.servlet-api-6.0.0.jar!/:6.0.0]
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.websockets.jsr.JsrWebSocketFilter.doFilter(JsrWebSocketFilter.java:172) ~[undertow-websockets-jsr-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.smallrye.opentracing.contrib.jaxrs2.server.SpanFinishingFilter.doFilter(SpanFinishingFilter.java:69) ~[smallrye-opentracing-contrib-3.0.0.jar!/:?]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:365) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:117) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) ~[spring-security-web-6.2.2.jar:6.2.2]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$3(HandlerMappingIntrospector.java:195) ~[spring-webmvc-6.1.5.jar:6.1.5]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:230) ~[spring-security-config-6.2.2.jar:6.2.2]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268) ~[spring-web-6.1.5.jar:6.1.5]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:124) ~[spring-boot-3.1.2.jar:3.1.2]
at org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:99) ~[spring-boot-3.1.2.jar:3.1.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:117) ~[spring-boot-3.1.2.jar:3.1.2]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68) ~[?:?]
at io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:275) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:134) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:131) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1435) ~[?:?]
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1435) ~[?:?]
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1435) ~[?:?]
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1435) ~[?:?]
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1435) ~[?:?]
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:255) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:100) ~[undertow-servlet-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:391) ~[undertow-core-2.3.0.Final.jar!/:2.3.0.Final]
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:859) ~[undertow-core-2.3.0.Final.jar!/:2.3.0.Final]
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) ~[?:?]
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990) ~[?:?]
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486) ~[?:?]
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377) ~[?:?]
at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282) ~[?:?]
at java.lang.Thread.run(Thread.java:1583) [?:?]
Expected outcome
I would like the null pointer be handled.
Minimal reproducible example
None
Steps to reproduce
None (sorry)
Environment
Vaadin version(s): 24.3.2 OS: Windows
Browsers
No response
Hi, thanks for reporting the issue.
Could you please provide more information about your application?
Are you using VaadinWebSecurity for Spring Security configuration?
How do you perform logout? Are you using Vaadin AuthenticationContext.logout() utility?
To handle logout I'm registering a SessionDestroyListener on the ServiceInitEvent in the serviceInit method of the VaadinServiceInitListener. In the destroylistener event handler I do some system logging to register the logout. That seems to be executed well.
Somewhere after that -- so after my destroylistener is called -- the nullpointer exception happens.
I'm using SpringFramework security (through @EnableWebSecurity), but not the AuthenticationContext utility. After login I use SecurityContextHolder.getContext().setAuthentication(authentication), however I never set that to null again.
Hope this helps...
So you are not using VaadinWebSecurity class? Could you share the code you use to trigger the logout and your destroy listener implementation and registration?
Also sharing your Spring security configuration may help to understand the problem
No, my SecurityConfig doesn't extend VaadinWebSecurity (yet).
Destroy listener implementation:
event.getSource().addSessionDestroyListener(sessionEvent -> {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); //
if (authentication != null) {
trafficSenseSessionService.doHandleSessionDestroyed(authentication);
// register logout
if (trafficSenseAuthorizationService!= null) {
trafficSenseAuthorizationService.logout(authentication);
}
}
});
where doHandleSessionDestroyed() writes a log message to a database and Service.logout() does nothing.
In this case I think the logout is triggered by expiration of the session, but if the user logs out it is triggered by
VaadinSession.getCurrent().getSession().invalidate();
UiSupport.getCurrentUI().getPage().reload();
@Configuration
@EnableWebSecurity
@Order(ConfigurationOrder.secondBatch)
public class SecurityConfiguration {
final private Logger log = LoggerFactory.getLogger(getClass());
// --- login
public static final String LOGIN_ROUTE = "login";
public static final String LOGIN_PROCESSING_URL = "/" + LOGIN_ROUTE;
public static final String LOGIN_FAILURE_URL = "/" + LOGIN_ROUTE + "?error";
// --- logout
public static final String LOGOUT_ROUTE = "logout";
public static final String LOGOUT_SUCCESS_URL = "/" + LOGOUT_ROUTE;
// --- run
public static final String MAIN_ROUTE = ""; // always empty
public static boolean enableSecurity = true; // secure is the default
private final PasswordEncoder passwordEncoder;
@Autowired
private TrafficViewerAuthenticationProvider authenticationProvider;
@Autowired
public SecurityConfiguration(
final PasswordEncoder passwordEncoder
) {
this.passwordEncoder = passwordEncoder;
}
/**
* Publish a SecurityFilterChain Bean.
*/
@Bean
public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {
if (SecurityConfiguration.enableSecurity) {
// disable caching
http.headers().cacheControl();
final AuthenticationSuccessHandler authenticationSuccessHandler = new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response,
final Authentication authentication) throws IOException, ServletException {
log.info("Succesfull login: " + authentication.getName());
final HttpSession session = request.getSession(false);
if (session != null) {
final User user = new User();
user.setUserName(authentication.getName()); //, activeUserStore);
session.setAttribute("user", user);
}
}
};
// Not using Spring built-in CSRF (Cross-Site Request Forgery) protection
// here to be able to use plain HTML for the login page
final HttpSecurity security = http.csrf().disable();
//final AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizeHttpRequestsConfigurer = http.authorizeHttpRequests();
final ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry reg =
http.authorizeRequests();
// Register our CustomRequestCache that saves unauthorized access attempts, so
// the user is redirected after login.
security.requestCache().requestCache(new CustomRequestCache())
// Restrict access to our application.
.and().authorizeRequests()
// Allow all flow internal requests.
.requestMatchers(SecurityUtils::isFrameworkInternalRequest).permitAll()
// Allow all requests by logged in users.
.anyRequest().authenticated()
// Configure the login page.
.and().formLogin().loginPage("/" + SecurityConfiguration.LOGIN_ROUTE).permitAll()
.loginProcessingUrl(SecurityConfiguration.LOGIN_PROCESSING_URL)
// failure
.failureUrl(SecurityConfiguration.LOGIN_FAILURE_URL)
// succes
.successHandler(authenticationSuccessHandler)
// Configure logout
.and().logout().logoutSuccessUrl(SecurityConfiguration.LOGOUT_SUCCESS_URL)
// exceptions
.and().exceptionHandling().accessDeniedPage("/page_403");
} else {
log.warn("Security has been disabled.");
http.authorizeRequests().anyRequest().permitAll();
}
return http.build();
}
@Autowired
public void configureGlobalSecurity(final AuthenticationManagerBuilder auth) throws Exception {
// nothing
}
private String encodedPassword(final String string) {
//return string;
return this.passwordEncoder.encode(string);
}
/**
* Register an application specific WebSecurityCustomizer.
*/
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> {
web.ignoring().requestMatchers(
// ... not copied in this example for security reasons
};
}
@Bean
AuthenticationManager authenticationManager(
final AuthenticationConfiguration authenticationConfiguration) throws Exception {
log.info("Setting AuthenticationManager.");
return authenticationConfiguration.getAuthenticationManager();
}
@Autowired
void registerProvider(final AuthenticationManagerBuilder auth) {
// set our own authentication provider
auth.authenticationProvider(authenticationProvider);
//auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
}
The error is weird because looking at the code, the VaadinSession is taken from UI and used, a couple of lines before the one the exception is thrown. And there is nothing between that is nullifying the session.
And the error happens during a UIDL request, so it seems not related to session expiration.
I wonder if there's something that is closing the UI without a proper VaadinSession lock.
A first suggestion is to update Vaadin to the latest version (24.3.12). Currently, I don't recall changes that can be related to this kind of issue, but the version you are using is a bit outdated.
Side note: based on the online documentation (see SecurityService.logout() in https://vaadin.com/docs/latest/security/enabling-security#log-out-capability and SecurityUtils.logout() in https://vaadin.com/docs/latest/security/advanced-topics/securing-plain-java-app#handling-user-log-in-out) I suggest moving the page reload code before the session invalidation.
We sometimes experience timeouts between client and server. Would it be possible that happened here?
Unfortunately, I can't say whether it could be related or not. Still, having the UI.session nullified while processing an UIDL request should not happen if changes to the UI are applied holding the VaadinSession lock.
And it is difficult to guess what's the cause, without being able to reproduce the issue.
Okay, thanks for the support! I'll upgrade and leave it with that.
I reported this also here https://github.com/vaadin/flow/issues/18796 . There was some investigation done on this, see the linked discord discussion.
this is still happening every now and then, suspecting this request handler has something to do with it ?
@Override
public boolean handleRequest(
VaadinSession session, VaadinRequest request, VaadinResponse response) throws IOException {
final Optional<String> preferredLanguage =
Arrays.stream(Optional.ofNullable(request.getCookies()).orElse(new Cookie[] {}))
.filter(cookie -> "lang.pref".equals(cookie.getName()))
.map(Cookie::getValue)
.findFirst();
preferredLanguage.ifPresent(
lang -> {
final Locale localeFromRequest = Locale.forLanguageTag(lang);
session.accessSynchronously(
() -> {
if (!Objects.equals(session.getLocale(), localeFromRequest)) {
session.setLocale(localeFromRequest);
}
});
});
return false; // Return false to let other request handlers do their job
}
}
Is it safe to access the session from a request handler ?
Request handler that needs to access the VaadinSession can extend SynchronizedRequestHandler that handles the session lock.
Thanks, using that one now but still getting the exception.
I'm not sure the request handler may be the cause of the exception. Looking at the stack trace, it could seem that the UI.session gets nullified by some pending task executed when building the UIDL response, potentially enqueued by session.access().
Maybe something invalidating the session or closing the UI?
Assumptions aside what could be causing it, should Vaadin internally guard against this ? It seems like a strongly assumed invariant is being violated or am i mistaken?
The result of it towards the browser is that you have to close the window or open a new private tab to get the application to load again.
Taking in mind that we need to understand the cause of the problem to provide a valuable fix, the question is: What should Flow do in this situation? The first that comes to mind would be to trigger a resynchronization, so that the browser page should be reloaded, but I'm not sure if this is a proper solution.