0
# Security
1
2
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.
3
4
## Core Imports
5
6
```java
7
// Security annotations
8
import com.vaadin.flow.server.auth.AnonymousAllowed;
9
import com.vaadin.flow.server.auth.PermitAll;
10
import com.vaadin.flow.server.auth.DenyAll;
11
import com.vaadin.flow.server.auth.RolesAllowed;
12
13
// Login components
14
import com.vaadin.flow.component.login.LoginForm;
15
import com.vaadin.flow.component.login.LoginOverlay;
16
import com.vaadin.flow.component.login.AbstractLogin.LoginEvent;
17
import com.vaadin.flow.component.login.AbstractLogin.ForgotPasswordEvent;
18
import com.vaadin.flow.component.login.LoginI18n;
19
20
// Navigation guards and observers
21
import com.vaadin.flow.router.BeforeEnterObserver;
22
import com.vaadin.flow.router.BeforeEnterEvent;
23
import com.vaadin.flow.router.BeforeLeaveObserver;
24
import com.vaadin.flow.router.BeforeLeaveEvent;
25
26
// Session and security context
27
import com.vaadin.flow.server.VaadinSession;
28
import com.vaadin.flow.server.VaadinService;
29
import com.vaadin.flow.server.VaadinRequest;
30
import com.vaadin.flow.component.UI;
31
32
// Component events
33
import com.vaadin.flow.component.ComponentEventListener;
34
import com.vaadin.flow.shared.Registration;
35
36
// Spring Security integration
37
import org.springframework.security.core.Authentication;
38
import org.springframework.security.core.context.SecurityContext;
39
import org.springframework.security.core.context.SecurityContextHolder;
40
import org.springframework.security.core.userdetails.UserDetails;
41
import org.springframework.security.authentication.AnonymousAuthenticationToken;
42
import org.springframework.security.core.GrantedAuthority;
43
44
// Spring Security configuration
45
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
46
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
47
import org.springframework.security.config.http.SessionCreationPolicy;
48
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
49
import org.springframework.security.crypto.password.PasswordEncoder;
50
import org.springframework.security.web.SecurityFilterChain;
51
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
52
53
// Method-level security
54
import org.springframework.security.access.prepost.PreAuthorize;
55
56
// Spring framework
57
import org.springframework.context.annotation.Bean;
58
import org.springframework.context.annotation.Configuration;
59
import org.springframework.beans.factory.annotation.Autowired;
60
import org.springframework.stereotype.Component;
61
import org.springframework.stereotype.Service;
62
63
// Servlet configuration
64
import com.vaadin.flow.server.VaadinServlet;
65
import com.vaadin.flow.server.VaadinServletConfiguration;
66
import javax.servlet.annotation.WebServlet;
67
import javax.servlet.ServletException;
68
69
// Standard Java types
70
import java.security.Principal;
71
import java.util.Arrays;
72
import java.util.List;
73
import java.util.Optional;
74
import java.util.Set;
75
import java.util.UUID;
76
import java.util.regex.Pattern;
77
78
// Logging
79
import org.slf4j.Logger;
80
import org.slf4j.LoggerFactory;
81
82
// Core components
83
import com.vaadin.flow.component.Component;
84
import com.vaadin.flow.component.button.Button;
85
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
86
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
87
import com.vaadin.flow.component.html.H1;
88
import com.vaadin.flow.component.notification.Notification;
89
import com.vaadin.flow.component.grid.Grid;
90
91
// Route annotations
92
import com.vaadin.flow.router.Route;
93
import com.vaadin.flow.router.PageTitle;
94
```
95
96
## Authentication Annotations
97
98
### Core Security Annotations
99
100
Built-in annotations for declarative access control.
101
102
```java { .api }
103
@Target({ElementType.TYPE, ElementType.METHOD})
104
@Retention(RetentionPolicy.RUNTIME)
105
public @interface AnonymousAllowed {
106
// Allows access to anonymous (unauthenticated) users
107
}
108
109
@Target({ElementType.TYPE, ElementType.METHOD})
110
@Retention(RetentionPolicy.RUNTIME)
111
public @interface PermitAll {
112
// Allows access to all authenticated users
113
}
114
115
@Target({ElementType.TYPE, ElementType.METHOD})
116
@Retention(RetentionPolicy.RUNTIME)
117
public @interface DenyAll {
118
// Denies access to all users
119
}
120
121
@Target({ElementType.TYPE, ElementType.METHOD})
122
@Retention(RetentionPolicy.RUNTIME)
123
public @interface RolesAllowed {
124
String[] value(); // Specifies allowed roles
125
}
126
```
127
128
## Authentication Integration
129
130
### SecurityContext Access
131
132
Access current authentication information.
133
134
```java { .api }
135
// Spring Security integration
136
public class SecurityService {
137
138
public UserDetails getAuthenticatedUser() {
139
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
140
if (authentication != null && authentication.isAuthenticated()) {
141
Object principal = authentication.getPrincipal();
142
if (principal instanceof UserDetails) {
143
return (UserDetails) principal;
144
}
145
}
146
return null;
147
}
148
149
public boolean isAuthenticated() {
150
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
151
return authentication != null &&
152
authentication.isAuthenticated() &&
153
!(authentication instanceof AnonymousAuthenticationToken);
154
}
155
156
public boolean hasRole(String role) {
157
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
158
if (authentication != null) {
159
return authentication.getAuthorities().stream()
160
.anyMatch(authority -> authority.getAuthority().equals("ROLE_" + role));
161
}
162
return false;
163
}
164
165
public void logout() {
166
SecurityContextHolder.clearContext();
167
VaadinSession.getCurrent().getSession().invalidate();
168
UI.getCurrent().navigate("");
169
}
170
}
171
```
172
173
### Login Components
174
175
Built-in login form components with customization options.
176
177
```java { .api }
178
public class LoginForm extends Component {
179
public LoginForm();
180
181
// Event handling
182
public Registration addLoginListener(ComponentEventListener<LoginEvent> listener);
183
public Registration addForgotPasswordListener(ComponentEventListener<ForgotPasswordEvent> listener);
184
185
// Configuration
186
public void setError(boolean error);
187
public boolean isError();
188
public void setEnabled(boolean enabled);
189
public boolean isEnabled();
190
191
// Internationalization
192
public void setI18n(LoginI18n i18n);
193
public LoginI18n getI18n();
194
195
// Actions
196
public void setAction(String action);
197
public String getAction();
198
public void setForgotPasswordButtonVisible(boolean forgotPasswordButtonVisible);
199
public boolean isForgotPasswordButtonVisible();
200
}
201
202
public class LoginOverlay extends LoginForm {
203
public LoginOverlay();
204
205
// Overlay control
206
public void setOpened(boolean opened);
207
public boolean isOpened();
208
209
// Title and description
210
public void setTitle(String title);
211
public String getTitle();
212
public void setDescription(String description);
213
public String getDescription();
214
}
215
216
// Login events
217
public class LoginEvent extends ComponentEvent<LoginForm> {
218
public LoginEvent(LoginForm source, boolean fromClient, String username, String password);
219
220
public String getUsername();
221
public String getPassword();
222
}
223
224
public class ForgotPasswordEvent extends ComponentEvent<LoginForm> {
225
public ForgotPasswordEvent(LoginForm source, boolean fromClient, String username);
226
227
public String getUsername();
228
}
229
230
// Internationalization
231
public class LoginI18n {
232
public Header getHeader();
233
public void setHeader(Header header);
234
public Form getForm();
235
public void setForm(Form form);
236
public ErrorMessage getErrorMessage();
237
public void setErrorMessage(ErrorMessage errorMessage);
238
239
public static class Header {
240
public String getTitle();
241
public void setTitle(String title);
242
public String getDescription();
243
public void setDescription(String description);
244
}
245
246
public static class Form {
247
public String getTitle();
248
public void setTitle(String title);
249
public String getUsername();
250
public void setUsername(String username);
251
public String getPassword();
252
public void setPassword(String password);
253
public String getSubmit();
254
public void setSubmit(String submit);
255
public String getForgotPassword();
256
public void setForgotPassword(String forgotPassword);
257
}
258
259
public static class ErrorMessage {
260
public String getTitle();
261
public void setTitle(String title);
262
public String getMessage();
263
public void setMessage(String message);
264
}
265
}
266
```
267
268
## Authorization and Access Control
269
270
### Navigation Guards
271
272
Implement authorization at the navigation level.
273
274
```java { .api }
275
public interface BeforeEnterObserver {
276
void beforeEnter(BeforeEnterEvent event);
277
}
278
279
// Example navigation guard
280
@Component
281
public class AuthenticationGuard implements BeforeEnterObserver {
282
283
@Autowired
284
private SecurityService securityService;
285
286
@Override
287
public void beforeEnter(BeforeEnterEvent event) {
288
if (!securityService.isAuthenticated()) {
289
// Redirect to login if not authenticated
290
event.rerouteTo(LoginView.class);
291
}
292
}
293
}
294
295
// Role-based navigation guard
296
public class RoleBasedGuard implements BeforeEnterObserver {
297
298
private final String requiredRole;
299
300
public RoleBasedGuard(String requiredRole) {
301
this.requiredRole = requiredRole;
302
}
303
304
@Override
305
public void beforeEnter(BeforeEnterEvent event) {
306
if (!hasRequiredRole()) {
307
event.rerouteTo(AccessDeniedView.class);
308
}
309
}
310
311
private boolean hasRequiredRole() {
312
// Check if current user has required role
313
return SecurityContextHolder.getContext().getAuthentication()
314
.getAuthorities().stream()
315
.anyMatch(auth -> auth.getAuthority().equals("ROLE_" + requiredRole));
316
}
317
}
318
```
319
320
### Programmatic Security
321
322
Programmatic access control within views and components.
323
324
```java { .api }
325
public class SecurityUtils {
326
327
public static boolean isUserLoggedIn() {
328
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
329
return authentication != null &&
330
authentication.isAuthenticated() &&
331
!(authentication instanceof AnonymousAuthenticationToken);
332
}
333
334
public static boolean hasRole(String role) {
335
return SecurityContextHolder.getContext().getAuthentication()
336
.getAuthorities().stream()
337
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_" + role));
338
}
339
340
public static boolean hasAnyRole(String... roles) {
341
return Arrays.stream(roles).anyMatch(SecurityUtils::hasRole);
342
}
343
344
public static String getCurrentUsername() {
345
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
346
if (authentication != null && authentication.isAuthenticated()) {
347
return authentication.getName();
348
}
349
return null;
350
}
351
352
public static Optional<Object> getCurrentUser() {
353
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
354
if (authentication != null && authentication.isAuthenticated()) {
355
return Optional.ofNullable(authentication.getPrincipal());
356
}
357
return Optional.empty();
358
}
359
}
360
```
361
362
## CSRF Protection
363
364
### CSRF Token Handling
365
366
Built-in CSRF protection with token management.
367
368
```java { .api }
369
public class CSRFTokenService {
370
371
// CSRF tokens are automatically handled by Vaadin
372
// Manual token access for custom scenarios
373
374
public String getCSRFToken() {
375
VaadinSession session = VaadinSession.getCurrent();
376
if (session != null) {
377
String token = (String) session.getAttribute("csrf-token");
378
if (token == null) {
379
token = generateCSRFToken();
380
session.setAttribute("csrf-token", token);
381
}
382
return token;
383
}
384
return null;
385
}
386
387
public boolean validateCSRFToken(String token) {
388
String sessionToken = getCSRFToken();
389
return sessionToken != null && sessionToken.equals(token);
390
}
391
392
private String generateCSRFToken() {
393
return UUID.randomUUID().toString();
394
}
395
}
396
```
397
398
## Session Security
399
400
### Session Management
401
402
Secure session handling and management.
403
404
```java { .api }
405
public class SessionSecurityService {
406
407
public void invalidateSession() {
408
VaadinSession vaadinSession = VaadinSession.getCurrent();
409
if (vaadinSession != null) {
410
// Clear security context
411
SecurityContextHolder.clearContext();
412
413
// Invalidate session
414
vaadinSession.close();
415
416
// Redirect to login
417
UI.getCurrent().getPage().setLocation("/login");
418
}
419
}
420
421
public void refreshSession() {
422
VaadinSession session = VaadinSession.getCurrent();
423
if (session != null) {
424
// Force session regeneration for security
425
session.lock();
426
try {
427
// Migrate session data if needed
428
Object userData = session.getAttribute("user-data");
429
session.close();
430
431
// Create new session
432
VaadinSession newSession = VaadinSession.getCurrent();
433
if (userData != null) {
434
newSession.setAttribute("user-data", userData);
435
}
436
} finally {
437
session.unlock();
438
}
439
}
440
}
441
442
public void setSessionTimeout(int timeoutInSeconds) {
443
VaadinSession session = VaadinSession.getCurrent();
444
if (session != null) {
445
session.getSession().setMaxInactiveInterval(timeoutInSeconds);
446
}
447
}
448
449
public boolean isSessionExpired() {
450
VaadinSession session = VaadinSession.getCurrent();
451
return session == null || session.isClosed();
452
}
453
}
454
```
455
456
## Content Security Policy
457
458
### CSP Configuration
459
460
Content Security Policy configuration for enhanced security.
461
462
```java { .api }
463
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
464
@VaadinServletConfiguration(ui = MyUI.class)
465
public class MyUIServlet extends VaadinServlet {
466
467
@Override
468
protected void servletInitialized() throws ServletException {
469
super.servletInitialized();
470
471
// Configure CSP headers
472
getService().addSessionInitListener(event -> {
473
event.getSource().addBootstrapListener(response -> {
474
response.setHeader("Content-Security-Policy",
475
"default-src 'self'; " +
476
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
477
"style-src 'self' 'unsafe-inline'; " +
478
"img-src 'self' data: https:; " +
479
"connect-src 'self' ws: wss:; " +
480
"font-src 'self'; " +
481
"object-src 'none'; " +
482
"media-src 'self'; " +
483
"frame-src 'none';"
484
);
485
});
486
});
487
}
488
}
489
```
490
491
## Input Validation and Sanitization
492
493
### Data Validation
494
495
Server-side validation for security and data integrity.
496
497
```java { .api }
498
public class SecurityValidator {
499
500
private static final Pattern SQL_INJECTION_PATTERN =
501
Pattern.compile("('.+--)|(--.+)|('.+#)|(#.+)|(\\b(select|insert|update|delete|drop|create|alter|exec|execute|union|script)\\b)",
502
Pattern.CASE_INSENSITIVE);
503
504
private static final Pattern XSS_PATTERN =
505
Pattern.compile("<\\s*script\\s*.*?>|<\\s*\\/\\s*script\\s*>|javascript:|vbscript:|onload=|onerror=|onmouseover=",
506
Pattern.CASE_INSENSITIVE);
507
508
public static boolean containsSQLInjection(String input) {
509
if (input == null) return false;
510
return SQL_INJECTION_PATTERN.matcher(input).find();
511
}
512
513
public static boolean containsXSS(String input) {
514
if (input == null) return false;
515
return XSS_PATTERN.matcher(input).find();
516
}
517
518
public static String sanitizeInput(String input) {
519
if (input == null) return null;
520
521
// Remove potentially dangerous characters
522
String sanitized = input.replaceAll("[<>\"'%;()&+]", "");
523
524
// Limit length
525
if (sanitized.length() > 1000) {
526
sanitized = sanitized.substring(0, 1000);
527
}
528
529
return sanitized.trim();
530
}
531
532
public static boolean isValidEmail(String email) {
533
return email != null &&
534
email.matches("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\\.[A-Za-z]{2,})$") &&
535
email.length() <= 254;
536
}
537
538
public static boolean isValidUsername(String username) {
539
return username != null &&
540
username.matches("^[a-zA-Z0-9_.-]{3,50}$");
541
}
542
}
543
```
544
545
## Security Event Handling
546
547
### Security Events
548
549
Handle security-related events and logging.
550
551
```java { .api }
552
public class SecurityEventService {
553
554
private static final Logger logger = LoggerFactory.getLogger(SecurityEventService.class);
555
556
public void logSuccessfulLogin(String username, String ipAddress) {
557
logger.info("Successful login - User: {}, IP: {}", username, ipAddress);
558
// Store in audit log
559
}
560
561
public void logFailedLogin(String username, String ipAddress) {
562
logger.warn("Failed login attempt - User: {}, IP: {}", username, ipAddress);
563
// Implement brute force protection
564
}
565
566
public void logLogout(String username) {
567
logger.info("User logout - User: {}", username);
568
}
569
570
public void logSecurityViolation(String username, String violation, String details) {
571
logger.error("Security violation - User: {}, Violation: {}, Details: {}",
572
username, violation, details);
573
// Alert security team
574
}
575
576
public void logAccessDenied(String username, String resource) {
577
logger.warn("Access denied - User: {}, Resource: {}", username, resource);
578
}
579
}
580
```
581
582
## Secure Configuration
583
584
### Security Configuration
585
586
Best practices for secure Vaadin application configuration.
587
588
```java { .api }
589
@Configuration
590
@EnableWebSecurity
591
public class SecurityConfiguration {
592
593
@Bean
594
public PasswordEncoder passwordEncoder() {
595
return new BCryptPasswordEncoder(12);
596
}
597
598
@Bean
599
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
600
return http
601
.authorizeHttpRequests(auth -> auth
602
.requestMatchers("/login", "/register", "/public/**").permitAll()
603
.requestMatchers("/admin/**").hasRole("ADMIN")
604
.anyRequest().authenticated()
605
)
606
.formLogin(form -> form
607
.loginPage("/login")
608
.defaultSuccessUrl("/dashboard")
609
.failureUrl("/login?error=true")
610
.permitAll()
611
)
612
.logout(logout -> logout
613
.logoutUrl("/logout")
614
.logoutSuccessUrl("/login")
615
.invalidateHttpSession(true)
616
.deleteCookies("JSESSIONID")
617
.permitAll()
618
)
619
.sessionManagement(session -> session
620
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
621
.maximumSessions(1)
622
.maxSessionsPreventsLogin(false)
623
)
624
.csrf(csrf -> csrf
625
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
626
)
627
.headers(headers -> headers
628
.frameOptions().deny()
629
.contentTypeOptions().and()
630
.httpStrictTransportSecurity(hsts -> hsts
631
.maxAgeInSeconds(31536000)
632
.includeSubdomains(true)
633
)
634
)
635
.build();
636
}
637
}
638
```
639
640
## Usage Examples
641
642
### Secured View Implementation
643
644
```java
645
@Route("admin/users")
646
@PageTitle("User Management")
647
@RolesAllowed("ADMIN")
648
public class UserManagementView extends VerticalLayout implements BeforeEnterObserver {
649
650
@Autowired
651
private SecurityService securityService;
652
653
@Autowired
654
private UserService userService;
655
656
private Grid<User> userGrid;
657
658
public UserManagementView() {
659
setupUI();
660
}
661
662
@Override
663
public void beforeEnter(BeforeEnterEvent event) {
664
// Additional security check
665
if (!securityService.hasRole("ADMIN")) {
666
event.rerouteTo(AccessDeniedView.class);
667
return;
668
}
669
670
// Log access
671
String currentUser = securityService.getAuthenticatedUser().getUsername();
672
logger.info("Admin access - User: {}, View: UserManagement", currentUser);
673
}
674
675
private void setupUI() {
676
H1 title = new H1("User Management");
677
678
userGrid = new Grid<>(User.class);
679
configureGrid();
680
681
Button addUserButton = new Button("Add User");
682
addUserButton.addClickListener(e -> addUser());
683
684
add(title, addUserButton, userGrid);
685
}
686
687
private void configureGrid() {
688
userGrid.setColumns("username", "email", "role", "lastLogin");
689
userGrid.addComponentColumn(user -> createActionButtons(user))
690
.setHeader("Actions");
691
692
userGrid.setItems(userService.findAll());
693
}
694
695
private Component createActionButtons(User user) {
696
Button editButton = new Button("Edit");
697
editButton.addClickListener(e -> editUser(user));
698
699
Button deleteButton = new Button("Delete");
700
deleteButton.addClickListener(e -> confirmDeleteUser(user));
701
deleteButton.getStyle().set("color", "var(--lumo-error-color)");
702
703
return new HorizontalLayout(editButton, deleteButton);
704
}
705
}
706
```
707
708
### Login View with Security
709
710
```java
711
@Route("login")
712
@PageTitle("Login")
713
@AnonymousAllowed
714
public class LoginView extends VerticalLayout {
715
716
@Autowired
717
private SecurityEventService securityEventService;
718
719
private LoginForm loginForm;
720
721
public LoginView() {
722
setupUI();
723
}
724
725
private void setupUI() {
726
setSizeFull();
727
setJustifyContentMode(JustifyContentMode.CENTER);
728
setAlignItems(Alignment.CENTER);
729
730
loginForm = new LoginForm();
731
loginForm.setAction("login");
732
loginForm.addLoginListener(this::handleLogin);
733
734
// Customize appearance
735
LoginI18n i18n = LoginI18n.createDefault();
736
i18n.getForm().setTitle("Secure Application");
737
i18n.getForm().setUsername("Username");
738
i18n.getForm().setPassword("Password");
739
i18n.getForm().setSubmit("Sign In");
740
loginForm.setI18n(i18n);
741
742
add(loginForm);
743
}
744
745
private void handleLogin(LoginEvent event) {
746
String username = event.getUsername();
747
String ipAddress = getClientIP();
748
749
try {
750
// Validate input
751
if (!SecurityValidator.isValidUsername(username)) {
752
loginForm.setError(true);
753
securityEventService.logSecurityViolation(username, "Invalid username format", ipAddress);
754
return;
755
}
756
757
// Additional security checks can be added here
758
securityEventService.logSuccessfulLogin(username, ipAddress);
759
760
} catch (Exception e) {
761
loginForm.setError(true);
762
securityEventService.logFailedLogin(username, ipAddress);
763
}
764
}
765
766
private String getClientIP() {
767
VaadinRequest request = VaadinService.getCurrentRequest();
768
return request != null ? request.getRemoteAddr() : "unknown";
769
}
770
}
771
```
772
773
### Secure Data Access
774
775
```java
776
@Service
777
public class SecureUserService {
778
779
@Autowired
780
private UserRepository userRepository;
781
782
@Autowired
783
private SecurityEventService securityEventService;
784
785
@PreAuthorize("hasRole('ADMIN') or authentication.name == #username")
786
public User getUserByUsername(String username) {
787
// Validate input
788
if (!SecurityValidator.isValidUsername(username)) {
789
throw new IllegalArgumentException("Invalid username format");
790
}
791
792
return userRepository.findByUsername(username);
793
}
794
795
@PreAuthorize("hasRole('ADMIN')")
796
public List<User> getAllUsers() {
797
String currentUser = SecurityContextHolder.getContext()
798
.getAuthentication().getName();
799
800
securityEventService.logSecurityViolation(currentUser,
801
"Admin access", "Retrieved all users");
802
803
return userRepository.findAll();
804
}
805
806
@PreAuthorize("hasRole('ADMIN') or authentication.name == #user.username")
807
public User saveUser(User user) {
808
// Sanitize input
809
user.setEmail(SecurityValidator.sanitizeInput(user.getEmail()));
810
user.setUsername(SecurityValidator.sanitizeInput(user.getUsername()));
811
812
// Validate data
813
if (!SecurityValidator.isValidEmail(user.getEmail())) {
814
throw new IllegalArgumentException("Invalid email format");
815
}
816
817
return userRepository.save(user);
818
}
819
}
820
```
821
822
The security system provides comprehensive protection while maintaining ease of development through declarative annotations and programmatic APIs.