So, i tried the Vaadin login component as in the tutorial https://vaadin.com/docs/latest/security/enabling-security and it works like a charm.
Now I'm trying to customize the login form but whenever I log in, I get logged out when i try to navigate to any other pages. Please help.
Login Form:
@Route("login")
@PageTitle("Login")
@AnonymousAllowed
public class LoginViewOverLay extends Div implements BeforeEnterObserver, ComponentEventListener<AbstractLogin.LoginEvent> {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
SecurityService securityservice;
@Autowired UserDetailsServiceImpl userdetails;
FormLayout form = new FormLayout();
LoginOverlay loginOverlay = new LoginOverlay();
public LoginViewOverLay() {
add(loginOverlay);
loginOverlay.setOpened(true);
loginOverlay.addLoginListener(this);
}
@Override
public void onComponentEvent(AbstractLogin.LoginEvent loginEvent) {
try {
securityservice.authenticateUser(loginEvent.getUsername(), loginEvent.getPassword());
loginOverlay.close();
getUI().ifPresent(ui -> ui.navigate("/"));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
if(beforeEnterEvent.getLocation()
.getQueryParameters()
.getParameters()
.containsKey("error")) {
loginOverlay.setError(true);
}
}
}
My Security service class:
@Component
public class SecurityService {
@Autowired
UserDetailsServiceImpl userdetails;
@Autowired
PasswordEncoder encoder;
private final AuthenticationContext authenticationContext;
public SecurityService(AuthenticationContext authenticationContext) {
this.authenticationContext = authenticationContext;
}
public UserDetails getAuthenticatedUser() {
return authenticationContext.getAuthenticatedUser(UserDetails.class).get();
}
public void logout() {
authenticationContext.logout();
}
public UserDetails getAuthenticatedUser2() {
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 authenticateUser(UserDetails userDetails) {
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
public void authenticateUser(String username, String password) {
UserDetails userDetails = userdetails.loadUserByUsername(username);
if (encoder.matches(password, userDetails.getPassword())) {
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
// System.out.println("wrong");
}
}
}
My User Details Class:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
@Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserLogin user = userRepository.findByUserNameAndEnabled(username, true);
if (user == null) {
throw new UsernameNotFoundException("No user present with username: " + username);
} else {
//System.out.println("Yes User");
return new User(user.getUserName(), user.getHashedPassword(), getAuthorities(user));
}
}
private static List<GrantedAuthority> getAuthorities(UserLogin user) {
return user.getRoles().stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRoleName()))
.collect(Collectors.toList());
}
}
Security Configuration class:
@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends VaadinWebSecurity {
@Autowired
UserDetailsServiceImpl userdetails;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize -> authorize
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/images/*.png")).permitAll()
// .requestMatchers(new AntPathRequestMatcher("/**",
// HttpMethod.POST.toString())).permitAll()
.requestMatchers(new AntPathRequestMatcher("/**", HttpMethod.DELETE.toString())).denyAll()
.requestMatchers(new AntPathRequestMatcher("/**", HttpMethod.OPTIONS.toString())).denyAll()
.requestMatchers(AntPathRequestMatcher.antMatcher(HttpMethod.OPTIONS, "/**")).denyAll()
).sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).invalidSessionUrl("/")
.maximumSessions(1).sessionRegistry(sessionRegistry()).expiredUrl("/")
.maxSessionsPreventsLogin(false));
super.configure(http);
setLoginView(http, LoginViewOverLay.class);
}
@Bean
SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}
The issue occurs by using Spring Security 6.0+
[Spring Security Documentation][1] [1]: https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html
To prevent the issue save the SecurityContext with the SecurityContextRepository as follows :
public void authenticateUser() {
Authentication authentication = new UsernamePasswordAuthenticationToken(loginEvent.getUsername(), loginEvent.getPassword());
Authentication authenticated = authenticationManager.authenticate(authentication);
SecurityContextHolder.getContext().setAuthentication(authenticated);
SecurityContext context = SecurityContextHolder.getContext();
securityRepo.saveContext(context, VaadinServletRequest.getCurrent(), VaadinServletResponse.getCurrent());
if (authenticated.isAuthenticated()) {
UI.getCurrent().navigate(DashboardView.class);
} else {
System.out.println("FAILURE");
Notification.show("Authentication failed", 3000,
Notification.Position.BOTTOM_CENTER); }
}