feat: LoginView and Spring SecurityConfiguration setup
This commit is contained in:
parent
0c248adff7
commit
03c20db830
10 changed files with 184 additions and 3 deletions
6
pom.xml
6
pom.xml
|
@ -62,13 +62,15 @@
|
||||||
<artifactId>line-awesome</artifactId>
|
<artifactId>line-awesome</artifactId>
|
||||||
<version>2.0.0</version>
|
<version>2.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.postgresql</groupId>
|
<groupId>org.postgresql</groupId>
|
||||||
<artifactId>postgresql</artifactId>
|
<artifactId>postgresql</artifactId>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.application.munera;
|
||||||
|
|
||||||
|
import com.application.munera.views.LoginView;
|
||||||
|
import com.vaadin.flow.spring.security.VaadinWebSecurity;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
|
import org.springframework.security.provisioning.UserDetailsManager;
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
public class SecurityConfiguration
|
||||||
|
extends VaadinWebSecurity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// Delegating the responsibility of general configurations
|
||||||
|
// of http security to the super class. It's configuring
|
||||||
|
// the followings: Vaadin's CSRF protection by ignoring
|
||||||
|
// framework's internal requests, default request cache,
|
||||||
|
// ignoring public views annotated with @AnonymousAllowed,
|
||||||
|
// restricting access to other views/endpoints, and enabling
|
||||||
|
// NavigationAccessControl authorization.
|
||||||
|
// You can add any possible extra configurations of your own
|
||||||
|
// here (the following is just an example):
|
||||||
|
|
||||||
|
// http.rememberMe().alwaysRemember(false);
|
||||||
|
|
||||||
|
// Configure your static resources with public access before calling
|
||||||
|
// super.configure(HttpSecurity) as it adds final anyRequest matcher
|
||||||
|
http.authorizeHttpRequests(auth -> auth.requestMatchers(new AntPathRequestMatcher("/public/**"))
|
||||||
|
.permitAll());
|
||||||
|
|
||||||
|
super.configure(http);
|
||||||
|
|
||||||
|
// This is important to register your login view to the
|
||||||
|
// navigation access control mechanism:
|
||||||
|
setLoginView(http, LoginView.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(WebSecurity web) throws Exception {
|
||||||
|
// Customize your WebSecurity configuration.
|
||||||
|
super.configure(web);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demo UserDetailsManager which only provides two hardcoded
|
||||||
|
* in memory users and their roles.
|
||||||
|
* NOTE: This shouldn't be used in real world applications.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public UserDetailsManager userDetailsService() {
|
||||||
|
UserDetails user =
|
||||||
|
User.withUsername("user")
|
||||||
|
.password("{noop}user")
|
||||||
|
.roles("USER")
|
||||||
|
.build();
|
||||||
|
UserDetails admin =
|
||||||
|
User.withUsername("admin")
|
||||||
|
.password("{noop}admin")
|
||||||
|
.roles("ADMIN")
|
||||||
|
.build();
|
||||||
|
return new InMemoryUserDetailsManager(user, admin);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.application.munera.services;
|
||||||
|
|
||||||
|
import com.vaadin.flow.component.UI;
|
||||||
|
import com.vaadin.flow.server.VaadinServletRequest;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SecurityService {
|
||||||
|
|
||||||
|
private static final String LOGOUT_SUCCESS_URL = "/";
|
||||||
|
|
||||||
|
public UserDetails getAuthenticatedUser() {
|
||||||
|
SecurityContext context = SecurityContextHolder.getContext();
|
||||||
|
Object principal = context.getAuthentication().getPrincipal();
|
||||||
|
if (principal instanceof UserDetails) {
|
||||||
|
return (UserDetails) context.getAuthentication().getPrincipal();
|
||||||
|
}
|
||||||
|
// Anonymous or no authentication.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logout() {
|
||||||
|
UI.getCurrent().getPage().setLocation(LOGOUT_SUCCESS_URL);
|
||||||
|
SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();
|
||||||
|
logoutHandler.logout(
|
||||||
|
VaadinServletRequest.getCurrent().getHttpServletRequest(), null,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
43
src/main/java/com/application/munera/views/LoginView.java
Normal file
43
src/main/java/com/application/munera/views/LoginView.java
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package com.application.munera.views;
|
||||||
|
|
||||||
|
import com.vaadin.flow.component.html.H1;
|
||||||
|
import com.vaadin.flow.component.html.H2;
|
||||||
|
import com.vaadin.flow.component.login.LoginForm;
|
||||||
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
|
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||||
|
import com.vaadin.flow.router.BeforeEnterObserver;
|
||||||
|
import com.vaadin.flow.router.PageTitle;
|
||||||
|
import com.vaadin.flow.router.Route;
|
||||||
|
import com.vaadin.flow.server.auth.AnonymousAllowed;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Route("login")
|
||||||
|
@PageTitle("Login")
|
||||||
|
@AnonymousAllowed
|
||||||
|
public class LoginView extends VerticalLayout implements BeforeEnterObserver {
|
||||||
|
|
||||||
|
private LoginForm login = new LoginForm();
|
||||||
|
|
||||||
|
public LoginView() {
|
||||||
|
addClassName("login-view");
|
||||||
|
setSizeFull();
|
||||||
|
|
||||||
|
setJustifyContentMode(JustifyContentMode.CENTER);
|
||||||
|
setAlignItems(Alignment.CENTER);
|
||||||
|
|
||||||
|
login.setAction("login");
|
||||||
|
|
||||||
|
add(new H1("Munera"), new H2("An expense tracking application"), login);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
|
||||||
|
if(beforeEnterEvent.getLocation()
|
||||||
|
.getQueryParameters()
|
||||||
|
.getParameters()
|
||||||
|
.containsKey("error")) {
|
||||||
|
login.setError(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,14 +3,18 @@ package com.application.munera.views;
|
||||||
import com.application.munera.views.expenses.*;
|
import com.application.munera.views.expenses.*;
|
||||||
import com.vaadin.flow.component.applayout.AppLayout;
|
import com.vaadin.flow.component.applayout.AppLayout;
|
||||||
import com.vaadin.flow.component.applayout.DrawerToggle;
|
import com.vaadin.flow.component.applayout.DrawerToggle;
|
||||||
|
import com.vaadin.flow.component.button.Button;
|
||||||
import com.vaadin.flow.component.html.Footer;
|
import com.vaadin.flow.component.html.Footer;
|
||||||
import com.vaadin.flow.component.html.H1;
|
import com.vaadin.flow.component.html.H1;
|
||||||
import com.vaadin.flow.component.html.Header;
|
import com.vaadin.flow.component.html.Header;
|
||||||
import com.vaadin.flow.component.html.Span;
|
import com.vaadin.flow.component.html.Span;
|
||||||
|
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||||
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
import com.vaadin.flow.component.orderedlayout.Scroller;
|
import com.vaadin.flow.component.orderedlayout.Scroller;
|
||||||
import com.vaadin.flow.component.sidenav.SideNav;
|
import com.vaadin.flow.component.sidenav.SideNav;
|
||||||
import com.vaadin.flow.component.sidenav.SideNavItem;
|
import com.vaadin.flow.component.sidenav.SideNavItem;
|
||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
|
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||||
import org.vaadin.lineawesome.LineAwesomeIcon;
|
import org.vaadin.lineawesome.LineAwesomeIcon;
|
||||||
|
|
||||||
|
@ -20,8 +24,10 @@ import org.vaadin.lineawesome.LineAwesomeIcon;
|
||||||
public class MainLayout extends AppLayout {
|
public class MainLayout extends AppLayout {
|
||||||
|
|
||||||
private H1 viewTitle;
|
private H1 viewTitle;
|
||||||
|
private final transient AuthenticationContext authContext;
|
||||||
|
|
||||||
public MainLayout() {
|
public MainLayout(AuthenticationContext authContext) {
|
||||||
|
this.authContext = authContext;
|
||||||
setPrimarySection(Section.DRAWER);
|
setPrimarySection(Section.DRAWER);
|
||||||
addDrawerContent();
|
addDrawerContent();
|
||||||
addHeaderContent();
|
addHeaderContent();
|
||||||
|
@ -34,7 +40,22 @@ public class MainLayout extends AppLayout {
|
||||||
viewTitle = new H1();
|
viewTitle = new H1();
|
||||||
viewTitle.addClassNames(LumoUtility.FontSize.LARGE, LumoUtility.Margin.NONE);
|
viewTitle.addClassNames(LumoUtility.FontSize.LARGE, LumoUtility.Margin.NONE);
|
||||||
|
|
||||||
|
// Creating the logout button
|
||||||
|
Button logout = new Button("Logout", click -> this.authContext.logout());
|
||||||
|
|
||||||
|
// Adding some padding to the logout button
|
||||||
|
logout.getStyle().set("padding", "10px");
|
||||||
|
|
||||||
|
// Creating the header and adding the logout button to the far left
|
||||||
|
HorizontalLayout header = new HorizontalLayout(logout);
|
||||||
|
header.setWidthFull(); // Make the header take the full width
|
||||||
|
header.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
|
header.setJustifyContentMode(FlexComponent.JustifyContentMode.END); // Align items to the start (left)
|
||||||
|
header.getStyle().set("padding", "0 10px"); // Add padding around the header if needed
|
||||||
|
|
||||||
|
|
||||||
addToNavbar(true, toggle, viewTitle);
|
addToNavbar(true, toggle, viewTitle);
|
||||||
|
addToNavbar(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDrawerContent() {
|
private void addDrawerContent() {
|
||||||
|
|
|
@ -22,12 +22,14 @@ import com.vaadin.flow.data.binder.BeanValidationBinder;
|
||||||
import com.vaadin.flow.data.binder.ValidationException;
|
import com.vaadin.flow.data.binder.ValidationException;
|
||||||
import com.vaadin.flow.router.*;
|
import com.vaadin.flow.router.*;
|
||||||
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
|
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
|
||||||
|
import jakarta.annotation.security.PermitAll;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@PageTitle("Categories")
|
@PageTitle("Categories")
|
||||||
|
@PermitAll
|
||||||
@Route(value = "categories/:categoryID?/:action?(edit)", layout = MainLayout.class)
|
@Route(value = "categories/:categoryID?/:action?(edit)", layout = MainLayout.class)
|
||||||
@Uses(Icon.class)
|
@Uses(Icon.class)
|
||||||
public class CategoriesView extends Div implements BeforeEnterObserver {
|
public class CategoriesView extends Div implements BeforeEnterObserver {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
|
import jakarta.annotation.security.PermitAll;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.Year;
|
import java.time.Year;
|
||||||
|
@ -20,6 +21,7 @@ import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
//@HtmlImport("frontend://styles/shared-styles.html") // If you have custom styles
|
//@HtmlImport("frontend://styles/shared-styles.html") // If you have custom styles
|
||||||
|
@PermitAll
|
||||||
@PageTitle("Dashboard")
|
@PageTitle("Dashboard")
|
||||||
@Route(value = "dashboard", layout = MainLayout.class)
|
@Route(value = "dashboard", layout = MainLayout.class)
|
||||||
public class DashboardView extends Div {
|
public class DashboardView extends Div {
|
||||||
|
|
|
@ -28,12 +28,14 @@ import com.vaadin.flow.router.BeforeEnterObserver;
|
||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
|
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
|
||||||
|
import jakarta.annotation.security.PermitAll;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@PageTitle("Events")
|
@PageTitle("Events")
|
||||||
|
@PermitAll
|
||||||
@Route(value = "events/:eventID?/:action?(edit)", layout = MainLayout.class)
|
@Route(value = "events/:eventID?/:action?(edit)", layout = MainLayout.class)
|
||||||
@Uses(Icon.class)
|
@Uses(Icon.class)
|
||||||
public class EventsView extends Div implements BeforeEnterObserver {
|
public class EventsView extends Div implements BeforeEnterObserver {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.vaadin.flow.data.binder.BeanValidationBinder;
|
||||||
import com.vaadin.flow.data.binder.ValidationException;
|
import com.vaadin.flow.data.binder.ValidationException;
|
||||||
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||||
import com.vaadin.flow.router.*;
|
import com.vaadin.flow.router.*;
|
||||||
|
import jakarta.annotation.security.PermitAll;
|
||||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||||
import org.vaadin.klaudeta.PaginatedGrid;
|
import org.vaadin.klaudeta.PaginatedGrid;
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@PermitAll
|
||||||
@PageTitle("Expenses")
|
@PageTitle("Expenses")
|
||||||
@Route(value = "/:expenseID?/:action?(edit)", layout = MainLayout.class)
|
@Route(value = "/:expenseID?/:action?(edit)", layout = MainLayout.class)
|
||||||
@RouteAlias(value = "", layout = MainLayout.class)
|
@RouteAlias(value = "", layout = MainLayout.class)
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.vaadin.flow.router.BeforeEnterObserver;
|
||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
|
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
|
||||||
|
import jakarta.annotation.security.PermitAll;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ import java.math.BigDecimal;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@PageTitle("People")
|
@PageTitle("People")
|
||||||
|
@PermitAll
|
||||||
@Route(value = "people/:personID?/:action?(edit)", layout = MainLayout.class)
|
@Route(value = "people/:personID?/:action?(edit)", layout = MainLayout.class)
|
||||||
@Uses(Icon.class)
|
@Uses(Icon.class)
|
||||||
public class PeopleView extends Div implements BeforeEnterObserver {
|
public class PeopleView extends Div implements BeforeEnterObserver {
|
||||||
|
|
Loading…
Reference in a new issue