Hi everyone, i have a strange error message upon logout.
I cant find the issue due to no error log or any other hint gets thrown. Does anyone has a clue?
I am using the JWT login from the documentation but the crux is that i need two login views and i think there must be a miss config anywhere…
Here is my SecurityConfig
SecurityConfig.java
`EnableWebSecurity
@Configuration
@Order(4)
public class SecurityConfigFrontend extends VaadinWebSecurity {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
LoginAttemptService loginAttemptService;
@Autowired
AzureKeyVaultService azureKeyVaultService;
@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
@Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
private static final String[] PUBLIC_ENDPOINTS = {
"/images/**",
"/application/health/**",
"/swagger-ui/**",
"/v3/**",
"/css/**",
"/js/**",
"/font-awesome/**",
"/img/**",
"/fonts/**",
"/VAADIN/**",
"/frontend/**",
"/webjars/**",
"/error"
};
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
// Öffentliche statische Ressourcen
.requestMatchers(Arrays.stream(PUBLIC_ENDPOINTS)
.map(path -> AntPathRequestMatcher.antMatcher(HttpMethod.GET, path))
.toArray(AntPathRequestMatcher[]::new)).permitAll()
// Login-Seiten für beide Rollen
.requestMatchers("/", "/admin/login", "/customer/login", "/customer/logout", "/admin/logout").permitAll()
// Alle anderen Anfragen erfordern Authentifizierung
.anyRequest().authenticated()
)
.exceptionHandling(exception -> exception
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(customAuthenticationEntryPoint)
.accessDeniedPage("/access-denied")
)
// CORS und JWT-Filter
.cors(withDefaults())
.addFilterBefore(new JwtAuthenticationFilter(azureKeyVaultService), UsernamePasswordAuthenticationFilter.class);
// Custom Authentication Provider
http.authenticationProvider(customAuthenticationProvider());
http.csrf(csrf -> csrf.disable());
// JWT-Konfiguration für Vaadin
setStatelessAuthentication(
http,
new SecretKeySpec(
Base64.getDecoder().decode(azureKeyVaultService.getSecret(AzureKeyEnum.VAADIN_JWT_KEY)),
JwsAlgorithms.HS256
),
"blabla",
86400
);
}
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http, CustomAuthenticationProvider customAuthProvider) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.authenticationProvider(customAuthProvider)
.build();
}
@Bean
public CustomAuthenticationProvider customAuthenticationProvider() {
return new CustomAuthenticationProvider(userDetailsService, encoder(), loginAttemptService, azureKeyVaultService);
}
@Bean("authProvider")
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
@Bean(name = "encoder")
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}`
From the following view i have one for customer and one for admin (m ust be two seperated views) This View extends BaseLoginView which is the implementation of LoginView Details and adds the loginform
LoginView
`@Slf4j
@RoutePrefix(Customer)
@Route(“login”)
@PageTitle(“Customer Login”)
@AnonymousAllowed
@PermitAll
public class CustomerLoginView extends BaseLoginView {
public CustomerLoginView(LoginAttemptService loginAttemptService, CaptchaService captchaService, AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder) {
super(loginAttemptService, captchaService, authenticationManager, passwordEncoder);
}
@Override
protected void onSuccessfulLogin(String username, Authentication authentication) {
SecurityContextHolder.getContext().setAuthentication(authentication);
Object principal = authentication.getPrincipal();
if (principal instanceof User user) {
if (user.getUserType() == UserType.Customer) {
loginAttemptService.loginSucceeded(username);
UI.getCurrent().navigate("/Customer/dashboard");
return;
}
loginAttemptService.loginFailed(username);
login.setEnabled(true);
NotificationUtil.error("Login fehlgeschlagen. Sie haben keine Berechtigung für das Customer-Portal.");
}
}
@Override
protected String getPortalTitle() {
return "Customer Portal";
}
@Override
protected String getBackgroundImagePath() {
return "/images/Customerportal/login-image.jpg";
}
}
`
Logout Method
` public void logout() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof JwtAuthenticationToken token)) {
// Kein gültiger Token vorhanden – einfach zur Startseite weiterleiten
UI.getCurrent().getPage().setLocation("/");
return;
}
Collection<? extends GrantedAuthority> authorities = token.getAuthorities();
boolean isSupplier = authorities.stream()
.anyMatch(authz -> "ROLE_CUSTOMER".equals(authz.getAuthority()));
boolean isInsurer = authorities.stream()
.anyMatch(authz -> "ROLE_ADMIN".equals(authz.getAuthority()));
// Session schließen, wenn vorhanden
VaadinSession session = VaadinSession.getCurrent();
if (session != null) {
session.getSession().invalidate();
session.close();
}
// Security-Kontext leeren
SecurityContextHolder.clearContext();
if (authenticationContext != null) {
authenticationContext.logout();
}
// Optional: Logging
System.out.println("User " + token.getName() + " logged out.");
// Weiterleitung je nach Rolle
String redirectUrl = "/login";
if (isSupplier) {
redirectUrl = "customer/logout";
} else if (isInsurer) {
redirectUrl = "admin/logout";
}
UI.getCurrent().getPage().setLocation(redirectUrl);
}`