Customizing Navigation Access Control
VaadinSecurityConfigurer enables navigation access control automatically. This page describes how to customize it in a Spring application.
Configuring the Access Checkers
When using VaadinSecurityConfigurer for configuring Spring Security, the Navigation Access Control feature is enabled by default. You can disable it by calling the enableNavigationAccessControl(false) method on the configurer. By default, only the AnnotatedViewAccessChecker is active.
For a fine-grained configuration, you can expose a bean of type NavigationAccessControlConfigurer. NavigationAccessControlConfigurer allows activating out-of-the-box navigation access checkers or adding new ones, providing a custom decision resolver, completely disable the functionality, etc.
|
Note
|
If VaadinSecurityConfigurer is not used, exposing the NavigationAccessControlConfigurer bean is mandatory to activate navigation access control.
|
In the following example, Navigation Access Control is configured to activate route path checker, a custom checker instance and all available NavigationAccessChecker beans that extend VotingNavigationAccessChecker. It also provides a custom decision resolver.
Source code
Customize NavigationAccessControl in Spring Project
@Bean
NavigationAccessControlConfigurer navigationAccessControlConfigurerCustomizer() {
return new NavigationAccessControlConfigurer()
.withRoutePathAccessChecker() 1
.withNavigationAccessChecker(new CustomChecker()) 2
.withAvailableNavigationAccessCheckers(checker ->
checker instanceof VotingNavigationAccessChecker 3
)
.withDecisionResolver(new CustomDecisionResolver()); 4
}-
Activates the route path access checker. The implementation is provided by the Vaadin Spring module and exposed as a bean.
-
Adds a custom navigation access checker, by creating a new instance.
-
Activates all registered navigation access checker bean, that matches the predicate.
-
Sets a custom decision resolver.
|
Important
|
When using VaadinSecurityConfigurer in the same configuration class where you define the NavigationAccessControlConfigurer bean, make sure to declare the bean method as static to prevent bootstrap errors due to circular dependencies in bean definitions.
|
Source code
Define NavigationAccessControlConfigurer Bean with VaadinSecurityConfigurer
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
static NavigationAccessControlConfigurer navigationAccessControlConfigurer() {
return new NavigationAccessControlConfigurer()
.withRoutePathAccessChecker()
.withDecisionResolver(new CustomDecisionResolver());
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
// VaadinSecurityConfigurer will use the NavigationAccessControlConfigurer bean
}).build();
}
}Define NavigationAccessControlConfigurer Bean with VaadinSecurityConfigurer
Consult the NavigationAccessControlConfigurer Javadoc for more information on how navigation access control can be customized.
URL-Pattern-Based Security
With RoutePathAccessChecker, a Flow route is protected by Spring Security request matchers for its path, in the same way as any other URL. The path used to navigate to the view is evaluated with Spring’s WebInvocationPrivilegeEvaluator.
Only AnnotatedViewAccessChecker is active by default, so first activate RoutePathAccessChecker with a NavigationAccessControlConfigurer bean, as described in Configuring the Access Checkers:
Source code
Java
@Bean
static NavigationAccessControlConfigurer navigationAccessControlConfigurer() {
return new NavigationAccessControlConfigurer()
.withAnnotatedViewAccessChecker() 1
.withRoutePathAccessChecker(); 2
}-
Keeps checking the access annotations. Leave this out to use only the request matchers.
-
Checks the route paths against the request matchers.
Then protect the route paths with request matchers:
Source code
Java
@Route("admin")
public class AdminView extends Div { }
@Route("protected/profile")
public class ProfileView extends Div { }
@Route("special/preview-feature")
public class PreviewFeatureView extends Div { }
@Route("")
public class HomeView extends Div { }
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin").hasRole("ADMIN")
.requestMatchers("/protected/**").authenticated()
.requestMatchers("/special/**")
.access((authentication, object) -> {
// Custom authorization logic
return new AuthorizationDecision(isPremiumUser(authentication));
})
.requestMatchers("/")
.permitAll()
);
http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
// Configure Vaadin security settings
});
return http.build();
}
}Java
When both checkers are active, the access annotations and the request matchers for the same view must agree. If one of them allows the navigation and the other denies it, the default decision resolver rejects the navigation, as described in Decision Resolver. To keep the rules for a view in one place, use only one of the two mechanisms for it.