Test View Access Control
This page covers Spring Security and Quarkus Security in separate sections. For Java EE/CDI, use the CDI setup and your application’s CDI access-control beans and login flow; the security annotations below do not configure Weld.
First configure view protection in your application. These examples assume a public default route, a login view, and views restricted to specific roles. Adapt the route names and roles to your application.
Spring Security
Start with the Spring Boot test setup.
Set Up View Access Control
To apply view access control, Vaadin requires a NavigationAccessControl to be registered as a BeforeEnterListener for the UI. For @SpringBootTest annotated tests, the checker is created and configured automatically. However, when testing with a restricted ApplicationContext, you may want to perform the setup yourself in a Configuration class by providing a VaadinServiceInitListener that executes this step.
Source code
Set Up NavigationAccessControl for Plain Spring Project
@Configuration
class TestViewSecurityConfig {
@Bean
VaadinServiceInitListener setupViewSecurityScenario() {
SpringNavigationAccessControl accessControl = new SpringNavigationAccessControl();
accessControl.setLoginView(LoginView.class);
return event -> {
event.getSource().addUIInitListener(uiEvent -> {
uiEvent.getUI().addBeforeEnterListener(accessControl);
});
};
}
}If you’re using the Vaadin Spring Add-On, you can instead import the out-of-the-box NavigationAccessControlInitializer. It requires only that you define a NavigationAccessControl bean.
Source code
Set Up NavigationAccessControl with Vaadin Spring Add-On
@Configuration
@Import({NavigationAccessControlInitializer.class})
class TestViewSecurityConfig {
@Bean
NavigationAccessControl navigationAccessControl() {
return new SpringNavigationAccessControl();
}
}Testing with Spring Security Annotations
With this support, you can use Spring Security test annotations — such as @WithMockUser, @WithAnonymousUser, or @WithUserDetails — to simulate different authentication scenarios with test method granularity. More information is available on the Spring Security documentation site. Authentication details are available before creating the UI instance and navigating to the default route. This way redirects to the login view aren’t performed when simulating logged-in users. In the same way, custom redirect logic for authenticated users works as expected.
To use Spring Security test annotations, first make sure the dependency is added to the project.
Source code
XML
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>|
Caution
|
Overriding beans with @MockitoBean or @MockBean makes Spring cache a separate application context for that test class. In a multi-class run, this can prevent the simulated user from being applied during navigation, causing protected views to redirect unexpectedly to the login view — sometimes in other test classes. If security navigation tests fail only when the whole suite runs, suspect a bean override elsewhere. See Using a Reduced Application Context for a context-friendly way to replace services.
|
Then extend SpringBrowserlessTest and annotate test methods to set up an authentication scenario. For the simplest use cases, use @WithMockUser or @WithAnonymousUser, providing the username and roles that should be granted.
Source code
Tests with Mock Users
@SpringBootTest
public class ViewSecurityTest extends SpringBrowserlessTest {
@Test
@WithAnonymousUser
void anonymousUser_protectedView_redirectToLogin() {
navigate("protected", LoginView.class);
}
@Test
@WithAnonymousUser
void anonymousUser_publicView_signInLinkPresent() {
// public view is default page
Assertions.assertInstanceOf(PublicView.class, getCurrentView());
Anchor anchor = find(Anchor.class).withText("Sign in").single();
Assertions.assertTrue(
test(anchor).isUsable(),
"Sign in link should be available for anonymous user");
}
@Test
@WithMockUser(username = "admin", roles = "ADMIN")
void adminUser_adminView_viewShown() {
navigate(AdminRoleView.class);
Assertions.assertTrue(
find(Avatar.class).single().isVisible(),
"Avatar should be visible for logged users");
}
}When custom User objects or complex grant rules should be used, provide a custom UserDetailsService and annotate the test method with @WithUserDetails.
Source code
Tests with Mock UserDetailsService
@ContextConfiguration(classes = SecurityTestConfig.class)
class SpringUnitSecurityTest extends SpringBrowserlessTest {
@Test
@WithUserDetails("admin")
void superuser_adminView_viewShown() {
navigate(AdminRoleView.class);
Assertions.assertTrue(
find(Avatar.class).single().isVisible(),
"Avatar should be visible for logged users");
}
@Test
@WithUserDetails
void user_adminView_accessDenied() {
RouteNotFoundError errorView = navigate("admin-role",
RouteNotFoundError.class);
Assertions.assertTrue(
errorView.getElement().getChild(0).getOuterHTML()
.contains("Reason: Access denied"),
"Admin view should be accessible only by users with ADMIN role");
}
}
@Configuration
class SecurityTestConfig {
@Bean
UserDetailsService mockUserDetailsService() {
return new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
if ("user".equals(username)) {
return new User(username, UUID.randomUUID().toString(),
List.of(
new SimpleGrantedAuthority("ROLE_DEV"),
new SimpleGrantedAuthority("ROLE_USER")
));
}
if ("admin".equals(username)) {
return new User(username, UUID.randomUUID().toString(),
List.of(
new SimpleGrantedAuthority("ROLE_SUPERUSER"),
new SimpleGrantedAuthority("ROLE_ADMIN")
));
}
throw new UsernameNotFoundException(
"User " + username + " not exists");
}
};
}
}Navigate after Changing Authentication
The default Spring Security test annotations establish authentication before initial navigation.
When a test changes authentication later, navigate again to apply access control to the new user.
For a root route that redirects anonymous users to LoginView, use this Spring test method:
Source code
Java
@Test
@WithMockUser(username = "admin", roles = "ADMIN",
setupBefore = TestExecutionEvent.TEST_EXECUTION)
void adminSignsInDuringTest_adminViewShown() {
// Setup navigated to the root route while the user was still anonymous,
// and access control redirected that navigation to the login view.
Assertions.assertInstanceOf(LoginView.class, getCurrentView());
// Navigating again applies access control to the current authentication.
navigate(AdminView.class);
Assertions.assertTrue(find(Avatar.class).single().isVisible());
}The example uses Spring Security’s TestExecutionEvent from org.springframework.security.test.context.support.
For a login-form test, perform the application’s login action and then navigate to the protected view before asserting its contents.
See Authentication Timing for why reloading the current location does not replace this step.
Quarkus Security
Start with the Quarkus test setup.
Set Up View Access Control
To apply view access control, Vaadin requires a NavigationAccessControl to be registered as a BeforeEnterListener for the UI. Currently, the Vaadin Quarkus plugin doesn’t support automatic registration of the access control feature. To enable it for browserless testing, perform the setup in a QuarkusTestProfile class by providing an observer for the Vaadin ServiceInitEvent that executes this step.
Source code
NavigationAccessControl for Quarkus Project Test
public class TestViewSecurityConfig implements QuarkusTestProfile {
@Override
public String getConfigProfile() {
return "test-security"; 1
}
@IfBuildProfile("test-security") 1
public static class NavigationAccessControlInitializer {
public void serviceInit(@Observes ServiceInitEvent event) { 2
// @QuarkusTest starts the whole application, so we check
// the VaadinService type to enable access control only for
// browserless tests
if (event.getSource() instanceof MockQuarkusServletService) { 3
event.getSource().addUIInitListener(uiEvent -> {
// Customize the NavigationAccessControl as needed
NavigationAccessControl accessControl = new NavigationAccessControl();
accessControl.setLoginView(LoginView.class);
uiEvent.getUI().addBeforeEnterListener(accessControl);
});
}
}
}
}-
Sets the configuration profile to be used for the test. The class is annotated with
@IfBuildProfileto make the observer only run it for tests that require this profile. -
Listens for Vaadin
ServiceInitEvent. This is the same as implementingVaadinServiceInitListenerand registering the class to be loaded by JavaServiceLoader. -
Checks that execution is started by the browserless test. This is required because
@QuarkusTestcauses the whole application to start when running the test.
Quarkus Test Security Features
When using QuarkusBrowserlessTest, if Quarkus Security is present on the classpath, the mock environment is instructed to fetch authentication details from Quarkus SecurityIdentity.
With this support, you can use Quarkus @TestSecurity annotation to simulate different authentication scenarios with test method granularity. More information is available from the Quarkus Security Testing documentation. Authentication details are available before creating the UI instance and navigating to the default route. Redirects to the login view aren’t performed when simulating logged-in users. In the same way, custom redirect logic for authenticated users works as expected.
To use Quarkus Security test annotations, first ensure the dependency is added to the project:
Source code
XML
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security</artifactId>
<scope>test</scope>
</dependency>Next, extend QuarkusBrowserlessTest and annotate test methods to set up an authentication scenario. For the simplest situations, use @TestSecurity, providing the username and roles that should be granted.
Source code
Tests with Mock Users
@QuarkusTest
@TestProfile(TestViewSecurityConfig.class) 1
class ViewSecurityTest extends QuarkusBrowserlessTest {
@Test
@TestSecurity(authorizationEnabled = false) 2
void anonymousUser_protectedView_redirectToLogin() {
navigate("protected", LoginView.class);
}
@Test
@TestSecurity(authorizationEnabled = false) 2
void anonymousUser_publicView_signInLinkPresent() {
// public view is default page
Assertions.assertInstanceOf(PublicView.class, getCurrentView());
Anchor anchor = find(Anchor.class).withText("Sign in").single();
Assertions.assertTrue(
test(anchor).isUsable(),
"Sign in link should be available for anonymous user");
}
@Test
@TestSecurity(user = "admin", roles = "ADMIN") 2
void adminUser_adminView_viewShown() {
navigate(AdminRoleView.class);
Assertions.assertTrue(
find(Avatar.class).single().isVisible(),
"Avatar should be visible for logged users");
}
}-
Sets a profile to activate Vaadin access control feature.
-
Uses Quarkus test security annotations.
Verify the Suite
Run the security tests together with the rest of the suite using mvn test.
For concurrent users and security isolation, see Test Multiple Users and Windows.
011585C3-F724-4B8D-B0B2-A52595587AAA