Skip to content
This repository has been archived by the owner on May 6, 2021. It is now read-only.

Latest commit

 

History

History
174 lines (145 loc) · 4.83 KB

adding-custom-login-form-with-spring-security.asciidoc

File metadata and controls

174 lines (145 loc) · 4.83 KB
title order layout
Adding a Custom Login Form with Spring Security
91
page

Adding a Custom Login Form with Spring Security

Instead of using the default Spring login form, you may want to use your own customized form. This article describes how to do that.

Dependencies and Server Configuration

The dependencies and server configuration is the same as described in Adding a Login Form with Spring Security, with a difference that you need to specify the URL of the custom login view.

SecurityConfig.java
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // Ignore the login processing url and vaadin endpoint calls
    http.csrf().ignoringAntMatchers("/login", "/connect/**");

    // specify the URL of the login view, the value of the parameter
    // is the defined route for the login view component.
    http.formLogin().loginPage("login");
  }
}

Custom login view

The easiest way to make a login view is to use the <vaadin-login-overlay> component. Vaadin provides a login helper method for Spring Security based authentication that you can use for the login action.

frontend/login-view.ts
import { LoginResult, login } from '@vaadin/flow-frontend';

@customElement('login-view')
export class LoginView extends LitElement implements AfterEnterObserver {

  @property({type: Boolean})
  private error = false;

  @property({type: Boolean})
  private open = true;

  // the url to redirect to after a successful login
  private returnUrl = '/';

  private onSuccess: (result: LoginResult) => void;

  constructor(){
    super();
    this.onSuccess = () => {
      Router.go(this.returnUrl);
    };
  }

  render() {
    return html`
      <vaadin-login-overlay
        ?opened="${this.open}"
        .error=${this.error}
        @login="${this.login}">
      </vaadin-login-overlay>
    `;
  }

  async login(event: CustomEvent): Promise<LoginResult> {
    this.error = false;
    // use the Vaadin provided login helper method to obtain the LoginResult
    const result = await login(event.detail.username, event.detail.password);
    this.error = result.error;

    if (!result.error) {
      this.onSuccess(result);
    }

    return result;
  }

  onAfterEnter(location: RouterLocation) {
    this.returnUrl = location.redirectFrom || this.returnUrl;
  }
}

After the login view is defined, you should also define a route for the login view component, for example in the index.ts file. Note, the path for the login view component should be the same as what you defined in SecurityConfig for http.formLogin().loginPage(). .frontend/index.ts

const routes = [
  {
    path: '/login',
    component: 'login-view'
  },
  // more routes
}

Redirect the user to the login view

To protect a view from unauthenticated users, that is, redirect unauthenticated users to the login view, you can use route action.

frontend/index.ts
const isUserLoggedIn = function() {
  return localStorage.getItem('loggedIn') === 'true';
}

const routes = [
  ...
  {
    path: '/my-view',
    action: (_: Router.Context, commands: Router.Commands) => {
      if (!isUserLoggedIn()) {
        return commands.redirect('/login');
      }
      return undefined;
    },
    component: 'my-view'
  }
  ...
}

You can also add the route action to the parent layout, so that all the child views are protected.

frontend/index.ts
const routes = [
  ...
  {
    path: '/',
    action: (_: Router.Context, commands: Router.Commands) => {
      if (!isUserLoggedIn()) {
        return commands.redirect('/login');
      }
      return undefined;
    },
    component: 'main-layout',
    children: [
      ...
    ]
  }
  ...
}

The isUserLoggedIn() method in the above code examples uses a loggedIn variable stored in the localStorage to check if the user is logged in. The loggedIn variable needs to be reset when logging out. Using localStorage allows navigating to sub views without having to check authentication from the backend on every navigation.

Logout

To avoid a full page reload the application needs to have a /logout route like the one below. It can be triggered with a link like <a href="/logout">Log out</a>.

frontend/index.ts
path: '/logout',
action: async (_: Context, commands: Commands) => {
  // use the logout helper method.
  await logout();
  // set the loggedIn to false.
  localStorage.setItem('loggedIn', String(false));
  return commands.redirect('/');
}