Implementing a simple LoginApp with Vaadin

If you have a small vaadin application you might want to avoid using complex authentication and authentifaction frameworks like Spring. In this post I want to show a small vaadin application with authentication functionality only using views. This solution might not fullfill complex authorization or authentication functionalities, but it is a simple ways to secure a vaadin interface. The whole application is available on github.

First we need an object which represents an user and some sort of repository where we can store the user object:

public class User implements Serializable {

  /**
   * 
   */
  private static final long serialVersionUID = 4753508683469842961L;
  
  private String name;
  private String username;
  private String password;
  
  public User() {
    this.name = "";
    this.username = "";
    this.password = "";
  }
  
  public User(String name, String username, String password) {
    this.name = name;
    this.username = username;
    this.password = password;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((password == null) ? 0 : password.hashCode());
    result = prime * result + ((username == null) ? 0 : username.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    User other = (User) obj;
    if (password == null) {
      if (other.password != null)
        return false;
    } else if (!password.equals(other.password))
      return false;
    if (username == null) {
      if (other.username != null)
        return false;
    } else if (!username.equals(other.username))
      return false;
    return true;
  }

  public String getName() {
    return name;
  }

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

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }
  
}

The repository class:

public class UserRepository {
  
  private static HashSet<User> getUsers() {
    HashSet<User> users = new HashSet<User>();
    users.add(new User("John Doe", "john", "amanda"));
    users.add(new User("Amanda Doe", "amanda", "john"));
    
    return users;
  }
  
  public static User authenticate(String username, String password) {
    // We simple create a new user with this credentials and check the hash set if exists.
    User userToCheck = new User("", username, password);
    
    for (Iterator<User> it = getUsers().iterator(); it.hasNext(); ) {
          User f = it.next();
          if (f.equals(userToCheck))
              return f;
      }
    
    return null;
  }
}

The User class implements a simple object which represents a User for login, consisting of name, username and password. This is just an example therefore we will not store the password crypted, never do this in a productive environment! We use username and password to check if an user is equal, or not.

The UserRepository class should represents a database with stores users, in this case we create two user “John” Doe” and “Amanda Doe” and if we call the autheticate class we simple iterate through the created users until we found the one with the given username and password and return him/her.

This two classes are implementing the environment, we need to implement our goal.

The main application consits of four classes:

  • LoginView: Represents the application view before the user has logged in.
  • MainView: The application view after login
  • SimpleLoginApp: The servlet which navigates between the two views.
  • LoginAppUiController: A simple controller which implements the login logic.

I will start with the the two Views:

public class LoginView extends VerticalLayout implements View {

  /**
   * 
   */
  private static final long serialVersionUID = 3070811273936236288L;
  public static String VIEWNAME = "login";
  
  public LoginView(LoginAppUiController uicontroller) {
    this.setLocale(UI.getCurrent().getLocale());
    this.setSizeFull();
    this.setSpacing(false);
    this.setMargin(false);
    
    VerticalLayout loginWrapper = new VerticalLayout();
    loginWrapper.setWidth(500, Unit.PIXELS);
    loginWrapper.setSpacing(true);
    this.addComponent(loginWrapper);
    this.setComponentAlignment(loginWrapper, Alignment.MIDDLE_CENTER);
    
    TextField username = new TextField();
    username.setWidth(100, Unit.PERCENTAGE);
    username.setPlaceholder("Username");
    username.setIcon(VaadinIcons.USER);
    username.setStyleName("inline-icon");
    username.focus();
    loginWrapper.addComponent(username);
    
    PasswordField password = new PasswordField();
    password.setWidth(100, Unit.PERCENTAGE);
    password.setPlaceholder("Password");
    password.setIcon(VaadinIcons.LOCK);
    password.setStyleName("inline-icon");
    loginWrapper.addComponent(password);
    
    NativeButton loginButton = new NativeButton("Login");
    loginWrapper.addComponent(loginButton);
    loginWrapper.setComponentAlignment(loginButton, Alignment.MIDDLE_CENTER);
    loginButton.addClickListener(listener -> uicontroller.login(username, password));
    loginButton.addShortcutListener(new ShortcutListener("", ShortcutAction.KeyCode.ENTER, null) {

      /**
       * 
       */
      private static final long serialVersionUID = 8664364871536035397L;

      @Override
      public void handleAction(Object sender, Object target) {
        uicontroller.login(username, password);
      }});
  }
  
  @Override
  public void enter(ViewChangeEvent event) {
    Page.getCurrent().setTitle("SimpleLoginApp - Login");
    }
}

Some remarks to the listing above:

  • public class LoginView extends VerticalLayout implements View: This class should implement a vaadin view, therefore we must implemente the View interface.
  • public static String VIEWNAME: This constant is quit important, because if defines the navigation name for this view. We will need the name in the SimpleLoginApp servlet.
  •  If he/she presses on login the LoginAppUiController is called to verify the credentials and to continue.
public class MainView extends VerticalLayout implements View {

  /**
   * 
   */
  private static final long serialVersionUID = 3070811273936236288L;
  public static String VIEWNAME = "main";
  
  public MainView(LoginAppUiController uicontroller) {
    this.setLocale(UI.getCurrent().getLocale());
    this.setSizeFull();
    this.setSpacing(false);
    this.setMargin(false);
    
    VerticalLayout wrapper = new VerticalLayout();
    wrapper.setWidth(500, Unit.PIXELS);
    wrapper.setSpacing(true);
    this.addComponent(wrapper);
    this.setComponentAlignment(wrapper, Alignment.MIDDLE_CENTER);
    
    Label info = new Label("This is the main view of our application, you can reload and close the browser tab, until the session"
        + " does not expire, or you press the logout button, you will always stay on this view.");
    info.setContentMode(ContentMode.HTML); // Set to html to allow line breaks.
    info.setWidth(100, Unit.PERCENTAGE);
    wrapper.addComponent(info);
    
    NativeButton logoutButton = new NativeButton("Logout");
    wrapper.addComponent(logoutButton);
    wrapper.setComponentAlignment(logoutButton, Alignment.MIDDLE_CENTER);
    logoutButton.addClickListener(listener -> uicontroller.logout());
  }
  
  @Override
  public void enter(ViewChangeEvent event) {
    Page.getCurrent().setTitle("SimpleLoginApp - Main");
    }
}

Like the listing of the LoginView, we need to implemente the View interface for the MainView as well. Apart of this, we only have a NativeButton which calls the logout method of the LoginAppUiController if pressed.

The next listing represents the LoginAppUiController, which implements the login logic of the application:

public class LoginAppUiController implements Serializable {

  /**
   * 
   */
  private static final long serialVersionUID = -7570570535339156683L;
  public static String SESSION_ATTRIBUTE = "loginAppUiController";
  
  private User user; // The current user of this instance of the application
  private SimpleLoginApp app; // Our application class
  
  public LoginAppUiController(SimpleLoginApp app) {
    this.app = app;
  }
  
  public void login(TextField username, PasswordField password) {
    // If the username and password combination exists the repository will return the user
    this.user = UserRepository.authenticate(username.getValue(), password.getValue());
    
    // The user has been authenticated therefore we continue to the main application view.
    if(this.user != null) {
      // After login you need to add the current controller to the session.
      UI.getCurrent().getSession().setAttribute(SESSION_ATTRIBUTE, this);
    
      // Finally navigate to the main view of the application
      this.app.getNavigator().navigateTo(MainView.VIEWNAME);
    } else {
      Notification.show("This combination of username and password does not exists", Type.ERROR_MESSAGE);
      username.clear();
      password.clear();
      username.focus();
    }
  }
  
  /**
   * This method checks if the {@link User} is authenticated.
   * @return
   */
  public boolean isAuthenticated() {
    if(user != null)
      return true;
    else
      return false;
  }
  
  /**
   * This method implements the logout procedure of the application
   */
  public void logout() {
    // Close the session, this means we destroy the session including this instance of the LoginAppUiController
        UI.getCurrent().getSession().close();
    
        // Go to the login page 
        UI.getCurrent().getPage().reload();
  }
}

Let us go a little bit more into detail of some aspects of this class:

  • public static String SESSION_ATTRIBUTE: We will store the whole LoginAppUiController as session attribute in the vaadin session, under this name.
  • private SimpleLoginApp app: Reference to the servlet which implements the navigation for the two views.
  • public void login(TextField username, PasswordField password): Performs the validation of the user credentials, called from the LoginView. If the credentials are confirmed we add the instance of this LoginAppUiController to the Vaadin session and navigate to the MainView. Otherwise we clear the username and password field and prompt the user to enter its credentials again.
  • public boolean isAuthenticated(): This method checks if the user is authenticated or not.
  • public void logout(): This method is called from the MainView of the application, after the logout button is pressed. It closes the current Vaddin session and forces a reload of the whole UI of the application.

The last class is the servlet which creates a Vaadin Navigator and implements the java servlet:

@Theme("mytheme")
public class SimpleLoginApp extends UI {
  
  /**
   * 
   */
  private static final long serialVersionUID = -8272040115893003478L;
  
  private Navigator navigator;

    @Override
    protected void init(VaadinRequest vaadinRequest) {
    		//
    // Create an instance of the LoginAppUiController and add it to the session
    //
    LoginAppUiController uicontroller;
    
    // If a session already exists, there must be an attribute containing the LoginAppUiController
    if(UI.getCurrent().getSession().getAttribute(LoginAppUiController.SESSION_ATTRIBUTE) != null)
      uicontroller = (LoginAppUiController) UI.getCurrent().getSession().getAttribute(LoginAppUiController.SESSION_ATTRIBUTE);
    // Create a new instance of the FrontendUiController, which means the user must enter his/her credentials
    else
      uicontroller = new LoginAppUiController(this);
    
    //
    // Create the navigator
    //
    this.navigator = new Navigator(this, this);
    this.navigator.addView(LoginView.VIEWNAME, new LoginView(uicontroller));
    this.navigator.addView(MainView.VIEWNAME, new MainView(uicontroller));
    
    //
    // Enter the right view
    //
    if(uicontroller.isAuthenticated())
      this.navigator.navigateTo(MainView.VIEWNAME);
    else
      this.navigator.navigateTo(LoginView.VIEWNAME);
    }

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

    /**
     * 
     */
    private static final long serialVersionUID = 4683186830720335264L;
    }
}
  • This class extends Vaadin UI and therefore implements the method init() and an internal class which extends VaadinServlet.
  • In line 15-24 we create an instance of the LoginAppUiController. First we check if the current session has already an instance of the controller by checking if the session attribute loginAppUiController is not null. Remember we defined the session attribute for this controller in its class. If the attribute is not null we will continue using this instance of the controller class, if not we create a new instance.
  • Next we create a vaadin navigator and add the two view, we have, LoginView and MainView (29-32).
  • Next we decide which view should be loaded. If the user is already authenticated (remember at that point of the application we will have a valid instance of the LoginAppUiController), we will directly navigate to the MainView, else to the LoginView.

That´s it, now we have a simple Vaadin application with authentication. If there are any questions, just leave a message.

Links:

Print Friendly, PDF & Email

Leave a Reply

Your email address will not be published. Required fields are marked *