Comprehensive Java web application development framework that enables server-side Java development with modern web UI components and automatic client-server communication.
Vaadin provides comprehensive security features including authentication, authorization, CSRF protection, and integration with popular security frameworks like Spring Security. The platform includes both declarative and programmatic security approaches.
// Security annotations
import com.vaadin.flow.server.auth.AnonymousAllowed;
import com.vaadin.flow.server.auth.PermitAll;
import com.vaadin.flow.server.auth.DenyAll;
import com.vaadin.flow.server.auth.RolesAllowed;
// Login components
import com.vaadin.flow.component.login.LoginForm;
import com.vaadin.flow.component.login.LoginOverlay;
import com.vaadin.flow.component.login.AbstractLogin.LoginEvent;
import com.vaadin.flow.component.login.AbstractLogin.ForgotPasswordEvent;
import com.vaadin.flow.component.login.LoginI18n;
// Navigation guards and observers
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeLeaveObserver;
import com.vaadin.flow.router.BeforeLeaveEvent;
// Session and security context
import com.vaadin.flow.server.VaadinSession;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.VaadinRequest;
import com.vaadin.flow.component.UI;
// Component events
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.shared.Registration;
// Spring Security integration
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
// Spring Security configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
// Method-level security
import org.springframework.security.access.prepost.PreAuthorize;
// Spring framework
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
// Servlet configuration
import com.vaadin.flow.server.VaadinServlet;
import com.vaadin.flow.server.VaadinServletConfiguration;
import javax.servlet.annotation.WebServlet;
import javax.servlet.ServletException;
// Standard Java types
import java.security.Principal;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
// Logging
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// Core components
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.grid.Grid;
// Route annotations
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.PageTitle;Built-in annotations for declarative access control.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonymousAllowed {
// Allows access to anonymous (unauthenticated) users
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermitAll {
// Allows access to all authenticated users
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DenyAll {
// Denies access to all users
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RolesAllowed {
String[] value(); // Specifies allowed roles
}Access current authentication information.
// Spring Security integration
public class SecurityService {
public UserDetails getAuthenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
return (UserDetails) principal;
}
}
return null;
}
public boolean isAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null &&
authentication.isAuthenticated() &&
!(authentication instanceof AnonymousAuthenticationToken);
}
public boolean hasRole(String role) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
return authentication.getAuthorities().stream()
.anyMatch(authority -> authority.getAuthority().equals("ROLE_" + role));
}
return false;
}
public void logout() {
SecurityContextHolder.clearContext();
VaadinSession.getCurrent().getSession().invalidate();
UI.getCurrent().navigate("");
}
}Built-in login form components with customization options.
public class LoginForm extends Component {
public LoginForm();
// Event handling
public Registration addLoginListener(ComponentEventListener<LoginEvent> listener);
public Registration addForgotPasswordListener(ComponentEventListener<ForgotPasswordEvent> listener);
// Configuration
public void setError(boolean error);
public boolean isError();
public void setEnabled(boolean enabled);
public boolean isEnabled();
// Internationalization
public void setI18n(LoginI18n i18n);
public LoginI18n getI18n();
// Actions
public void setAction(String action);
public String getAction();
public void setForgotPasswordButtonVisible(boolean forgotPasswordButtonVisible);
public boolean isForgotPasswordButtonVisible();
}
public class LoginOverlay extends LoginForm {
public LoginOverlay();
// Overlay control
public void setOpened(boolean opened);
public boolean isOpened();
// Title and description
public void setTitle(String title);
public String getTitle();
public void setDescription(String description);
public String getDescription();
}
// Login events
public class LoginEvent extends ComponentEvent<LoginForm> {
public LoginEvent(LoginForm source, boolean fromClient, String username, String password);
public String getUsername();
public String getPassword();
}
public class ForgotPasswordEvent extends ComponentEvent<LoginForm> {
public ForgotPasswordEvent(LoginForm source, boolean fromClient, String username);
public String getUsername();
}
// Internationalization
public class LoginI18n {
public Header getHeader();
public void setHeader(Header header);
public Form getForm();
public void setForm(Form form);
public ErrorMessage getErrorMessage();
public void setErrorMessage(ErrorMessage errorMessage);
public static class Header {
public String getTitle();
public void setTitle(String title);
public String getDescription();
public void setDescription(String description);
}
public static class Form {
public String getTitle();
public void setTitle(String title);
public String getUsername();
public void setUsername(String username);
public String getPassword();
public void setPassword(String password);
public String getSubmit();
public void setSubmit(String submit);
public String getForgotPassword();
public void setForgotPassword(String forgotPassword);
}
public static class ErrorMessage {
public String getTitle();
public void setTitle(String title);
public String getMessage();
public void setMessage(String message);
}
}Implement authorization at the navigation level.
public interface BeforeEnterObserver {
void beforeEnter(BeforeEnterEvent event);
}
// Example navigation guard
@Component
public class AuthenticationGuard implements BeforeEnterObserver {
@Autowired
private SecurityService securityService;
@Override
public void beforeEnter(BeforeEnterEvent event) {
if (!securityService.isAuthenticated()) {
// Redirect to login if not authenticated
event.rerouteTo(LoginView.class);
}
}
}
// Role-based navigation guard
public class RoleBasedGuard implements BeforeEnterObserver {
private final String requiredRole;
public RoleBasedGuard(String requiredRole) {
this.requiredRole = requiredRole;
}
@Override
public void beforeEnter(BeforeEnterEvent event) {
if (!hasRequiredRole()) {
event.rerouteTo(AccessDeniedView.class);
}
}
private boolean hasRequiredRole() {
// Check if current user has required role
return SecurityContextHolder.getContext().getAuthentication()
.getAuthorities().stream()
.anyMatch(auth -> auth.getAuthority().equals("ROLE_" + requiredRole));
}
}Programmatic access control within views and components.
public class SecurityUtils {
public static boolean isUserLoggedIn() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null &&
authentication.isAuthenticated() &&
!(authentication instanceof AnonymousAuthenticationToken);
}
public static boolean hasRole(String role) {
return SecurityContextHolder.getContext().getAuthentication()
.getAuthorities().stream()
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_" + role));
}
public static boolean hasAnyRole(String... roles) {
return Arrays.stream(roles).anyMatch(SecurityUtils::hasRole);
}
public static String getCurrentUsername() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
return authentication.getName();
}
return null;
}
public static Optional<Object> getCurrentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
return Optional.ofNullable(authentication.getPrincipal());
}
return Optional.empty();
}
}Built-in CSRF protection with token management.
public class CSRFTokenService {
// CSRF tokens are automatically handled by Vaadin
// Manual token access for custom scenarios
public String getCSRFToken() {
VaadinSession session = VaadinSession.getCurrent();
if (session != null) {
String token = (String) session.getAttribute("csrf-token");
if (token == null) {
token = generateCSRFToken();
session.setAttribute("csrf-token", token);
}
return token;
}
return null;
}
public boolean validateCSRFToken(String token) {
String sessionToken = getCSRFToken();
return sessionToken != null && sessionToken.equals(token);
}
private String generateCSRFToken() {
return UUID.randomUUID().toString();
}
}Secure session handling and management.
public class SessionSecurityService {
public void invalidateSession() {
VaadinSession vaadinSession = VaadinSession.getCurrent();
if (vaadinSession != null) {
// Clear security context
SecurityContextHolder.clearContext();
// Invalidate session
vaadinSession.close();
// Redirect to login
UI.getCurrent().getPage().setLocation("/login");
}
}
public void refreshSession() {
VaadinSession session = VaadinSession.getCurrent();
if (session != null) {
// Force session regeneration for security
session.lock();
try {
// Migrate session data if needed
Object userData = session.getAttribute("user-data");
session.close();
// Create new session
VaadinSession newSession = VaadinSession.getCurrent();
if (userData != null) {
newSession.setAttribute("user-data", userData);
}
} finally {
session.unlock();
}
}
}
public void setSessionTimeout(int timeoutInSeconds) {
VaadinSession session = VaadinSession.getCurrent();
if (session != null) {
session.getSession().setMaxInactiveInterval(timeoutInSeconds);
}
}
public boolean isSessionExpired() {
VaadinSession session = VaadinSession.getCurrent();
return session == null || session.isClosed();
}
}Content Security Policy configuration for enhanced security.
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class)
public class MyUIServlet extends VaadinServlet {
@Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
// Configure CSP headers
getService().addSessionInitListener(event -> {
event.getSource().addBootstrapListener(response -> {
response.setHeader("Content-Security-Policy",
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: https:; " +
"connect-src 'self' ws: wss:; " +
"font-src 'self'; " +
"object-src 'none'; " +
"media-src 'self'; " +
"frame-src 'none';"
);
});
});
}
}Server-side validation for security and data integrity.
public class SecurityValidator {
private static final Pattern SQL_INJECTION_PATTERN =
Pattern.compile("('.+--)|(--.+)|('.+#)|(#.+)|(\\b(select|insert|update|delete|drop|create|alter|exec|execute|union|script)\\b)",
Pattern.CASE_INSENSITIVE);
private static final Pattern XSS_PATTERN =
Pattern.compile("<\\s*script\\s*.*?>|<\\s*\\/\\s*script\\s*>|javascript:|vbscript:|onload=|onerror=|onmouseover=",
Pattern.CASE_INSENSITIVE);
public static boolean containsSQLInjection(String input) {
if (input == null) return false;
return SQL_INJECTION_PATTERN.matcher(input).find();
}
public static boolean containsXSS(String input) {
if (input == null) return false;
return XSS_PATTERN.matcher(input).find();
}
public static String sanitizeInput(String input) {
if (input == null) return null;
// Remove potentially dangerous characters
String sanitized = input.replaceAll("[<>\"'%;()&+]", "");
// Limit length
if (sanitized.length() > 1000) {
sanitized = sanitized.substring(0, 1000);
}
return sanitized.trim();
}
public static boolean isValidEmail(String email) {
return email != null &&
email.matches("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\\.[A-Za-z]{2,})$") &&
email.length() <= 254;
}
public static boolean isValidUsername(String username) {
return username != null &&
username.matches("^[a-zA-Z0-9_.-]{3,50}$");
}
}Handle security-related events and logging.
public class SecurityEventService {
private static final Logger logger = LoggerFactory.getLogger(SecurityEventService.class);
public void logSuccessfulLogin(String username, String ipAddress) {
logger.info("Successful login - User: {}, IP: {}", username, ipAddress);
// Store in audit log
}
public void logFailedLogin(String username, String ipAddress) {
logger.warn("Failed login attempt - User: {}, IP: {}", username, ipAddress);
// Implement brute force protection
}
public void logLogout(String username) {
logger.info("User logout - User: {}", username);
}
public void logSecurityViolation(String username, String violation, String details) {
logger.error("Security violation - User: {}, Violation: {}, Details: {}",
username, violation, details);
// Alert security team
}
public void logAccessDenied(String username, String resource) {
logger.warn("Access denied - User: {}, Resource: {}", username, resource);
}
}Best practices for secure Vaadin application configuration.
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/register", "/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error=true")
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
)
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)
.headers(headers -> headers
.frameOptions().deny()
.contentTypeOptions().and()
.httpStrictTransportSecurity(hsts -> hsts
.maxAgeInSeconds(31536000)
.includeSubdomains(true)
)
)
.build();
}
}@Route("admin/users")
@PageTitle("User Management")
@RolesAllowed("ADMIN")
public class UserManagementView extends VerticalLayout implements BeforeEnterObserver {
@Autowired
private SecurityService securityService;
@Autowired
private UserService userService;
private Grid<User> userGrid;
public UserManagementView() {
setupUI();
}
@Override
public void beforeEnter(BeforeEnterEvent event) {
// Additional security check
if (!securityService.hasRole("ADMIN")) {
event.rerouteTo(AccessDeniedView.class);
return;
}
// Log access
String currentUser = securityService.getAuthenticatedUser().getUsername();
logger.info("Admin access - User: {}, View: UserManagement", currentUser);
}
private void setupUI() {
H1 title = new H1("User Management");
userGrid = new Grid<>(User.class);
configureGrid();
Button addUserButton = new Button("Add User");
addUserButton.addClickListener(e -> addUser());
add(title, addUserButton, userGrid);
}
private void configureGrid() {
userGrid.setColumns("username", "email", "role", "lastLogin");
userGrid.addComponentColumn(user -> createActionButtons(user))
.setHeader("Actions");
userGrid.setItems(userService.findAll());
}
private Component createActionButtons(User user) {
Button editButton = new Button("Edit");
editButton.addClickListener(e -> editUser(user));
Button deleteButton = new Button("Delete");
deleteButton.addClickListener(e -> confirmDeleteUser(user));
deleteButton.getStyle().set("color", "var(--lumo-error-color)");
return new HorizontalLayout(editButton, deleteButton);
}
}@Route("login")
@PageTitle("Login")
@AnonymousAllowed
public class LoginView extends VerticalLayout {
@Autowired
private SecurityEventService securityEventService;
private LoginForm loginForm;
public LoginView() {
setupUI();
}
private void setupUI() {
setSizeFull();
setJustifyContentMode(JustifyContentMode.CENTER);
setAlignItems(Alignment.CENTER);
loginForm = new LoginForm();
loginForm.setAction("login");
loginForm.addLoginListener(this::handleLogin);
// Customize appearance
LoginI18n i18n = LoginI18n.createDefault();
i18n.getForm().setTitle("Secure Application");
i18n.getForm().setUsername("Username");
i18n.getForm().setPassword("Password");
i18n.getForm().setSubmit("Sign In");
loginForm.setI18n(i18n);
add(loginForm);
}
private void handleLogin(LoginEvent event) {
String username = event.getUsername();
String ipAddress = getClientIP();
try {
// Validate input
if (!SecurityValidator.isValidUsername(username)) {
loginForm.setError(true);
securityEventService.logSecurityViolation(username, "Invalid username format", ipAddress);
return;
}
// Additional security checks can be added here
securityEventService.logSuccessfulLogin(username, ipAddress);
} catch (Exception e) {
loginForm.setError(true);
securityEventService.logFailedLogin(username, ipAddress);
}
}
private String getClientIP() {
VaadinRequest request = VaadinService.getCurrentRequest();
return request != null ? request.getRemoteAddr() : "unknown";
}
}@Service
public class SecureUserService {
@Autowired
private UserRepository userRepository;
@Autowired
private SecurityEventService securityEventService;
@PreAuthorize("hasRole('ADMIN') or authentication.name == #username")
public User getUserByUsername(String username) {
// Validate input
if (!SecurityValidator.isValidUsername(username)) {
throw new IllegalArgumentException("Invalid username format");
}
return userRepository.findByUsername(username);
}
@PreAuthorize("hasRole('ADMIN')")
public List<User> getAllUsers() {
String currentUser = SecurityContextHolder.getContext()
.getAuthentication().getName();
securityEventService.logSecurityViolation(currentUser,
"Admin access", "Retrieved all users");
return userRepository.findAll();
}
@PreAuthorize("hasRole('ADMIN') or authentication.name == #user.username")
public User saveUser(User user) {
// Sanitize input
user.setEmail(SecurityValidator.sanitizeInput(user.getEmail()));
user.setUsername(SecurityValidator.sanitizeInput(user.getUsername()));
// Validate data
if (!SecurityValidator.isValidEmail(user.getEmail())) {
throw new IllegalArgumentException("Invalid email format");
}
return userRepository.save(user);
}
}The security system provides comprehensive protection while maintaining ease of development through declarative annotations and programmatic APIs.
Install with Tessl CLI
npx tessl i tessl/maven-com-vaadin--vaadin