Vaadin Login Example
A login form is used to process user authentication in a web application.Ccontrary to web pages, web applications could be a complex piece of software and the public nature of the Internet make the user authentication a vital part of any web application. In this example I am going to show you how to implement a login form in Vaadin to protect the secure part of a web application.
1. The tools
- Java JDK 8
- Latest Eclipse Mars
- Vaadin 7.6.4
- Tomcat Server 8
2. Introduction
A login is a special form that collects user credentials and communicates with an authentication mechanism to check the validity of the credentials, then if the credentials are valid the user is routed to a secure area else if the credentials are invalid the page keep asking for a valid input, there are a multiples approach to this problem.
You can define the number of times a user can retry for invalid credentials from a single username, a browser user-agent, an IP, a mac address, cookies and other user guessing identification methods, you can use MD5 to hash your credentials but the security of the MD5 hash function is severely compromised. The best approach nowadays is to use the HTTP over SSL (https) protocol that create a secure channel over an insecure network for your web application. Vaadin offers a navigation mechanism to manage your views, using that I constructed the login form, in older versions of Vaadin have a LoginForm class but from version 7 and up LoginForm is deprecated.
3. Prerequisites
- JDK installed
- Eclipse Mars installed and working
- Vaadin 7.6.4 plugin installed
- Tomcat 8 installed and running
4. Set up the project
In the file menu choose File -> New -> Other:
Now from the list choose Vaadin 7 project:
Hit next and name your project then hit finish.
5. Coding the example
5.1 Mockup Authentication class
Lets create a mockup authentication class that handle the backend authentication with only one user, you can change it to any needed mechanism.
Authentication.java
package com.example.vaadinlogin; public class Authentication { private String username; private String password; public Authentication() { setUsername("myuser"); setPassword("mypass"); } private void setUsername(String username) { this.username = username; } private String getUsername(){ return this.username; } private void setPassword(String password) { this.password = password; } private String getPassword(){ return this.password; } public Boolean authenticate(String username, String password){ if(username.equals(getUsername()) && password.equals(getPassword())){ return true; } return false; } }
This class have two private String
fields for the user and the password, also have the boilerplate setter and getter and a method public Boolean authenticate(String username, String password)
that receive two strings as parameters and returns true if the credentials correspond with the mockup user created, here you have space to plug a database or whatever backend you choose to store your users credentials.
5.2 Login Page
Let’s build the login page:
Layout
public class LoginPage extends VerticalLayout implements View { private static final long serialVersionUID = 1L; public static final String NAME = "";
The login page extends VerticalLayout
to place our components and implements View
to use a Navigator
, more on this later, and have a field NAME
to use also in the navigation flow of the application.
Login Panel
Panel panel = new Panel("Login"); panel.setSizeUndefined(); addComponent(panel);
The fields of the login form are inside a Panel
so create the panel and add it to the layout.
Login Form
FormLayout content = new FormLayout(); TextField username = new TextField("Username"); content.addComponent(username); PasswordField password = new PasswordField("Password"); content.addComponent(password);
Inside the panel there is a form with a TextField
for the username, a PasswordField
that is a special kind of text field that allows to hide the user input, the password field is used for the password, I add both fields to the FormLayout content
.
Button Send
Button send = new Button("Enter"); send.addClickListener(new ClickListener() { private static final long serialVersionUID = 1L; @Override public void buttonClick(ClickEvent event) { if(VaadinloginUI.AUTH.authenticate(username.getValue(), password.getValue())){ VaadinSession.getCurrent().setAttribute("user", username.getValue()); getUI().getNavigator().addView(SecurePage.NAME, SecurePage.class); getUI().getNavigator().addView(OtherSecurePage.NAME, OtherSecurePage.class); Page.getCurrent().setUriFragment("!"+SecurePage.NAME); }else{ Notification.show("Invalid credentials", Notification.Type.ERROR_MESSAGE); } } });
I created a Button
to check the credentials against the mockup authentication class. The button have a ClickListener()
and when the button is pressed, the following things happen: VaadinloginUI.AUTH.authenticate
check the credentials, if the credentials are incorrect a Notification
is show, is better not to specify what credential is wrong so is a general message with invalid credentials, if the credentials are correct then set the user in the session to keep logged in between page refresh, next add two more views to the navigator. These view are private and you need to be logged to access them and last redirect the page using setting a uri fragment. In this case the uri fragment correspond to the last part of the uri after the “#” character, so I am using the name defined in the page to navigate to that page.
Add components
content.addComponent(send); content.setSizeUndefined(); content.setMargin(true); panel.setContent(content); setComponentAlignment(panel, Alignment.MIDDLE_CENTER);
Add the components to the layout and set the panel alignment to the center as is usual in login forms but you can put it on any place of the page.
5.3 Secure page
The secure page is a page to show the private area behavior, in the private area you can have all the pages you need.
The layout
public class SecurePage extends VerticalLayout implements View { private static final long serialVersionUID = 1L; private Label secure; private Label currentUser; private Button otherSecure; private Button logout; public static final String NAME = "Secure";
As before this page use a vertical layout to put the components, it have two labels and two buttons, the label currentUser
is used to show the user logged in the application, it have a button to go to the other secure page and a button to logout.
otherSecure Button
otherSecure = new Button("OtherSecure"); otherSecure.addClickListener(new ClickListener() { private static final long serialVersionUID = 1L; @Override public void buttonClick(ClickEvent event) { Page.getCurrent().setUriFragment("!"+OtherSecurePage.NAME); } });
The otherSecure
button is used to navigate to the other private page used in this example, the navigation is done using Page.getCurrent().setUriFragment("!"+OtherSecurePage.NAME);
that change the uri fragment as before.
Logout button
logout = new Button("Logout"); logout.addClickListener(new ClickListener() { private static final long serialVersionUID = 1L; @Override public void buttonClick(ClickEvent event) { getUI().getNavigator().removeView(SecurePage.NAME); getUI().getNavigator().removeView(OtherSecurePage.NAME); VaadinSession.getCurrent().setAttribute("user", null); Page.getCurrent().setUriFragment(""); } });
The logout button first remove the secure page view with getUI().getNavigator().removeView(SecurePage.NAME);
, remove the other secure page with getUI().getNavigator().removeView(OtherSecurePage.NAME);
, clean the session with VaadinSession.getCurrent().setAttribute("user", null);
and clean the uri fragment to change the current page to the login page, removing the views prevent it to be accessible from the navigator.
Place the components
secure = new Label("secure"); currentUser = new Label("Current User"); addComponent(secure); addComponent(currentUser); addComponent(otherSecure); addComponent(logout);
Add the components to the layout to show them.
enter
@Override public void enter(ViewChangeEvent event) { currentUser.setCaption("Current user : " + VaadinSession.getCurrent().getAttribute("user").toString()); }
This event is always called before the view is shown to the screen, here I get the username from the session and show it in the currentUser label.
5.4 OtherSecure page
OtherSecure page
public class OtherSecurePage extends VerticalLayout implements View{ private static final long serialVersionUID = 1L; private Label otherSecure; public static final String NAME = "OtherSecure"; private Button mainsecure; public OtherSecurePage() { mainsecure = new Button("Main Secure Area"); mainsecure.addClickListener(new ClickListener() { private static final long serialVersionUID = 1L; @Override public void buttonClick(ClickEvent event) { Page.getCurrent().setUriFragment("!"+SecurePage.NAME); } }); otherSecure = new Label("Other Secure Page ..."); addComponent(otherSecure); addComponent(mainsecure); } @Override public void enter(ViewChangeEvent event) { } }
This page only have one button that send the user to the secure page, is made here to the purpose of show how to handle multiples pages inside the secure area, usually is a better idea to make a navigation menu and inside the navigation menu put all your pages, to avoid make a navigation widget for every page but everything here depends on your design, remember if the design is bad the rest is bad too so a solid rock design is the bullet proof foundation of your program.
5.5 The main UI
Init
public static Authentication AUTH; @Override protected void init(VaadinRequest request) { AUTH = new Authentication(); new Navigator(this, this);
Ok here I declared an static AUTH
variable to store an instance of the Authentication
class and create a Navigator(this, this)
, the navigator is a class that tracks the active view using URI fragments, is in charge of show the current view and store a record and change the other views.
Initial page
getNavigator().addView(LoginPage.NAME, LoginPage.class); getNavigator().setErrorView(LoginPage.class);
Using the navigator, add the login page as a view and set the error page, the error view is a page that is redirected when an error occurs in the application. In this case I used the same login page as an error view, just to no create another dummy page and the login page is public and if any error raise in the application I don’t want a user hanging in the private area, the error page is a design topic, where you need it and how.
Uri fragment listener
Page.getCurrent().addUriFragmentChangedListener(new UriFragmentChangedListener() { @Override public void uriFragmentChanged(UriFragmentChangedEvent event) { router(event.getUriFragment()); } }); router("");
To know when the uri fragment changes we have this listener so every time the uri fragment is modified the router function is called with the new uri fragment as a parameter, and finally I call the router with a empty string to redirect the page to the login page.
router method
private void router(String route){ Notification.show(route); if(getSession().getAttribute("user") != null){ getNavigator().addView(SecurePage.NAME, SecurePage.class); getNavigator().addView(OtherSecurePage.NAME, OtherSecurePage.class); if(route.equals("!OtherSecure")){ getNavigator().navigateTo(OtherSecurePage.NAME); }else{ getNavigator().navigateTo(SecurePage.NAME); } }else{ getNavigator().navigateTo(LoginPage.NAME); } }
This router method is in charge of the page flow. Every time the uri changes the application call this method and using the navigator change the page to the correct one. If the user is no logged and try to navigate to an in-existent page this router shows the login page and in the case the user is logged in the the router navigate to the secure page by default, so it checks the session with if(getSession().getAttribute("user") != null)
. If no user is in the session then show the login page and if the user is in the session navigate to the secure area, this can be done because the session is stored server side and because you are handling the user in the server is safe to navigate in this way. You can use cookies for user authentication too but is not recommended because cookies are stored client side and can be forged by malicious users.
6. The complete source code
VaadinloginUI.java
package com.example.vaadinlogin; import javax.servlet.annotation.WebServlet; import com.vaadin.annotations.Theme; import com.vaadin.annotations.VaadinServletConfiguration; import com.vaadin.navigator.Navigator; import com.vaadin.server.Page; import com.vaadin.server.Page.UriFragmentChangedEvent; import com.vaadin.server.Page.UriFragmentChangedListener; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinServlet; import com.vaadin.ui.Notification; import com.vaadin.ui.UI; @SuppressWarnings("serial") @Theme("vaadinlogin") public class VaadinloginUI extends UI { @WebServlet(value = "/*", asyncSupported = true) @VaadinServletConfiguration(productionMode = false, ui = VaadinloginUI.class) public static class Servlet extends VaadinServlet { } public static Authentication AUTH; @Override protected void init(VaadinRequest request) { AUTH = new Authentication(); new Navigator(this, this); getNavigator().addView(LoginPage.NAME, LoginPage.class); getNavigator().setErrorView(LoginPage.class); Page.getCurrent().addUriFragmentChangedListener(new UriFragmentChangedListener() { @Override public void uriFragmentChanged(UriFragmentChangedEvent event) { router(event.getUriFragment()); } }); router(""); } private void router(String route){ Notification.show(route); if(getSession().getAttribute("user") != null){ getNavigator().addView(SecurePage.NAME, SecurePage.class); getNavigator().addView(OtherSecurePage.NAME, OtherSecurePage.class); if(route.equals("!OtherSecure")){ getNavigator().navigateTo(OtherSecurePage.NAME); }else{ getNavigator().navigateTo(SecurePage.NAME); } }else{ getNavigator().navigateTo(LoginPage.NAME); } } }
LoginPage.java
package com.example.vaadinlogin; import com.vaadin.navigator.View; import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.server.Page; import com.vaadin.server.VaadinSession; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.FormLayout; import com.vaadin.ui.Notification; import com.vaadin.ui.Panel; import com.vaadin.ui.PasswordField; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; public class LoginPage extends VerticalLayout implements View { private static final long serialVersionUID = 1L; public static final String NAME = ""; public LoginPage(){ Panel panel = new Panel("Login"); panel.setSizeUndefined(); addComponent(panel); FormLayout content = new FormLayout(); TextField username = new TextField("Username"); content.addComponent(username); PasswordField password = new PasswordField("Password"); content.addComponent(password); Button send = new Button("Enter"); send.addClickListener(new ClickListener() { private static final long serialVersionUID = 1L; @Override public void buttonClick(ClickEvent event) { if(VaadinloginUI.AUTH.authenticate(username.getValue(), password.getValue())){ VaadinSession.getCurrent().setAttribute("user", username.getValue()); getUI().getNavigator().addView(SecurePage.NAME, SecurePage.class); getUI().getNavigator().addView(OtherSecurePage.NAME, OtherSecurePage.class); Page.getCurrent().setUriFragment("!"+SecurePage.NAME); }else{ Notification.show("Invalid credentials", Notification.Type.ERROR_MESSAGE); } } }); content.addComponent(send); content.setSizeUndefined(); content.setMargin(true); panel.setContent(content); setComponentAlignment(panel, Alignment.MIDDLE_CENTER); } @Override public void enter(ViewChangeEvent event) { } }
SecurePage.java
package com.example.vaadinlogin; import com.vaadin.navigator.View; import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.server.Page; import com.vaadin.server.VaadinSession; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; public class SecurePage extends VerticalLayout implements View { private static final long serialVersionUID = 1L; private Label secure; private Label currentUser; private Button otherSecure; private Button logout; public static final String NAME = "Secure"; public SecurePage() { otherSecure = new Button("OtherSecure"); otherSecure.addClickListener(new ClickListener() { private static final long serialVersionUID = 1L; @Override public void buttonClick(ClickEvent event) { Page.getCurrent().setUriFragment("!"+OtherSecurePage.NAME); } }); logout = new Button("Logout"); logout.addClickListener(new ClickListener() { private static final long serialVersionUID = 1L; @Override public void buttonClick(ClickEvent event) { getUI().getNavigator().removeView(SecurePage.NAME); getUI().getNavigator().removeView(OtherSecurePage.NAME); VaadinSession.getCurrent().setAttribute("user", null); Page.getCurrent().setUriFragment(""); } }); secure = new Label("secure"); currentUser = new Label("Current User"); addComponent(secure); addComponent(currentUser); addComponent(otherSecure); addComponent(logout); } @Override public void enter(ViewChangeEvent event) { currentUser.setCaption("Current user : " + VaadinSession.getCurrent().getAttribute("user").toString()); } }
OtherSecurePage.java
package com.example.vaadinlogin; import com.vaadin.navigator.View; import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; import com.vaadin.server.Page; import com.vaadin.ui.Button; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; public class OtherSecurePage extends VerticalLayout implements View{ private static final long serialVersionUID = 1L; private Label otherSecure; public static final String NAME = "OtherSecure"; private Button mainsecure; public OtherSecurePage() { mainsecure = new Button("Main Secure Area"); mainsecure.addClickListener(new ClickListener() { private static final long serialVersionUID = 1L; @Override public void buttonClick(ClickEvent event) { Page.getCurrent().setUriFragment("!"+SecurePage.NAME); } }); otherSecure = new Label("Other Secure Page ..."); addComponent(otherSecure); addComponent(mainsecure); } @Override public void enter(ViewChangeEvent event) { } }
Authentication.java
package com.example.vaadinlogin; public class Authentication { private String username; private String password; public Authentication() { setUsername("myuser"); setPassword("mypass"); } private void setUsername(String username) { this.username = username; } private String getUsername(){ return this.username; } private void setPassword(String password) { this.password = password; } private String getPassword(){ return this.password; } public Boolean authenticate(String username, String password){ if(username.equals(getUsername()) && password.equals(getPassword())){ return true; } return false; } }
7. Running the example
Right click on the project folder and choose Run as -> Run on server choose Tomcat 8 server and hit finish.
8. Results
Failed Login:
Filled Login:
Secure Page:
The Other Secure Page:
9. Download the Source Code
This was an example of Vaadin Login.
You can download the Eclipse project here: VaadinLogin
Hello,
I tried this example because it’s understandable and transparent to me. (I’m beginner in Java). And it’s worked fine. So I want to improve it and combined it with Spring boot. The Authentication class is directly injected into the LoginPage class. But it does not work. It throws a NullPointerException. The final goal is its use with JPA.
I upload my code to GitHub here: https://github.com/korponaiz/test-vaadin-spring-login