Right now I'm preparing my Vaadin 23.2.5 application for production and very often after redeploy the application unable to fully reload itself and hangs with fat blue progress bar at the top.
The only issue I may see, is the following JS issue in the browser console:
FireFox
Chrome
Also, based on my previous questions on this subject - I completely removed the Grid component from the application, but as you may see - the issue still exists. How to solve this issue?
UPDATED
My configs:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends VaadinWebSecurityConfigurerAdapter {
private final ClientRegistrationRepository clientRegistrationRepository;
private final GrantedAuthoritiesMapper authoritiesMapper;
private final ProfileService profileService;
SecurityConfiguration(ClientRegistrationRepository clientRegistrationRepository,
GrantedAuthoritiesMapper authoritiesMapper, ProfileService profileService) {
this.clientRegistrationRepository = clientRegistrationRepository;
this.authoritiesMapper = authoritiesMapper;
this.profileService = profileService;
SecurityContextHolder.setStrategyName(VaadinAwareSecurityContextHolderStrategy.class.getName());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
// Enable OAuth2 login
.oauth2Login(oauth2Login ->
oauth2Login
.clientRegistrationRepository(clientRegistrationRepository)
.userInfoEndpoint(userInfoEndpoint ->
userInfoEndpoint
// Use a custom authorities mapper to get the roles from the identity provider into the Authentication token
.userAuthoritiesMapper(authoritiesMapper)
)
// Use a Vaadin aware authentication success handler
.successHandler(new KeycloakVaadinAuthenticationSuccessHandler(profileService))
)
// Configure logout
.logout(logout ->
logout
// Enable OIDC logout (requires that we use the 'openid' scope when authenticating)
.logoutSuccessHandler(logoutSuccessHandler())
// When CSRF is enabled, the logout URL normally requires a POST request with the CSRF
// token attached. This makes it difficult to perform a logout from within a Vaadin
// application (since Vaadin uses its own CSRF tokens). By changing the logout endpoint
// to accept GET requests, we can redirect to the logout URL from within Vaadin.
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
);
}
@Bean
@Primary
public SpringViewAccessChecker springViewAccessChecker(AccessAnnotationChecker accessAnnotationChecker) {
return new KeycloakSpringViewAccessChecker(accessAnnotationChecker, "/oauth2/authorization/keycloak");
}
private OidcClientInitiatedLogoutSuccessHandler logoutSuccessHandler() {
var logoutSuccessHandler = new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);
logoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}");
return logoutSuccessHandler;
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
// Don't apply security rules on our static pages
web.ignoring().antMatchers("/session-expired", "/images/*");
}
@Bean
public PolicyFactory htmlSanitizer() {
// This is the policy we will be using to sanitize HTML input
return Sanitizers.FORMATTING.and(Sanitizers.BLOCKS).and(Sanitizers.STYLES).and(Sanitizers.LINKS);
}
}
@Component
class VaadinSessionConfiguration implements VaadinServiceInitListener, SystemMessagesProvider, SessionDestroyListener {
private final String relativeSessionExpiredUrl;
VaadinSessionConfiguration(ServerProperties serverProperties) {
relativeSessionExpiredUrl = UriComponentsBuilder.fromPath(serverProperties.getServlet().getContextPath()).path("logout").build().toUriString();
}
@Override
public SystemMessages getSystemMessages(SystemMessagesInfo systemMessagesInfo) {
var messages = new CustomizedSystemMessages();
// Redirect to a specific screen when the session expires. In this particular case we don't want to logout
// just yet. If you would like the user to be completely logged out when the session expires, this URL
// should the logout URL.
messages.setSessionExpiredURL(relativeSessionExpiredUrl);
return messages;
}
@Override
public void sessionDestroy(SessionDestroyEvent event) {
// We also want to destroy the underlying HTTP session since it is the one that contains the authentication
// token.
try {
event.getSession().getSession().invalidate();
} catch (Exception ignore) {
// Session was probably already invalidated.
}
}
@Override
public void serviceInit(ServiceInitEvent event) {
event.getSource().setSystemMessagesProvider(this);
event.getSource().addSessionDestroyListener(this);
}
}
public final class VaadinAwareSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
@Override
public void clearContext() {
contextHolder.remove();
}
@Override
@NonNull
public SecurityContext getContext() {
var context = contextHolder.get();
if (context == null) {
context = getFromVaadinSession().orElseGet(() -> {
var newCtx = createEmptyContext();
// This copies the behaviour of ThreadLocalSecurityContextHolder.
contextHolder.set(newCtx);
return newCtx;
});
}
return context;
}
@NonNull
private Optional<SecurityContext> getFromVaadinSession() {
// Don't store this security context in the ThreadLocal as that may lead to the context leaking
// into other sessions as threads may be reused.
var session = VaadinSession.getCurrent();
if (session == null) {
return Optional.empty();
}
var securityContext = session.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
if (securityContext instanceof SecurityContext) {
return Optional.of((SecurityContext) securityContext);
} else {
return Optional.empty();
}
}
@Override
public void setContext(@NonNull SecurityContext securityContext) {
contextHolder.set(requireNonNull(securityContext));
}
@Override
@NonNull
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}
UPDATED 1