SimiSimi
SimiSimi

Reputation: 1

Missing user in SecurityContextHolder

I have created a field for user login, user data is taken from MySQL database. I am taken to a new window named /grid. In /grid, I have a "Save" button, which I want to use to save some data to the MySQL database. However, the problem is that when I press the "Save" button, I lose the logged-in user, and the method called by the button shows the user as "anonymous".

In the Vaadin session, I can see which user is logged in, but Spring loses it. Can anyone help me? Advise me on what I might be missing in my configuration? Perhaps there is a multithreading problem, but how can I deal with it effectively?

Here are the console logs:

User successful logged in: fish1
Logged user: fish1
Username while button is pressing in /grid: fish1
User in SecurityContext in class EnterProductService: anonymousUser  
User in VaadinSession in class EnterProductService: fish1
@Service public class EnterProductService {
@Autowired
private RestTemplate restTemplate;
public String urlForMapping = null;
public boolean enteredProductError = false;
public boolean reqOpenFoodFacts = false;
public boolean reqFoodDataCentral = false;
public String reqBarcode = null;
public MealType reqMealType = null;
public double reqGrams = 0;

private final Executor executor;

@Autowired
public EnterProductService(@Qualifier("taskExecutor") Executor executor) {
    this.executor = new DelegatingSecurityContextExecutor(executor);
}

@PreAuthorize("hasRole('USER')")
@Transactional
public void onSaveButtonClicked(String searchProductField, String gramAmount, MealType mealType) {

    SecurityContext securityContext = SecurityContextHolder.getContext();

    VaadinSession currentSession = VaadinSession.getCurrent();
    UI currentUI = UI.getCurrent();


    executor.execute(() -> {
        VaadinSession.setCurrent(currentSession);

        try {
            currentSession.lock();
            currentUI.access(() -> {
                SecurityContextHolder.setContext(securityContext);
                System.out.println("User in SecurityContext in class EnterProductService: " + SecurityContextHolder.getContext().getAuthentication().getName());
                System.out.println("User in VaadinSession in class EnterProductService: " + VaadinSession.getCurrent().getSession().getAttribute("username"));
                if (searchProductField != null && mealType != null && gramAmount != null) {
                    if (isOnlyNumeric(searchProductField)) {
                        reqBarcode = searchProductField;
                        reqGrams = Double.parseDouble(gramAmount);
                        reqMealType = mealType;
                        urlForMapping = String.format("/openfoodfacts/%s/%s/%s", searchProductField, gramAmount, mealType.name());
                        reqOpenFoodFacts = true;
                        reqFoodDataCentral = false;
                    } else if (isOnlyLetters(searchProductField)) {
                        reqBarcode = searchProductField;
                        reqGrams = Double.parseDouble(gramAmount);
                        reqMealType = mealType;
                        urlForMapping = String.format("/fooddatacentral/%s/%s/%s", searchProductField, gramAmount, mealType.name());
                        reqFoodDataCentral = true;
                        reqOpenFoodFacts = false;
                    } else {
                        enteredProductError = true;
                        reqOpenFoodFacts = false;
                        reqFoodDataCentral = false;
                    }
                }
            });
        } finally {

            currentSession.unlock();

        }

    });
}
public void sendRequestToApi() {
    String url = urlForMapping;
    String response = restTemplate.getForObject("http://localhost:8080/product" + url, String.class);

}

public boolean isOnlyNumeric (String string){
    return string.matches("\\d+");
}

public boolean isOnlyLetters (String string){
    return string.matches("[a-zA-Z]+");
}
}

Here is my class where the user in SecurityContext is missing:

Here I call onSaveButtonClicked() and the user is still available:

saveButton.addClickListener(event -> {
            System.out.println("Username while button is pressing in /grid " + username);
            enterProductService.onSaveButtonClicked(
            searchProductField.getValue(),
            gramAmount.getValue(),
            comboBox.getValue());
            enterProductService.sendRequestToApi();

I attempted to handle multithreading by checking active sessions, but this doesn't seem to work.

What am I missing in my configuration? Why is my user being lost in SecurityContext but still available in the VaadinSession?

@Configuration
@EnableWebSecurity
public class SecurityConfig extends VaadinWebSecurity {

    private final UserService userService;

    @Autowired
    public SecurityConfig(@Lazy UserService userService) {
        this.userService = userService;
    }
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/login", "/user", "/VAADIN/**", "/UIDL/**", "/HEARTBEAT/**", "/resources/**", "/websocket-client.js").permitAll()
                        .requestMatchers("/grid").hasRole("USER")
                        .anyRequest().permitAll()
                )
                .formLogin(form -> form
                        .loginPage("/login")
                        .defaultSuccessUrl("/grid", true)
                        .permitAll()
                )
                .logout(logout -> logout
//                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/login")
                        .permitAll()
                )
                .csrf(csrf -> csrf.disable());
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(authenticationProvider);
    }

Upvotes: 0

Views: 101

Answers (1)

Andrey Smelik
Andrey Smelik

Reputation: 1266

The problem is in the expression

.anyRequest().permitAll()

This code disables secured access to all your endpoints.

If you replace it with

.anyRequest().authenticated()

then Spring Security authorization will work.

Upvotes: -1

Related Questions