Vaadin

Vaadin Spring Security Example

1. Introduction

Vaadin is a web application framework written in Java, and is built on Google Web Toolkit from Vaadin Ltd.

Spring Security is a part of Spring framework that focuses on providing both authentication and authorization to Java applications. Both Vaadin and Spring Security are open sourced and licensed under the Apache 2.0 license.

In this example, we will build a web application in three steps:

  1. Create a Vaadin web application from the Vaadin archetype
  2. Modify the generated Vaadin web application with model-view-controller patterns
  3. Secure the Vaadin web application with a login page via Spring Security framework

2. Technologies Used

The example code in this article was built and run using:

  • Java 1.8.101 (1.8.x will do fine)
  • Maven 3.3.9 (3.3.x will do fine)
  • Eclipse Mars (Any Java IDE would work)
  • Vaadin 8.1.5 (7.x will do fine)
  • Spring Security 4.2.3.RELEASE (5.x will do fine)
  • Jetty 9.x

3. A Generated Vaadin Web Application

Vaadin is designed for creating rich and interactive applications that run in the browser. If you have not worked with Vaadin before, please check it out here.

In this step, we will create a web application via the Vaadin Maven archetype.

3.1 Create a Vaadin Maven Project

Install the Eclipse Vaadin plug-in by following the instructions here.

Create a Vaadin Maven project with the steps below:

  1. File->New->Vaadin->Vaadin 8 Project, then click next

    Figure 1 Eclipse Vaadin Project
  2. Keep the default option as Single-Module Application Project, then click next
  3. Enter the Group Id as jcg.demo and Artifact Id as Vaadin-Spring-Security, then hit finish

It will generate a Maven Vaadin project with MyUI.java and README.txt. Follow the instructions in README.txt to build and run the project.

3.2 Review Generated MyUI.java

The generated MyUI.java has an init method which constructs a VerticalLayout with a TextField to take the user’s input and a Click Me button. The Click Me button displays the entered data.

MyUI.java

package jcg.demo.ui.vaadin;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

/**
 * This UI is the application entry point. A UI may either represent a browser window 
 * (or tab) or some part of a html page where a Vaadin application is embedded.
 * <p>
 * The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be 
 * overridden to add component to the user interface and initialize non-component functionality.
 */
@Theme("mytheme")
public class MyUI extends UI {

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        final VerticalLayout layout = new VerticalLayout();
        
        final TextField name = new TextField();
        name.setCaption("Type your name here:");

        Button button = new Button("Click Me");
        button.addClickListener( e -> {
            layout.addComponent(new Label("Thanks " + name.getValue() 
                    + ", it works!"));
        });
        
        layout.addComponents(name, button);
        layout.setMargin(true);
        layout.setSpacing(true);
        
        setContent(layout);
    }

    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
    public static class MyUIServlet extends VaadinServlet {
    }
}

3.3 Demo Generated Vaadin Web Application

Execute Jetty:run and go to http://localhost:8080. Enter a name and then hit the Click Me button. Repeat this process with different names and see the different outcomes.

Figure 2 ClickMe Demo

Note: The entered data is displayed on top of each other.

4. Modify the Vaadin Application

Model View Controller (MVC) is a very useful and popular design pattern for a web application.

In this step, we will modify the generated MyUI.java by displaying the view data from a data model class: InputData and using the controller class InputController to set the view data.

4.1 Data Model

Create an InputData model to capture the name from the user’s input and moreData from the back end service’s operation. It is the “Model” part of MVC.

InputData.java

package jcg.demo.model;

public class InputData {
	public InputData() {
		super();
	}

	public InputData(String name) {
		super();
		this.name = name;
	}

	private String name;
	private String moreData;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getMoreData() {
		return moreData;
	}

	public void setMoreData(String moreData) {
		this.moreData = moreData;
	}

}

4.2 Service

Create an UserDataService to look up the additional data based on the name. It will be used by the controller.

UserDataService.java

package jcg.demo.service;

import org.springframework.stereotype.Service;

import jcg.demo.model.InputData;

@Service
public class UserDataService {

	public InputData findData(String name) {
		InputData inputData = new InputData(name);
		switch (name) {
		case "mary":
			inputData.setMoreData("Zheng");
			break;
		case "tom":
			inputData.setMoreData("Johnson");
			break;
		default:
			inputData.setMoreData("Cool dude!");
		}

		return inputData;

	}

}

4.3 Controller

Create an InputController to return the InputData created at step 4.1 based on the user’s input. This is the “Controller” part of the MVC.

InputController.java

package jcg.demo.ui.vaadin;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import jcg.demo.model.InputData;
import jcg.demo.service.UserDataService;

@Component
public class InputController {

	@Autowired
	private UserDataService userService ;

	public InputData setUserInput(String value) {
		return userService.findData(value);
	}

}

4.4 View

Modify the generated MyUI.java to display the data from InputData created at step 4.1 which is set by the InputController created at  step 4.3. This is the “View” part of MVC.

MyUI.java

package jcg.demo.ui.vaadin;

import javax.servlet.annotation.WebServlet;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.Page;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.spring.server.SpringVaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

import jcg.demo.model.InputData;

/**
 * This UI is the application entry point. A UI may either represent a browser
 * window (or tab) or some part of an HTML page where a Vaadin application is
 * embedded.
 * 
 * The UI is initialized using {@link #init(VaadinRequest)}. This method is
 * intended to be overridden to add component to the user interface and
 * initialize non-component functionality.
 */
@SuppressWarnings("serial")
@Theme("mytheme")
@SpringUI(path = "/app")
public class MyUI extends UI {

	@Autowired
	private InputController controller;

	private InputData inputData;

	@Override
	protected void init(VaadinRequest vaadinRequest) {
		final VerticalLayout layout = new VerticalLayout();

		final TextField name = new TextField();
		name.setCaption("Type your name here:");
		
		Label sessionIdLabel = new Label();
		

		Label dataFromService_Name = new Label();
		dataFromService_Name.setVisible(false);
		
		Label dataFromService_more = new Label();
		dataFromService_more.setVisible(false);

		layout.addComponent(name);
		layout.addComponent(sessionIdLabel);
		layout.addComponent(dataFromService_Name);
		layout.addComponent(dataFromService_more);


		Button button = new Button("Click Me");
		button.addClickListener(e -> {
			inputData = controller.setUserInput(name.getValue());
			String sessionID = ((com.vaadin.server.VaadinServletRequest) VaadinService.getCurrentRequest())
					.getHttpServletRequest().getSession().getId();

			sessionIdLabel.setValue(sessionID);
			dataFromService_Name.setValue("Thanks, you entered: " + inputData.getName() );
			dataFromService_Name.setVisible(true);
			
			dataFromService_more.setValue("Thanks, it has more data: " + inputData.getMoreData() );
			dataFromService_more.setVisible(true);

		});

		layout.addComponent(button);

		setContent(layout);
	}

	@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
	@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
	public static class MyUIServlet extends SpringVaadinServlet {
	}
}

4.5 Demo Modified Vaadin Web Application

Execute Jetty:run and go to http://localhost:8080.

Enter a name and then click the Click Me button several times.

Figure 3 ClickMe

Note: Only the last entered data is displayed.

5. Secure the Web Application

If you already know how to use Spring Security, skip forward, if not, check it out here.

Spring Security framework has two key context objects which an application must interact with:

  • SecurityContextHolderSecurityContextHolder contains information about the current security context of the application, which includes detailed information about the user currently working with the application.
  • UserDetailsService – UserDetailsService is used to create a UserDetails object by implementing the single method of this interface: UserDetails loadUserByUsername (String username) throws UsernameNotFoundException

There are four steps needed to secure a web application with a login page via the Spring Security framework:

  1. The user logs in with a name and a password. These two credentials are combined into an instance of the class UsernamePasswordAuthenticationToken. Then, they are passed to the AuthenticationManager for verification.
  2. If the username does not match the password, the BadCredentialsException is returned along with the message “Bad Credentials.”
  3. If the username and password match, it will return a populated authentication instance.
  4. The user sets a security context by calling the SecurityContextHolder.getContext().setAuthentication() method, where the object that returned from authenticationProvider.authenticate() is passed.

5.1 Dependencies

Modify the POM.xml to add the Spring Security framework dependencies.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>jcg.demo.mary</groupId>
	<artifactId>vaadin-spring-security</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>Vaadin Spring Security Integration</name>

	<prerequisites>
		<maven>3</maven>
	</prerequisites>

	<properties>
		<spring.version>4.2.3.RELEASE</spring.version>
		<vaadin.version>8.1.5</vaadin.version>
		<vaadin.plugin.version>8.1.5</vaadin.plugin.version>
		<jetty.plugin.version>9.3.9.v20160517</jetty.plugin.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>	
		<vaadin.widgetset.mode>local</vaadin.widgetset.mode>
	</properties>

	<repositories>
		<repository>
			<id>vaadin-addons</id>
			<url>http://maven.vaadin.com/vaadin-addons</url>
		</repository>
	</repositories>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>com.vaadin</groupId>
				<artifactId>vaadin-bom</artifactId>
				<version>${vaadin.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>com.vaadin</groupId>
			<artifactId>vaadin-server</artifactId>
		</dependency>
		<dependency>
			<groupId>com.vaadin</groupId>
			<artifactId>vaadin-push</artifactId>
		</dependency>
		<dependency>
			<groupId>com.vaadin</groupId>
			<artifactId>vaadin-client-compiled</artifactId>
		</dependency>
		<dependency>
			<groupId>com.vaadin</groupId>
			<artifactId>vaadin-themes</artifactId>
		</dependency>
		
		<dependency>
			<groupId>com.vaadin</groupId>
			<artifactId>vaadin-spring</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.1</version>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.0.0</version>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
					<!-- Exclude an unnecessary file generated by the GWT compiler. -->
					<packagingExcludes>WEB-INF/classes/VAADIN/widgetsets/WEB-INF/**</packagingExcludes>
				</configuration>
			</plugin>
		
			
			<plugin>
				<groupId>com.vaadin</groupId>
				<artifactId>vaadin-maven-plugin</artifactId>
				<version>${vaadin.plugin.version}</version>
				<executions>
					<execution>
						<goals>
							<goal>update-theme</goal>
							<goal>update-widgetset</goal>
							<goal>compile</goal>
							<!-- Comment out compile-theme goal to use on-the-fly theme compilation -->
							<goal>compile-theme</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-clean-plugin</artifactId>
				<version>3.0.0</version>
				<!-- Clean up also any pre-compiled themes -->
				<configuration>
					<filesets>
						<fileset>
							<directory>src/main/webapp/VAADIN/themes</directory>
							<includes>
								<include>**/styles.css</include>
								<include>**/styles.scss.cache</include>
							</includes>
						</fileset>
					</filesets>
				</configuration>
			</plugin>

			<!-- The Jetty plugin allows us to easily test the development build by
				running jetty:run on the command line. -->
			<plugin>
				<groupId>org.eclipse.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>${jetty.plugin.version}</version>
				<configuration>
					<scanIntervalSeconds>2</scanIntervalSeconds>
				</configuration>
			</plugin>
			
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>tomcat-maven-plugin</artifactId>
				<version>1.0-beta-1</version>
			</plugin>
		</plugins>
	</build>

	<profiles>
		<profile>
			<!-- Vaadin pre-release repositories -->
			<id>vaadin-prerelease</id>
			<activation>
				<activeByDefault>false</activeByDefault>
			</activation>

			<repositories>
				<repository>
					<id>vaadin-prereleases</id>
					<url>http://maven.vaadin.com/vaadin-prereleases</url>
				</repository>
				<repository>
					<id>vaadin-snapshots</id>
					<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
					<releases>
						<enabled>false</enabled>
					</releases>
					<snapshots>
						<enabled>true</enabled>
					</snapshots>
				</repository>
			</repositories>
			<pluginRepositories>
				<pluginRepository>
					<id>vaadin-prereleases</id>
					<url>http://maven.vaadin.com/vaadin-prereleases</url>
				</pluginRepository>
				<pluginRepository>
					<id>vaadin-snapshots</id>
					<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
					<releases>
						<enabled>false</enabled>
					</releases>
					<snapshots>
						<enabled>true</enabled>
					</snapshots>
				</pluginRepository>
			</pluginRepositories>
		</profile>
	</profiles>

</project>

5.2 UserDetailsService Interface

Spring Security framework provides UserDetailsService interface which allows an application to implement the loadUserByUsername method. Create AuthUserDetailsService by implementing UserDetailsService's loadUserByUsername.

AuthUserDetailsService.java

package jcg.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;


@Service
public class AuthUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
	
	@Autowired
	private AuthUserLookUpService userLookupService;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		
		return userLookupService.findUser(username);
	}

}

5.3 Service

Create an AuthUserLookUpService to set up two users for the web application.

AuthUserLookUpService.java

package jcg.demo.service;

import org.springframework.stereotype.Service;

import jcg.demo.model.User;

@Service
public class AuthUserLookUpService {

	User findUser(String username) {
		User found = null;
		switch (username) {
		case "admin":
			found = new User("admin", "admin");
			break;
		case "mzheng":
			found = new User("mzheng", "great");
			break;
		}
		return found;
	}

}

5.4 Login Page

Create a LoginUI which allows users to enter their username and password and authenticate it before continuing onto the main application. The AuthenticationProvider is utilized to authenticate the user, and if the user passes the authentication, then they are directed to the main application page, otherwise display the error message.

LoginUI.java

package jcg.demo.ui.vaadin;

import java.net.MalformedURLException;
import java.net.URI;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

import com.vaadin.annotations.Theme;
import com.vaadin.server.Page;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinServletRequest;
import com.vaadin.server.VaadinServletResponse;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.LoginForm;
import com.vaadin.ui.Notification;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

/**
 * This is the form login page.
 * 
 *
 */
@SuppressWarnings("serial")
@Theme("mytheme")
@SpringUI(path = "/login")
public class LoginUI extends UI {
	
	@Autowired
    private AuthenticationProvider authenticationProvider;
	
	@Autowired
	SessionAuthenticationStrategy sessionAuthenticationStrategy;	

    @Override
    protected void init(final VaadinRequest request) {    	
    	
    	if (!(SecurityContextHolder.getContext().getAuthentication() instanceof AnonymousAuthenticationToken))
    	{
    		URI currentLoc = Page.getCurrent().getLocation();
    		try {
				Page.getCurrent().setLocation(  currentLoc.toURL().toString().replace("/login", "/app"));
			} catch (MalformedURLException e1) {				
				e1.printStackTrace();
			}
    		return;
    	}
    	
    	VerticalLayout vl = new VerticalLayout();
    	LoginForm lf = new LoginForm();
    	lf.addLoginListener(e -> {
            final Authentication auth = new UsernamePasswordAuthenticationToken(e.getLoginParameter("username"), e.getLoginParameter("password"));
            try {
            	// this is the code for achieving the spring security authentication in a programmatic way
                final Authentication authenticated = authenticationProvider.authenticate(auth);
                SecurityContextHolder.getContext().setAuthentication(authenticated);
                sessionAuthenticationStrategy.onAuthentication(auth, ((VaadinServletRequest)VaadinService.getCurrentRequest()).getHttpServletRequest(), ((VaadinServletResponse)VaadinService.getCurrentResponse()).getHttpServletResponse());
                URI currentLoc = Page.getCurrent().getLocation();
        		try {
    				Page.getCurrent().setLocation(  currentLoc.toURL().toString().replace("/login", "/app"));
    			} catch (MalformedURLException e1) {    				
    				e1.printStackTrace();
    			}
            } catch (final AuthenticationException ex) {
            	String message = "Incorrect user or password:" + ex.getMessage() + e.getLoginParameter("username") + ":" + e.getLoginParameter("password");
            	Notification.show(message, Notification.Type.ERROR_MESSAGE);
            }

    	});
    	
    	vl.addComponent(lf);
    	vl.setComponentAlignment(lf, Alignment.MIDDLE_CENTER);
    	vl.setSizeFull();
    	setContent(vl);
    }

}
  • line 61: create UsernamePasswordAuthenticationToken from the login form
  • line 64: invoke authenticationProvider.authenticate for the username and password
  • line 65: set SecurityContextHolder.getContext().setAuthentication

5.5 Security Configuration

Spring Security framework provides WebSecurityConfigurerAdapter to allow an application to configure the security.
In this step, we will create a SecurityConfig class by extending it from WebSecurityConfigurerAdapter, and overriding the configure method to specify the LoginUI as the loginPage.

SecurityConfig.java

package jcg.demo.spring;

import java.util.LinkedList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;

import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;

import com.vaadin.spring.annotation.EnableVaadin;

/**
 * This class is the main security configuration class. For more information of
 * how to configure the security, go to
 * https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html
 * 
 *
 */
@EnableWebSecurity
@Configuration
@ComponentScan(basePackages = { "jcg.demo" })
@EnableVaadin
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(authenticationProvider());
	}

	@Bean
	public DaoAuthenticationProvider authenticationProvider() {
		final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
		authProvider.setUserDetailsService(userDetailsService);
		authProvider.setPasswordEncoder(encoder());
		return authProvider;
	}

	@Override
	protected void configure(final HttpSecurity http) throws Exception {
		http
			.csrf().disable()
			.authorizeRequests()
			  .antMatchers("/VAADIN/**", "/HEARTBEAT/**", "/UIDL/**", "/resources/**", "/login", "/login**", "/login/**").permitAll()
			.anyRequest().authenticated()
			  .antMatchers("/app").access("hasAuthority('USE-APP-ROLE')").and()
			.formLogin().loginPage("/login?auth").permitAll().defaultSuccessUrl("/app", true).and()
			.sessionManagement().sessionAuthenticationStrategy(sessionControlAuthenticationStrategy());	
	}
	
    @Bean
    public SessionAuthenticationStrategy sessionControlAuthenticationStrategy(){
        SessionFixationProtectionStrategy sessionFixationProtectionStrategy = new SessionFixationProtectionStrategy();
        sessionFixationProtectionStrategy.setMigrateSessionAttributes(false);

        RegisterSessionAuthenticationStrategy registerSessionAuthenticationStrategy = new RegisterSessionAuthenticationStrategy(sessionRegistry());

        List strategies = new LinkedList();
        strategies.add(sessionFixationProtectionStrategy);
        strategies.add(registerSessionAuthenticationStrategy);

        CompositeSessionAuthenticationStrategy compositeSessionAuthenticationStrategy = new CompositeSessionAuthenticationStrategy(strategies);

        return compositeSessionAuthenticationStrategy;
    }

	@Bean
	public SessionRegistry sessionRegistry() {
		SessionRegistry sessionRegistry = new SessionRegistryImpl();
		return sessionRegistry;
	}

	public PasswordEncoder encoder() {
		return NoOpPasswordEncoder.getInstance();
	}

}
  • line 42: Auto wire UserDetailsService created at step 5.2
  • line 52: Use the UserDetailsService created at line 42 in the authProvider
  • line 58-66: Configure the HttpSecurity

5.6 Initialize Security Web Application

Spring Security framework provides AbstractSecurityWebApplicationInitializer to allow a web application to initialize the Spring Security context. In this step, we will initialize the Spring Security context with the SecurityConfig created at Step 5.3.

SecurityWebApplicationInitializer.java

package jcg.demo.spring;

import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.security.web.session.HttpSessionEventPublisher;

/**
 * This class will initialize the spring security framework 
 *
 */
@WebListener
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

	public SecurityWebApplicationInitializer() {
		super(SecurityConfig.class);
		
	}
	
	@Override
	protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
		super.beforeSpringSecurityFilterChain(servletContext);
		servletContext.addListener(new HttpSessionEventPublisher());
	}
	
}
  • line 17: set SecurityConfig at the SecurityWebApplicationInitializer

5.7 Modify View to Add a Logout Button

Modify MyUI.java to add a logout button, which will invalidate the current session, and redirect the page to the root of the application.

MyUI .java

package jcg.demo.ui.vaadin;

import javax.servlet.annotation.WebServlet;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.Page;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.spring.server.SpringVaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

import jcg.demo.model.InputData;

/**
 * This UI is the application entry point. A UI may either represent a browser
 * window (or tab) or some part of an HTML page where a Vaadin application is
 * embedded.
 * 
 * The UI is initialized using {@link #init(VaadinRequest)}. This method is
 * intended to be overridden to add component to the user interface and
 * initialize non-component functionality.
 */
@SuppressWarnings("serial")
@Theme("mytheme")
@SpringUI(path = "/app")
public class MyUI extends UI {

	@Autowired
	private InputController controller;

	private InputData inputData;

	@Override
	protected void init(VaadinRequest vaadinRequest) {
		final VerticalLayout layout = new VerticalLayout();

		final TextField name = new TextField();
		name.setCaption("Type your name here:");
		
		Label sessionIdLabel = new Label();
		

		Label dataFromService_Name = new Label();
		dataFromService_Name.setVisible(false);
		
		Label dataFromService_more = new Label();
		dataFromService_more.setVisible(false);

		layout.addComponent(name);
		layout.addComponent(sessionIdLabel);
		layout.addComponent(dataFromService_Name);
		layout.addComponent(dataFromService_more);


		Button button = new Button("Click Me");
		button.addClickListener(e -> {
			inputData = controller.setUserInput(name.getValue());
			String sessionID = ((com.vaadin.server.VaadinServletRequest) VaadinService.getCurrentRequest())
					.getHttpServletRequest().getSession().getId();

			sessionIdLabel.setValue(sessionID);
			dataFromService_Name.setValue("Thanks, you entered: " + inputData.getName() );
			dataFromService_Name.setVisible(true);
			
			dataFromService_more.setValue("Thanks, it has more data: " + inputData.getMoreData() );
			dataFromService_more.setVisible(true);

		});

		layout.addComponent(button);
		Button logout = new Button("Logout");
		logout.addClickListener(e -> {
			VaadinService.getCurrentRequest().getWrappedSession().invalidate();
			new SecurityContextLogoutHandler()
					.logout(((com.vaadin.server.VaadinServletRequest) VaadinService.getCurrentRequest())
							.getHttpServletRequest(), null, null);
			Page.getCurrent().setLocation("/");
		});

		layout.addComponent(logout);

		setContent(layout);
	}

	@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
	@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
	public static class MyUIServlet extends SpringVaadinServlet {
	}
}
  • line 82-86: Add a logout button to invalidate the session.

6. Demo Secured Vaadin Web Application

Execute Jetty:run and go to http://localhost:8080. Enter your valid username and password.

Figure 4 login

Enter a name and then click the Click Me button several times.

Note: For different outputs, enter different names.

Click the logout button. Enter an invalid username and password.

Figure 6 bad login

Note: Enter your valid username again, then verify that the SessionId value is updated.

7. Summary

In this example, we built a web application and secured it with a login page. The web application was created via the Vaadin Maven archetype and then modified with the MVC pattern, and then added a login page via Spring Security framework.

8. Download the Source Code

This example consists of a secured Vaadin web application.

Download
You can download the full source code of this example here: Vaadin Spring Security Example

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
mukta
mukta
3 years ago

how can we achieve this integrated with spring boot

Back to top button