Problemas con Login

Hola, estaba haciendo una aplicación sencilla con login y mi problema es que al iniciar sesión en el main y hacer click en algún botón para cambiar de vista me solicita volver a logearme y no se como guardar la sesión activa, si alguien pudiera ayudarme se lo agradecería muchísimo. Les dejo aca todo el código de mi aplicación

package com.diego.vaadin1.moodle;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;


import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User implements UserDetails{
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	@Column(name = "username")
	private String username;

	@Column(name = "password")
	private String password;
	
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return new ArrayList<GrantedAuthority>();
	}

	public String getPassword() {
		return this.password;
	}

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

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

	public String getUsername() {
		return this.username;
	}

	public boolean isAccountNonExpired() {
		return true;
	}

	public boolean isAccountNonLocked() {
		return true;
	}

	public boolean isCredentialsNonExpired() {
		return true;
	}

	public boolean isEnabled() {
		return true;
	}
	
	
}

package com.diego.vaadin1;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.theme.Theme;
import com.vaadin.flow.theme.lumo.Lumo;

@SpringBootApplication
@Theme(themeClass = Lumo.class)
public class Vaadin1Application implements AppShellConfigurator {

	public static void main(String[] args) {
		SpringApplication.run(Vaadin1Application.class, args);
	}

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder(); 
	}

}
package com.diego.vaadin1.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import com.diego.vaadin1.views.LoginView;
import com.vaadin.flow.spring.security.VaadinWebSecurity;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends VaadinWebSecurity  {

	@Autowired
	private UserDetailsService userDetailsService;
	
	@Autowired
	private BCryptPasswordEncoder encoder;
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		super.configure(http);
		setLoginView(http, LoginView.class);
	}
	
	@Override
	protected void configure(WebSecurity web) throws Exception {
		web.ignoring().requestMatchers("/images/**", "/login", "/signup");
		super.configure(web);
	}
	
    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
           AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
            authenticationManagerBuilder.authenticationProvider(authenticationProvider());
            return authenticationManagerBuilder.build();
    }

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


package com.diego.vaadin1.moodle.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.diego.vaadin1.moodle.User;

@Repository
public interface SecurityRepository extends JpaRepository<User, Integer>{
	
	@Query("select u from User u where u.username=:username")
	User findByUsername( @Param("username") String username);
}

package com.diego.vaadin1.services;

public interface SecurityService {
public void save(String username, String password);
public void logout();
}

package com.diego.vaadin1.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Service;

import com.diego.vaadin1.moodle.User;
import com.diego.vaadin1.moodle.repository.SecurityRepository;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.server.VaadinServletRequest;

@Service
public class SecurityServiceImpl implements SecurityService,
				UserDetailsService{
	
	@Autowired
	private SecurityRepository securityR;
	
	@Autowired
	public BCryptPasswordEncoder  encoder; 

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

		User user = securityR.findByUsername(username);
		return user;
	}
		
	@Override
	public void save(String username, String password) {
		User user = new User(); 
		user.setUsername(username);
		user.setPassword(encoder.encode(password));
		securityR.save(user); 
		
	}

	@Override
	public void logout() {
		UI.getCurrent().getPage().setLocation("/login");
		var logoutHandler = new SecurityContextLogoutHandler();
		logoutHandler.logout(VaadinServletRequest.getCurrent()
				.getHttpServletRequest(), null, null);
	}
	
}

package com.diego.vaadin1.views;

import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;

@PageTitle("Login")
@Route(value = LoginView.PATH)
//se accede sin necesidad de auntentificacion
@AnonymousAllowed
public class LoginView extends VerticalLayout{
	 private static final String PATH = "/login"; 
	  
	 public LoginView(LoginViewFactory loginFactory) {
		 add(loginFactory.create());  	
	 }
}

package com.diego.vaadin1.views;



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.Notification.Position;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.FlexComponent.Alignment;
import com.vaadin.flow.component.orderedlayout.FlexComponent.JustifyContentMode;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.component.textfield.TextField;

@org.springframework.stereotype.Component
public class LoginViewFactory {
	
	@Autowired
	private DaoAuthenticationProvider authenticationProvider; 
	
	private class LoginForm{
		
		
		private VerticalLayout root; 
		private TextField username; 
		private PasswordField password; 
		private Button login; 
		private Button signup; 
		
		public LoginForm init() {
			
			username = new TextField("Username");
			password = new PasswordField("Password");
			login = new Button("Login");
			signup = new Button("Signup");
			
			login.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
			signup.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
			
			root = new VerticalLayout();
			root.setAlignItems(Alignment.CENTER);
			root.setJustifyContentMode(JustifyContentMode.CENTER);
			root.setMargin(true);
			
			return this;
		}
		
		private void login() {
			try {
				var auth = new UsernamePasswordAuthenticationToken(username.getValue(), password.getValue());
				var authenticated = authenticationProvider.authenticate(auth);
				SecurityContextHolder.getContext().setAuthentication(authenticated);	
				login.getUI().ifPresent(ui -> ui.navigate(""));	
			}catch (AuthenticationException e) {
				Notification notification = Notification.
						show("Incorrect username or password...");
				notification.addThemeVariants(NotificationVariant.LUMO_ERROR);
				notification.setPosition(Position.TOP_CENTER);
				e.printStackTrace();
			}
		}

		public Component layout() {
			//root.add(new Image(Constants.LOGIN_URL, "Login Image"));
			root.add(username);
			root.add(password);
			root.add(new HorizontalLayout(login, signup));
		
			login.addClickListener(e -> login());
			//signup.addClickListener(e -> signup());
			
			return root; 
		}
	}
	public Component create() {
		return new LoginForm().init().layout();
	}
}

package com.diego.vaadin1.views;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import com.diego.vaadin1.moodle.Status;
import com.diego.vaadin1.moodle.Student;
import com.diego.vaadin1.services.SecurityService;
import com.diego.vaadin1.services.StudentService;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.Lumo;

import jakarta.annotation.security.PermitAll;

@Route(value = "")
@PageTitle(value = "Home")
@PermitAll
public class MainView extends VerticalLayout {
	// constructor injection
	private final StudentService studentS;
	private final SecurityService securityService;
	
	private Grid<Student> grid;
	private LogoLayout logoLayout;
	private TextField filterField;
	private Checkbox themeToggle;
	private static boolean isChecked; 

	public MainView(StudentService studentS, SecurityService securityService) {
		this.studentS = studentS;
		this.securityService = securityService; 
		
		setSizeFull();
		setAlignItems(Alignment.CENTER);

		createFieldsVariables();
		configurateGrid();

		add(logoLayout, createToolbar(), grid);
		loadStudents();
	}

	private Checkbox createToogle() {
		// TODO Auto-generated method stub
		this.themeToggle = new Checkbox("Dark Box");
		this.themeToggle.setValue(isChecked );
		this.themeToggle.addValueChangeListener(e->{
			MainView.isChecked =!isChecked;
			setTheme(isChecked);
		});
		return this.themeToggle;
	}

	private void setTheme(boolean dark) {
		// TODO Auto-generated method stub
		var js = MessageFormat.format("""
			    document.documentElement.setAttribute("theme", "{0}")
			""", dark ? Lumo.DARK : Lumo.LIGHT);
		//execute java script
	getElement().executeJs(js);
	
	}

	private Component createToolbar() {
		// TODO Auto-generated method stub
		filterField.setPlaceholder("Filter by name...");
		filterField.setClearButtonVisible(true);
		filterField.setValueChangeMode(ValueChangeMode.LAZY);
		filterField.addValueChangeListener(e -> updateStudents());

		Button addStudentButton = new Button("Add Student");
		addStudentButton.addClickListener(e -> getUI().ifPresent(ui -> ui.navigate("add-student")));

		Button deleteStudentButton = new Button("Delete Student");
		deleteStudentButton.addClickListener(e -> getUI().ifPresent(ui -> ui.navigate("delete-student")));
		
		Button logout = new Button("Logout"); 
		 logout.addClickListener(e->securityService.logout());
		
		
		return new HorizontalLayout(filterField, addStudentButton, deleteStudentButton
				,logout,createToogle());
	}

	private void updateStudents() {
		// TODO Auto-generated method stub
		grid.setItems(studentS.find(filterField.getValue()));

	}

	private void configurateGrid() {
		// TODO Auto-generated method stub
		grid.setSizeFull();
		grid.setColumns("country", "zipcode");
		grid.addColumn(s -> s.getName()).setHeader("Name");
		grid.addColumn(s -> s.getAge()).setHeader("Age");
		grid.getColumns().forEach(col -> col.setAutoWidth(true));

		grid.addComponentColumn(s -> {
			Icon icon;
			if (s.getStatus().getName().equals("ACTIVE")) {
				icon = VaadinIcon.CIRCLE.create();
				icon.setColor("green");
			} else if (s.getStatus().getName().equals("PASSIVE")) {
				icon = VaadinIcon.CLOSE_CIRCLE.create();
				icon.setColor("red");
			} else {
				icon = VaadinIcon.CHECK_CIRCLE.create();
				icon.setColor("orange");
			}
			return icon;
		}).setHeader("Status");
	}

	private void loadStudents() {
		grid.setItems(studentS.findAll());
	}

	private void createFieldsVariables() {
		// TODO Auto-generated method stub
		this.logoLayout = new LogoLayout();
		this.grid = new Grid<>(Student.class);
		this.filterField = new TextField();
	}
}

package com.diego.vaadin1.views;

import java.util.List;

import com.diego.vaadin1.moodle.Status;
import com.diego.vaadin1.moodle.Student;
import com.diego.vaadin1.services.StatusService;
import com.diego.vaadin1.services.StudentService;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasValue;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.Notification.Position;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.ValidationException;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;

import jakarta.annotation.security.PermitAll;


@PageTitle("Add Students")
@Route(value = "add-student")
@PermitAll
public class AddStudentView extends VerticalLayout {
	private TextField age;
	private TextField name;
	private TextField country;
	private TextField zipcode;
	private ComboBox<Status> status;
	private LogoLayout image;

	private Button save;
	private Button close;

	private final StatusService statusS;
	private final StudentService studentS;

	private Student student;
	private Binder<Student> binder;

	public AddStudentView(StatusService statusS, StudentService studentS) {
		this.statusS = statusS;
		this.studentS = studentS;

		setAlignItems(Alignment.CENTER);
		createVariables();
		createStatus();
		createBinder();
		add(image);
		add(createFormLayout());
	}

	private void createBinder() {
		// TODO Auto-generated method stub
		this.student = new Student();
		binder = new BeanValidationBinder<>(Student.class);
		binder.bindInstanceFields(this);
	}

	private void createStatus() {
		// TODO Auto-generated method stub
		List<Status>statusItems = statusS.findAll();
		status.setItems(statusItems); 
		status.setValue(statusItems.get(0));
		status.setItemLabelGenerator(Status::getName);
	}

	private Component createFormLayout() {
		// TODO Auto-generated method stub
		FormLayout formLayout = new FormLayout();
		formLayout.add(this.name, this.age, this.country, this.zipcode, this.status, createButton());
		formLayout.setColspan(image, 2);
		formLayout.setColspan(name, 2);
		return formLayout;
	}

	private Component createButton() {
		// TODO Auto-generated method stub
		save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
		close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

		close.addClickListener(e -> closeView());
		save.addClickListener(e -> saveStudent());

		return new HorizontalLayout(save, close);
	}

	private void saveStudent() {
		// TODO Auto-generated method stub
		try {
			binder.writeBean(student);
			studentS.save(student);
			clearFields();
			Notification notification = Notification.show("Student save succefully");
			notification.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
			notification.setPosition(Position.TOP_CENTER);
		} catch (ValidationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void clearFields() {
		// TODO Auto-generated method stub
		student = new Student();
		status.setValue(statusS.findAll().get(0));
		binder.getFields().forEach(HasValue::clear);
	}

	private void closeView() {
		// TODO Auto-generated method stub
		getUI().ifPresent(ui -> ui.navigate(""));
	}

	private void createVariables() {
		this.age = new TextField("Age");
		this.name = new TextField("Name");
		this.country = new TextField("Country");
		this.zipcode = new TextField("Zipcode");
		this.status = new ComboBox<Status>("Status");
		this.image = new LogoLayout();

		this.save = new Button("Save");
		this.close = new Button("Close");
	}
}

Hola Diego, como estas?
Bienvenido a la comunidad!
Te hago una consulta, por las dudas sabes que versión de Vaadin estás utilizando exactamente?
Asumiendo de que sea Vaadin 24, algo que podría ayudarte, es que intentes crear una aplicación en start.vaadin.com con seguridad habilitada, y luego comparar la implementación de, por ejemplo, la clase SecurityConfig que es la que gestiona todo lo relacionado a seguridad con la clase SecurityConfiguration que te genera automáticamente start.vaadin.com.
Mirándola muy por arriba veo que es diferente a tu implementación, veo que estás inyectando un AuthManager y un authenticationProvider, cuando eso por lo general no es necesario, com simplemente implementar UserDetailsService, Spring Security debería inyectar el servicio de forma automática.
No estoy seguro de si es ese el problema, pero empezaría por ahí a ver que encontramos.
Saludos!

1 Like

Muchas gracias Martín, estaré probando tus recomendaciones, espero eso me funcione!!