Why can't i login using postman? I changed some files and this is the backend terminal that i get now:

2024-12-02T11:43:35.493+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] c.f.ecommerce.backend.Application        : Starting Application using Java 17.0.11 with PID 47444 (C:\Users\hassa\Documents\ecommerce.backend\target\classes started by hassa in C:\Users\hassa\Documents\ecommerce.backend)
2024-12-02T11:43:35.497+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] c.f.ecommerce.backend.Application        : Running with Spring Boot v3.3.5, Spring v6.1.14
2024-12-02T11:43:35.499+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] c.f.ecommerce.backend.Application        : No active profile set, falling back to 1 default profile: "default"
2024-12-02T11:43:35.545+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2024-12-02T11:43:35.546+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2024-12-02T11:43:36.491+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2024-12-02T11:43:36.579+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 80 ms. Found 7 JPA repository interfaces.    
2024-12-02T11:43:37.494+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-12-02T11:43:37.511+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-12-02T11:43:37.512+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.31]
2024-12-02T11:43:37.618+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-12-02T11:43:37.620+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2072 ms
2024-12-02T11:43:38.090+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-12-02T11:43:38.194+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.5.3.Final
2024-12-02T11:43:38.248+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.h.c.internal.RegionFactoryInitiator    : HHH000026: Second-level cache disabled
2024-12-02T11:43:38.524+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2024-12-02T11:43:38.551+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-12-02T11:43:38.740+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection ConnectionID:1 ClientConnectionId: ad043cce-1e8a-4f08-838a-949d8619212b
2024-12-02T11:43:38.742+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.    
2024-12-02T11:43:38.761+01:00  WARN 47444 --- [ecommerce.backend] [  restartedMain] org.hibernate.orm.deprecation            : HHH90000025: SQLServerDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
2024-12-02T11:43:39.709+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2024-12-02T11:43:40.246+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2024-12-02T11:43:40.785+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] c.f.e.b.api.security.JWTRequestFilter    : Filter 'JWTRequestFilter' configured for use
2024-12-02T11:43:41.415+01:00 TRACE 47444 --- [ecommerce.backend] [  restartedMain] o.s.w.s.s.s.WebSocketHandlerMapping      : No patterns in 'webSocketHandlerMapping'
2024-12-02T11:43:41.480+01:00 TRACE 47444 --- [ecommerce.backend] [  restartedMain] o.s.w.s.s.s.WebSocketHandlerMapping      : Mapped [/websocket/**] onto org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@4290e146
2024-12-02T11:43:41.480+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] o.s.w.s.s.s.WebSocketHandlerMapping      : Patterns [/websocket/**] in 'stompWebSocketHandlerMapping'
2024-12-02T11:43:41.547+01:00  WARN 47444 --- [ecommerce.backend] [  restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2024-12-02T11:43:41.610+01:00 TRACE 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler : Looking for @MessageExceptionHandler mappings: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@61be1e9c, started on Mon Dec 02 11:43:35 CET 2024     
2024-12-02T11:43:41.623+01:00 TRACE 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler : Detected @MessageExceptionHandler methods in org.springframework.web.socket.messaging.WebSocketAnnotationMethodMessageHandler$MessagingControllerAdviceBean@69844b4f
2024-12-02T11:43:41.637+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler :
        c.f.e.b.a.c.a.AuthenticationController:       

2024-12-02T11:43:41.639+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler :
        c.f.e.b.a.c.CartController:

2024-12-02T11:43:41.640+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler :
        c.f.e.b.a.c.ImageController:

2024-12-02T11:43:41.640+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler :
        c.f.e.b.a.c.o.OrderController:

2024-12-02T11:43:41.640+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler :
        c.f.e.b.a.c.p.ProductController:

2024-12-02T11:43:41.641+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler :
        c.f.e.b.a.c.u.UserController:

2024-12-02T11:43:41.650+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler :
        c.f.e.b.a.c.WebsocketController:
        {[MESSAGE],[/sendMessage]}: handleMessage(ChatMessage,String,StompHeaderAccessor)
        {[MESSAGE],[/test]}: handleTest(String)       
        {[MESSAGE],[/admin/message]}: handleAdminMessage(ChatMessage,String,StompHeaderAccessor)
2024-12-02T11:43:41.661+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] .WebSocketAnnotationMethodMessageHandler :
        o.s.b.a.w.s.e.BasicErrorController:

2024-12-02T11:43:42.429+01:00  WARN 47444 --- [ecommerce.backend] [  restartedMain] .s.s.UserDetailsServiceAutoConfiguration :

Using generated security password: 075dcd22-3669-4731-9d70-5fc9fe933e4f

This generated password is for development use only. Your security configuration must be updated before running your application in production.

2024-12-02T11:43:42.443+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name inMemoryUserDetailsManager
2024-12-02T11:43:42.514+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint beneath base path '/actuator'
2024-12-02T11:43:42.617+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2024-12-02T11:43:42.674+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-12-02T11:43:42.676+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] o.s.m.s.ExecutorSubscribableChannel      : clientOutboundChannel added SubProtocolWebSocketHandler[StompSubProtocolHandler[v10.stomp, v11.stomp, v12.stomp]]
2024-12-02T11:43:42.676+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] o.s.m.s.ExecutorSubscribableChannel      : clientInboundChannel added WebSocketAnnotationMethodMessageHandler[prefixes=[/app/]]     
2024-12-02T11:43:42.676+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.s.m.s.b.SimpleBrokerMessageHandler     : Starting...
2024-12-02T11:43:42.676+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] o.s.m.s.ExecutorSubscribableChannel      : clientInboundChannel added SimpleBrokerMessageHandler [org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry@5b3ef3b0]
2024-12-02T11:43:42.676+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] o.s.m.s.ExecutorSubscribableChannel      : brokerChannel added SimpleBrokerMessageHandler [org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry@5b3ef3b0]
2024-12-02T11:43:42.677+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.s.m.s.b.SimpleBrokerMessageHandler     : BrokerAvailabilityEvent[available=true, SimpleBrokerMessageHandler [org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry@5b3ef3b0]]
2024-12-02T11:43:42.677+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] o.s.m.s.b.SimpleBrokerMessageHandler     : Started.
2024-12-02T11:43:42.678+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] o.s.m.s.ExecutorSubscribableChannel      : clientInboundChannel added UserDestinationMessageHandler[DefaultUserDestinationResolver[prefix=/user/]]
2024-12-02T11:43:42.678+01:00 DEBUG 47444 --- [ecommerce.backend] [  restartedMain] o.s.m.s.ExecutorSubscribableChannel      : brokerChannel added UserDestinationMessageHandler[DefaultUserDestinationResolver[prefix=/user/]]
2024-12-02T11:43:42.687+01:00  INFO 47444 --- [ecommerce.backend] [  restartedMain] c.f.ecommerce.backend.Application        : Started Application in 7.502 seconds (process running for 7.966)
2024-12-02T11:43:44.420+01:00  INFO 47444 --- [ecommerce.backend] [on(2)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-12-02T11:43:44.420+01:00  INFO 47444 --- [ecommerce.backend] [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-12-02T11:43:44.422+01:00  INFO 47444 --- [ecommerce.backend] [on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms   
2024-12-02T11:44:42.640+01:00  INFO 47444 --- [ecommerce.backend] [MessageBroker-1] o.s.w.s.c.WebSocketMessageBrokerStats    : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]




Here's the neccessary files that i changed and have now:

package com.foodstore.ecommerce.backend.api.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

// WebSocketChannelInterceptor.java (update your existing one)
@Component
public class WebSocketChannelInterceptor implements ChannelInterceptor {

    private final RateLimiter rateLimiter;
    private final MessageValidator messageValidator;
    private static final Logger logger = LoggerFactory.getLogger(WebSocketChannelInterceptor.class);

    public WebSocketChannelInterceptor(RateLimiter rateLimiter, MessageValidator messageValidator) {
        this.rateLimiter = rateLimiter;
        this.messageValidator = messageValidator;
    }

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);

        if (accessor != null) {
            try {
                if (accessor.getCommand() == null) {
                    return message;
                }

                switch (accessor.getCommand()) {
                    case CONNECT:
                        logger.debug("Processing CONNECT command");
                        return handleConnect(message, accessor);
                    case SUBSCRIBE:
                        logger.debug("Processing SUBSCRIBE command");
                        return handleSubscribe(message, accessor);
                    case SEND:
                        logger.debug("Processing SEND command");
                        return handleMessageSend(message, accessor);
                    default:
                        return message;
                }
            } catch (Exception e) {
                logger.error("Exception in WebSocketChannelInterceptor: {}", e.getMessage(), e);
                return createErrorMessage(accessor, "Internal server error: " + e.getMessage());
            }
        }
        return message;
    }

    private Message<?> handleConnect(Message<?> message, StompHeaderAccessor accessor) {
        Authentication auth = (Authentication) accessor.getUser();
        if (auth == null) {
            logger.warn("Connection attempt without authentication");
            return createErrorMessage(accessor, "Connection attempt without authentication");
        }
        logger.info("User {} connected", auth.getName());
        return message;
    }

    private Message<?> handleSubscribe(Message<?> message, StompHeaderAccessor accessor) {
        Authentication auth = (Authentication) accessor.getUser();
        String destination = accessor.getDestination();

        if (auth == null) {
            logger.warn("Subscription attempt without authentication");
            return createErrorMessage(accessor, "Subscription attempt without authentication");
        }

        if (destination != null) {
            logger.info("User {} subscribing to {}", auth.getName(), destination);
            // You can add rate limiting or validation logic here if needed
        }

        return message;
    }

    private Message<?> handleMessageSend(Message<?> message, StompHeaderAccessor accessor) {
        Authentication auth = (Authentication) accessor.getUser();
        if (auth == null) {
            logger.warn("Message send attempt without authentication");
            return createErrorMessage(accessor, "Message send attempt without authentication");
        }

        // Validate message if it's a ChatMessage
        if (message.getPayload() instanceof ChatMessage) {
            try {
                messageValidator.validate((ChatMessage) message.getPayload());
            } catch (ValidationException e) {
                logger.warn("Message validation failed for user {}: {}", auth.getName(), e.getMessage());
                return createErrorMessage(accessor, "Invalid message content: " + e.getMessage());
            }
        }

        return message;
    }

    private Message<?> createErrorMessage(StompHeaderAccessor accessor, String errorText) {
        StompHeaderAccessor errorAccessor = StompHeaderAccessor.create(StompCommand.ERROR);
        errorAccessor.setMessage(errorText);
        errorAccessor.setSessionId(accessor.getSessionId());
        errorAccessor.setUser(accessor.getUser());

        return MessageBuilder.createMessage(new byte[0], errorAccessor.getMessageHeaders());
    }

    @Override
    public void afterSendCompletion(
            Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
        if (ex != null) {
            logger.error("Exception in WebSocketChannelInterceptor afterSendCompletion: {}", ex.getMessage(), ex);
            // Handle the exception as needed
        }
    }
}



package com.foodstore.ecommerce.backend.api.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import org.springframework.web.socket.messaging.StompSubProtocolErrorHandler;

import com.foodstore.ecommerce.backend.model.LocalUser;
import com.foodstore.ecommerce.backend.model.service.UserService;

import jakarta.validation.ValidationException;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;

import com.foodstore.ecommerce.backend.api.security.ErrorMessage;
import com.foodstore.ecommerce.backend.api.security.WebSocketSessionRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.socket.config.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;

@Configuration
@EnableWebSocket
@EnableWebSocketMessageBroker
public class WebsocketConfiguration implements WebSocketMessageBrokerConfigurer {

    private static final Logger logger = LoggerFactory.getLogger(WebsocketConfiguration.class);
    private final ApplicationContext context;
    private final JWTRequestFilter jwtRequestFilter;
    private final UserService userService;
    private final SimpMessagingTemplate messagingTemplate;
    private final RateLimiter rateLimiter;
    private final MessageValidator messageValidator;
    private final WebSocketSessionRegistry sessionRegistry;
    private static final AntPathMatcher MATCHER = new AntPathMatcher();
    private final WebSocketSecurityInterceptor securityInterceptor;
    private final WebSocketChannelInterceptor channelInterceptor;
    private ExceptionHandlingChannelInterceptor exceptionHandlingChannelInterceptor;

    public WebsocketConfiguration(
            ApplicationContext context,
            JWTRequestFilter jwtRequestFilter,
            UserService userService,
            @Lazy SimpMessagingTemplate messagingTemplate,
            RateLimiter rateLimiter,
            @Lazy MessageValidator messageValidator,
            @Lazy WebSocketSessionRegistry sessionRegistry,
            WebSocketSecurityInterceptor securityInterceptor,
            WebSocketChannelInterceptor channelInterceptor,
            ExceptionHandlingChannelInterceptor exceptionHandlingChannelInterceptor) {
        this.context = context;
        this.jwtRequestFilter = jwtRequestFilter;
        this.userService = userService;
        this.messagingTemplate = messagingTemplate;
        this.rateLimiter = rateLimiter;
        this.messageValidator = messageValidator;
        this.sessionRegistry = sessionRegistry;
        this.securityInterceptor = securityInterceptor;
        this.channelInterceptor = channelInterceptor;
        this.exceptionHandlingChannelInterceptor = exceptionHandlingChannelInterceptor;
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket")
                .setAllowedOriginPatterns("*")
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.setApplicationDestinationPrefixes("/app");
        registry.enableSimpleBroker("/topic", "/queue")
                .setHeartbeatValue(new long[] { 25000, 25000 })
                .setTaskScheduler(heartBeatScheduler());
    }

    private void validateSubscription(StompHeaderAccessor accessor) {
        String destination = accessor.getDestination();
        if (destination == null) {
            throw new ValidationException("Subscription destination cannot be null");
        }

        if (!destination.startsWith("/topic/user/")) {
            throw new ValidationException("Invalid subscription destination");
        }

        Authentication auth = (Authentication) accessor.getUser();
        if (auth == null) {
            throw new ValidationException("User not authenticated");
        }

        try {
            String[] parts = destination.split("/");
            Long userId = Long.parseLong(parts[3]);
            LocalUser user = (LocalUser) auth.getPrincipal();
            if (!user.getId().equals(userId)
                    && !auth.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
                throw new ValidationException("Unauthorized subscription");
            }
        } catch (Exception e) {
            throw new ValidationException("Invalid subscription format");
        }
    }

    // Add reconnection strategy
    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        registration
                .setMessageSizeLimit(128 * 1024)
                .setSendTimeLimit(20 * 1000)
                .setSendBufferSizeLimit(512 * 1024)
                .addDecoratorFactory(handler -> new WebSocketHandlerDecorator(handler) {
                    @Override
                    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
                        super.afterConnectionEstablished(session);
                        sessionRegistry.registerSession(session);
                    }

                    @Override
                    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
                        super.afterConnectionClosed(session, status);
                        sessionRegistry.removeSession(session.getId());
                    }
                })
                .addDecoratorFactory(webSocketHandler -> new EmaWebSocketHandlerDecorator(webSocketHandler));
    }

    // Add error handling for subscription failures
    @Override
public void configureClientInboundChannel(ChannelRegistration registration) {
    // Create and configure the thread pool executor
    ThreadPoolTaskExecutor clientInboundExecutor = new ThreadPoolTaskExecutor();
    clientInboundExecutor.setCorePoolSize(10);
    clientInboundExecutor.setMaxPoolSize(100);
    clientInboundExecutor.setQueueCapacity(500);
    clientInboundExecutor.setKeepAliveSeconds(60);
    clientInboundExecutor.setThreadNamePrefix("clientInboundChannel-");
    clientInboundExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    clientInboundExecutor.initialize();

    // Set the task executor
    registration.taskExecutor(clientInboundExecutor);

    // Set the interceptors
    registration.interceptors(
            exceptionHandlingChannelInterceptor, // First
            securityInterceptor,
            channelInterceptor
    );
}

    private Authentication checkAuthToken(String bearerToken) {
        String token = bearerToken.startsWith("Bearer ") ? bearerToken.substring(7) : bearerToken;
        try {
            return jwtRequestFilter.checkToken(token);
        } catch (Exception e) {
            logger.error("Error validating WebSocket token", e);
            return null;
        }
    }

    private class RejectClientMessagesOnChannelsChannelInterceptor implements ChannelInterceptor {
        private final String[] protectedPaths = {
                "/topic/user/*/address"
        };

        @Override
        public Message<?> preSend(Message<?> message, MessageChannel channel) {
            StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
            if (accessor != null && accessor.getMessageType() == SimpMessageType.MESSAGE) {
                String destination = accessor.getDestination();
                if (destination != null) {
                    for (String path : protectedPaths) {
                        if (MATCHER.match(path, destination)) {
                            logger.warn("Rejected client message to protected destination: {}", destination);
                            return null;
                        }
                    }
                }
            }
            return message;
        }
    }

    private class DestinationLevelAuthorizationChannelInterceptor implements ChannelInterceptor {
        @Override
        public Message<?> preSend(Message<?> message, MessageChannel channel) {
            StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
            if (accessor != null && accessor.getMessageType() == SimpMessageType.SUBSCRIBE) {
                String destination = accessor.getDestination();
                if (destination != null) {
                    String userTopicMatcher = "/topic/user/{userId}/**";
                    if (MATCHER.match(userTopicMatcher, destination)) {
                        logger.info("Processing subscription request to: {}", destination);
                        // Add any additional subscription authorization logic here if needed
                    }
                }
            }
            return message;
        }
    }

    private AuthorizationManager<Message<?>> makeMessageAuthorizationManager() {
        MessageMatcherDelegatingAuthorizationManager.Builder messages = new MessageMatcherDelegatingAuthorizationManager.Builder();
        messages
                .simpDestMatchers("/topic/user/**").authenticated()
                .simpDestMatchers("/app/**").authenticated()
                .anyMessage().permitAll();
        return messages.build();
    }

    @Bean
    public TaskScheduler websocketHeartbeatScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(1);
        scheduler.setThreadNamePrefix("websocket-heartbeat-thread-");
        scheduler.setDaemon(true);
        scheduler.initialize();
        return scheduler;
    }

    // Register the custom error handler
    // Register the custom error handler

    @Bean
    public TaskScheduler heartBeatScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(1);
        scheduler.setThreadNamePrefix("ws-heartbeat-");
        scheduler.setDaemon(true);
        return scheduler;
    }
}



package com.foodstore.ecommerce.backend.api.security;

import java.security.Principal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;

@ControllerAdvice
public class WebSocketErrorHandler {

    private final SimpMessagingTemplate messagingTemplate;
    private static final Logger logger = LoggerFactory.getLogger(WebSocketErrorHandler.class);

    @Autowired
    public WebSocketErrorHandler(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @MessageExceptionHandler(AccessDeniedException.class)
    public void handleAccessDeniedException(AccessDeniedException exception, Principal principal) {
        String errorMessage = "Access denied: " + exception.getMessage();
        logger.error(errorMessage, exception);

        if (principal != null) {
            messagingTemplate.convertAndSendToUser(
                    principal.getName(),
                    "/queue/errors",
                    new ErrorMessage(errorMessage));
        }
    }

    @MessageExceptionHandler(Exception.class)
    public void handleException(Exception exception, Principal principal) {
        String errorMessage = "Error processing message: " + exception.getMessage();
        logger.error(errorMessage, exception);

        if (principal != null) {
            messagingTemplate.convertAndSendToUser(
                    principal.getName(),
                    "/queue/errors",
                    new ErrorMessage(errorMessage));
        }
    }
}



package com.foodstore.ecommerce.backend.api.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.messaging.MessageSecurityMetadataSourceRegistry;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    @Autowired
    private WebSocketSecurityService webSocketSecurityService;

    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages
                .simpDestMatchers("/topic/admin/**").hasRole("ADMIN")
                .simpDestMatchers("/topic/user/{userId}/admin-updates/**").hasRole("ADMIN")
                .simpDestMatchers("/topic/user/{userId}/**")
                .access("@webSocketSecurityService.checkUserId(authentication, #userId)")
                .simpDestMatchers("/topic/public/**").authenticated()
                .simpDestMatchers("/app/**").authenticated()
                .anyMessage().authenticated();
    }

    @Override
    protected boolean sameOriginDisabled() {
        return true; // Disable CSRF for WebSocket endpoints
    }
}



package com.foodstore.ecommerce.backend.api.security;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

@Component
public class WebSocketSecurityInterceptor implements ChannelInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(WebSocketSecurityInterceptor.class);

    private static final AntPathMatcher matcher = new AntPathMatcher();

    @Autowired
    private WebSocketSecurityService webSocketSecurityService;

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {

        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);

        if (accessor != null) {
            if (accessor.getCommand() == null) {
                return message;
            }

            switch (accessor.getCommand()) {
                case SUBSCRIBE:
                    return handleSubscribe(message, accessor);
                case SEND:
                    return handleSend(message, accessor);
                default:
                    return message;
            }
        }

        return message;
    }

    private Message<?> handleSubscribe(Message<?> message, StompHeaderAccessor accessor) {
        Authentication authentication = (Authentication) accessor.getUser();
        String destination = accessor.getDestination();

        if (authentication != null && destination != null) {
            String userId = extractUserIdFromDestination(destination);
            if (userId == null) {
                logger.warn("Invalid destination format: {}", destination);
                return createErrorMessage(accessor, "Invalid destination format.");
            }
            if (!webSocketSecurityService.checkUserId(authentication, userId)) {
                logger.warn("Access denied for user: {}", authentication.getName());
                return createErrorMessage(accessor, "Access denied: insufficient permissions.");
            }
        } else {
            logger.warn("Unauthenticated subscription attempt");
            return createErrorMessage(accessor, "Unauthenticated subscription attempt.");
        }

        return message; // Continue processing
    }

    private Message<?> handleSend(Message<?> message, StompHeaderAccessor accessor) {
        Authentication authentication = (Authentication) accessor.getUser();

        if (authentication != null) {
            boolean isAdmin = authentication.getAuthorities().stream()
                    .anyMatch(auth -> auth.getAuthority().equals("ROLE_ADMIN"));

            if (!isAdmin) {
                logger.warn("Access denied for user: {}", authentication.getName());
                return createErrorMessage(accessor, "Access denied: insufficient permissions.");
            }
        } else {
            logger.warn("Unauthenticated message send attempt");
            return createErrorMessage(accessor, "Unauthenticated message send attempt.");
        }

        return message;
    }

    private String extractUserIdFromDestination(String destination) {
        String pattern = "/topic/user/{userId}/**";

        if (matcher.match(pattern, destination)) {
            Map<String, String> variables = matcher.extractUriTemplateVariables(pattern, destination);
            return variables.get("userId");
        } else {
            logger.warn("Destination does not match pattern: {}", destination);
            return null; // Or return an error message
        }
    }

    private Message<?> createErrorMessage(StompHeaderAccessor accessor, String errorText) {
        StompHeaderAccessor errorAccessor = StompHeaderAccessor.create(StompCommand.ERROR);
        errorAccessor.setSessionId(accessor.getSessionId());
        errorAccessor.setUser(accessor.getUser());
        errorAccessor.setMessage(errorText);

        return MessageBuilder.createMessage(new byte[0], errorAccessor.getMessageHeaders());
    }
}




package com.foodstore.ecommerce.backend.api.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import com.foodstore.ecommerce.backend.model.LocalUser;

// WebSocketSecurityService.java
@Service
public class WebSocketSecurityService {

    private static final Logger logger = LoggerFactory.getLogger(WebSocketSecurityService.class);

    public boolean checkUserId(Authentication authentication, String userId) {
        if (authentication == null || !(authentication.getPrincipal() instanceof LocalUser)) {
            logger.warn("Invalid authentication or principal");
            return false;
        }

        LocalUser user = (LocalUser) authentication.getPrincipal();

        // Allow admins to access all channels
        if (user.getRole() == LocalUser.UserRole.ADMIN) {
            logger.debug("Admin access granted for user {}", user.getUsername());
            return true;
        }

        // Allow users to access only their own channels
        boolean hasAccess = user.getId().toString().equals(userId);
        logger.debug("User {} access to channel {}: {}", user.getUsername(), userId, hasAccess);
        return hasAccess;
    }
}



package com.foodstore.ecommerce.backend.api.security;

import java.security.Principal;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;

// WebSocketSessionRegistry.java
@Component
public class WebSocketSessionRegistry {
    private final ConcurrentHashMap<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Set<String>> userSessions = new ConcurrentHashMap<>();
    private static final Logger logger = LoggerFactory.getLogger(WebSocketSessionRegistry.class);

    public void registerSession(WebSocketSession session) {
        sessions.put(session.getId(), session);
        Principal principal = session.getPrincipal();
        if (principal != null) {
            userSessions.computeIfAbsent(principal.getName(), k -> ConcurrentHashMap.newKeySet())
                       .add(session.getId());
            logger.info("Session registered for user: {}", principal.getName());
        }
    }

    public void removeSession(String sessionId) {
        WebSocketSession session = sessions.remove(sessionId);
        if (session != null && session.getPrincipal() != null) {
            String username = session.getPrincipal().getName();
            Set<String> userSessionSet = userSessions.get(username);
            if (userSessionSet != null) {
                userSessionSet.remove(sessionId);
                if (userSessionSet.isEmpty()) {
                    userSessions.remove(username);
                }
            }
            logger.info("Session removed for user: {}", username);
        }
    }

    public Set<WebSocketSession> getUserSessions(String username) {
        Set<String> sessionIds = userSessions.getOrDefault(username, Collections.emptySet());
        return sessionIds.stream()
                        .map(sessions::get)
                        .filter(Objects::nonNull)
                        .collect(Collectors.toSet());
    }
}





And i commented out this whole file:

// package com.foodstore.ecommerce.backend.api.security;

// import java.util.Arrays;

// import org.springframework.context.annotation.Bean;
// import org.springframework.context.annotation.Configuration;
// import org.springframework.messaging.handler.annotation.MessageMapping;
// import org.springframework.messaging.handler.annotation.SendTo;
// import org.springframework.security.config.annotation.web.builders.HttpSecurity;
// import org.springframework.security.web.SecurityFilterChain;
// import org.springframework.security.web.access.intercept.AuthorizationFilter;
// import org.springframework.stereotype.Controller;
// import org.springframework.web.cors.CorsConfiguration;
// import org.springframework.web.cors.CorsConfigurationSource;
// import org.springframework.web.cors.UrlBasedCorsConfigurationSource;


// @Configuration
// public class WebSecurityConfig {
// private JWTRequestFilter jwtRequestFilter;

// public WebSecurityConfig(JWTRequestFilter jwtRequestFilter) {
//     this.jwtRequestFilter = jwtRequestFilter;
// }

// @Bean
// public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//     http
//             .csrf().disable()
//             .cors() // Enable CORS (remove the .disable())
//             .and()
//             .headers().cacheControl().disable()
//             .and()
//             .addFilterBefore(jwtRequestFilter, AuthorizationFilter.class)
//             .authorizeHttpRequests()
//             .requestMatchers("/", "/product", "/auth/register", "/auth/login", "/auth/verify", "/error",
//                     "/auth/forgot",
//                     "/auth/reset", "/websocket", "/websocket/**",
//                     "/websocket",
//                     "/websocket/info",
//                     "/websocket/websocket/**", "/images/**", "/placeholder-image.jpg")
//             .permitAll().requestMatchers("/product/admin/**").hasRole("ADMIN")
//             .anyRequest().authenticated();
//     return http.build();
// }

// @Bean
// public CorsConfigurationSource corsConfigurationSource() {
//     CorsConfiguration configuration = new CorsConfiguration();
//     configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
//     configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
//     configuration.setAllowedHeaders(Arrays.asList(
//             "Authorization",
//             "Content-Type",
//             "Accept",
//             "x-requested-with",
//             "x-auth-token"));
//     configuration.setAllowCredentials(true);
//     configuration.setMaxAge(3600L);

//     UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//     source.registerCorsConfiguration("/**", configuration);
//     return source;
// }


//     @Controller
//     public class WebSocketController {
//         @MessageMapping("/profile")
//         @SendTo("/topic/profile")
//         public String handleProfile(String message) {
//             System.out.println("Message received at /profile: " + message);
//             return "Response from /profile: " + message;
//         }
//     }

    
    


// }