Не работают роли с JWT

такая проблема что сейчас изучаю JWT , делаю с ним авторизацию . На моменте где хочу сделать доступ на определенный url только с ролью , либо авторизированным - не пускает - .requestMatchers("/user/**").hasRole("ADMIN"). После успешной авторизации должен быть переход по url /user/yes , но ошибка 403,если убрать требование роли то спокойно переходит. В headers токен передается из хранилища браузера. Не совсем понимаю этот момент с ролью. Подскажите что можно тут поправить

@Data
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(name = "ROLE")
    private String role;

    @NotEmpty(message = "Поле не должно быть пустым")
    @Size(min = 2, max = 30, message = "Имя пользователя должно иметь не менее 2 и не более 30 символов")
    @Email
    @Column(name = "EMAIL")
    private String email;

    @NotEmpty(message = "Поле не должно быть пустым")
   // @Size(min = 2, max = 30, message = "Пароль должен быть не менее 2 и не более 30 символов")
    @Column(name = "PASSWORD")
    private String password;

    @NotEmpty(message = "Поле не должно быть пустым")
    @Size(min = 2, max = 30, message = "Имя пользователя должно иметь не менее 2 и не более 30 символов")
    @Column(name = "USERNAME")
    private String userName;

}
@Configuration
public class JwtUtil {
@Value("${jwt_secret}")
    private String secret;
    public String generateToken(String email, String role) {
        Date expirationDate = Date.from(ZonedDateTime.now().plusMinutes(60).toInstant());
        return JWT.create()
                .withSubject("User details")
                .withClaim("email", email)
                .withClaim("authorities", role)
                .withIssuedAt(new Date())
                .withIssuer("registration")
                .withExpiresAt(expirationDate)
                .sign(Algorithm.HMAC256(secret));
    }
    public String validateTokenAndRetrieveClaim(String token) throws JWTVerificationException {
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret))
                .withSubject("User details")
                .withIssuer("registration")
                .build();

        DecodedJWT jwt = verifier.verify(token);
        return jwt.getClaim("email").asString();
    }
}

@Component
public class JWTFilter extends OncePerRequestFilter {
    private final JwtUtil jwtUtil;
    private final MyUserDetailsService userDetailsService;
    public JWTFilter(JwtUtil jwtUtil, MyUserDetailsService userDetailsService) {
        this.jwtUtil = jwtUtil;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain)
            throws ServletException, IOException {

        final String authHeader = request.getHeader("Authorization");
        System.out.println("Начался doFilterInternal");
        System.out.println("authHeader "+ authHeader);
        if (authHeader != null && authHeader.startsWith("Bearer ") && !authHeader.isBlank()) {
            String jwt = authHeader.substring(7);
            if (jwt.isBlank()) {
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Неправильный токен");
            } else {
                try {
                    String email = jwtUtil.validateTokenAndRetrieveClaim(jwt);
                    System.out.println("Сточка до userDetailsService.loadUserByUsername(email)");
                    UserDetails userDetails = userDetailsService.loadUserByUsername(email);
                    System.out.println("Сточка после userDetailsService.loadUserByUsername(email)");
                    System.out.println("userDetails "+ userDetails);
                    UsernamePasswordAuthenticationToken authToken =
                            new UsernamePasswordAuthenticationToken(userDetails,userDetails.getPassword(),
                                    userDetails.getAuthorities());

                    if (SecurityContextHolder.getContext().getAuthentication() == null) {
                        SecurityContextHolder.getContext().setAuthentication(authToken);
                    }
                }catch (JWTVerificationException exception) {
                    response.sendError(HttpServletResponse.SC_BAD_REQUEST,"Неправильный токен");
                }
            }
        }System.out.println("request"+request);
        System.out.println("response"+response);
        filterChain.doFilter(request,response);

    }
}
@Configuration
@EnableWebSecurity
Security
public class SecurityConfig{
    private final JWTFilter jwtFilter;

    public SecurityConfig(JWTFilter jwtFilter) {
        this.jwtFilter = jwtFilter;
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new MyUserDetailsService();
}
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(auth -> auth

                    .requestMatchers("/user/**").hasRole("ADMIN")
                    .requestMatchers("/","/static/**","/error","/auth/**").permitAll()
                    )
            .sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
            .formLogin(formLogin -> {
                        formLogin
                                .loginPage("/auth/login")
                                .loginProcessingUrl("/process_login")
                                .usernameParameter("username")
                                .passwordParameter("password")
                                .permitAll()
                                .defaultSuccessUrl("/user/yes", true)
                                .failureUrl("/auth/login?error=true")
                                .failureUrl("/auth/login?error");
                    }
            )
            .logout((logout) -> logout.logoutUrl("/logout").logoutSuccessUrl("/auth/login"))
                .sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)



            .build();

}

@Bean
public AuthenticationProvider authenticationProvider() {
    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    provider.setUserDetailsService(userDetailsService());
    provider.setPasswordEncoder(passwordEncoder());

    return provider;
}

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Bean
    public ServletWebServerFactory servletContainer() {
        return new TomcatServletWebServerFactory() {
            @Override
            protected void postProcessContext(Context context) {
                Rfc6265CookieProcessor rfc6265Processor = new Rfc6265CookieProcessor();
                rfc6265Processor.setSameSiteCookies("None");
                context.setCookieProcessor(rfc6265Processor);
            }
        };
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }
}

Страница HTML :

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
    <div class="wrapper">
        <form id="loginForm">
            <h1>Login</h1>
            <div class="input-box">
                <input type="text" id="email" placeholder="Email" required>
            </div>
            <div class="input-box">
                <input type="password" id="password" placeholder="Password" required>
            </div>
            <br/>
            <input type="submit" value="Login" class="btn">
        </form>
    </div>

    <script>
        document.getElementById("loginForm").addEventListener("submit", function(event) {
            event.preventDefault();

            const formData = {
                email: document.getElementById("email").value,
                password: document.getElementById("password").value
            };

            fetch('/auth/login', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(formData)
            })
                .then(response => response.json())
                .then(data => {
                    if (data.jwt) {
                        localStorage.setItem('Authorization', data.jwt);
                        makeAuthorizedRequest('/user/yes', data.jwt);
                    }
                })
                .catch(error => console.error('Error:', error));
        });

        function makeAuthorizedRequest(url, jwt) {
            const headers = new Headers();
            headers.append('Authorization','Bearer '+jwt);

            fetch(url, {
                method: 'GET',
                headers: headers
            })
                .then(response => {
                    // Обработка ответа от сервера
                })
                .catch(error => console.error('Error:', error));
        }

        // Добавляем код для отправки запроса с заголовком из localStorage сразу после загрузки страницы
        document.addEventListener("DOMContentLoaded", function() {
            const jwt = localStorage.getItem('Authorization');
            if (jwt) {
                makeAuthorizedRequest('/user/yes', jwt);
            }
        });
    </script>
    </body>
    </html>

И примерно такие данные передаются в токене

{
  "sub": "User details",
  "email": "mion@mail.ru",
  "authorities": "ROLE_ADMIN",
  "iat": 1711025019,
  "iss": "registration",
  "exp": 1711028619
}

Ответы (0 шт):