Spring Security все время выдает ошибку 403

Пишу программу первый раз и хочу настроить Spring Security. К следюущему эдпоинту "/api/resumes/**" есть доступ только у тех пользователей, у которых роль равен "ROLE_USER" или "ROLE_ADMIN".

Авторизация и регистрация пользователя проходит штатно, ошибок не наблюдаются. Полученный токен указываю в Headers в поле "Authentication". но все равно система не дает обратиться к эдпоинту ".../api/resumes/"

В БД роли хранятся с префиксом "ROLE_": введите сюда описание изображения

Помогите пожалуйста разобраться в чем проблема. Я пока, что ещё не проверял как у пользователя с ролью ADMIN, но думаю там тоже есть проблемы. Ссылка на проект в репозитории - https://github.com/Startaper/course_paper_backend

Код программы:

SecurityConfig.class:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    @Bean
    public JwtTokenFilter jwtTokenFilter() {
        return new JwtTokenFilter();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new JwtUserDetailsServices();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable)
                .cors(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/registration/**", "/login/**").permitAll()
                        .requestMatchers("/admin/**").hasRole("ADMIN")
                        .anyRequest().authenticated()
                )
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authenticationProvider(daoAuthenticationProvider())
                .addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        return daoAuthenticationProvider;
    }

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

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

}

JwtTokenProvider.class:

@Component
public class JwtTokenProvider {

    @Value("${jwt.token.secret}")
    private String secret;
    @Value("${jwt.token.expired}")
    private long expiration;
    private SecretKey secretKey;
    @Autowired
    private UserDetailsService userDetailsService;

    @PostConstruct
    protected void init() {
        secret = Base64.getEncoder().encodeToString(secret.getBytes());
        secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));
    }

    public String createToken(String username) {
        Claims claims = Jwts.claims().setSubject(username);
//        claims.put("roles", getRoleNames(roles));

        Date now = new Date();
        Date validity = new Date(now.getTime() + expiration);

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(validity)
                .signWith(secretKey, SignatureAlgorithm.HS256)
                .compact();
    }

    public Authentication getAuthentication(String token) {
        UserDetails userDetails = userDetailsService.loadUserByUsername(getUsername(token));
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    public String getUsername(String token) {
        return Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJwt(token).getBody().getSubject();
    }

    public String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authentication");
        if (bearerToken != null && bearerToken.startsWith("Bearer")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    public boolean validateToken(String token) {
        try {
            Jws<Claims> claims = Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);

            return !claims.getBody().getExpiration().before(new Date());
        } catch (JwtException | IllegalArgumentException e) {
            throw new JwtAuthenticationException("JWT token is expired or invalid");
        }
    }

    private List<String> getRoleNames(List<RoleEntity> userRoles) {
        return userRoles.stream()
                .map(RoleEntity::getName)
                .collect(Collectors.toList());
    }

}

UserService.class:

@Service
public class UserService {

    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final BCryptPasswordEncoder passwordEncoder;

    @Autowired
    public UserService(UserRepository userRepository, RoleRepository roleRepository, BCryptPasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.passwordEncoder = passwordEncoder;
    }

    public UserEntity register(UserEntity user) throws InvalidFieldsException {
        List<RoleEntity> userRoles = Collections.singletonList(roleRepository.findByName("ROLE_USER"));

        if (user.getUsername() == null || user.getUsername().isBlank()) {
            throw new InvalidFieldsException("Логин и (или) пароль не могут быть пустыми!");
        }
        if (user.getPassword() == null || user.getPassword().isBlank()) {
            throw new InvalidFieldsException("Логин и (или) пароль не могут быть пустыми!");
        }
        if (user.getLastName() == null || user.getLastName().isBlank()) {
            throw new InvalidFieldsException("Фамилия не может быть пустым!");
        }
        if (user.getFirstName() == null || user.getFirstName().isBlank()) {
            throw new InvalidFieldsException("Имя не может быть пустым!");
        }
        if (user.getEmail() == null || user.getEmail().isBlank()) {
            throw new InvalidFieldsException("Email не может быть пустым!");
        }

        user.setRoles(userRoles);
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        user.setStatus(UserStatus.ACTIVE);
        user.setCreatedAt(new Date());
        user.setUpdatedAt(new Date());

        return userRepository.save(user);
    }

    public List<UserEntity> getAll() {
        List<UserEntity> users = new ArrayList<>();
        userRepository.findAll().forEach(users::add);
        return users;
    }

    public List<RoleEntity> getUserRolesByUsername(String username) throws UsernameNotFoundException {
        UserEntity user = userRepository.findByUsername(username);

        if (user == null) {
            throw new UsernameNotFoundException("Пользователь с указанным username: " + username + " не найден!");
        }

        return user.getRoles();
    }

    public UserEntity findByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    public UserEntity findById(Long id) throws NotFoundException {
        return userRepository.findById(id)
                .orElseThrow(() -> new NotFoundException("Пользователь с указанным id не найден!"));
    }

    public void delete(Long id) throws NotFoundException {
        UserEntity user = findById(id);
        userRepository.delete(user);
    }

}

AuthController.class:

@RestController
@RequestMapping("/")
public class AuthController {

    private final AuthenticationManager authenticationManager;
    private final JwtTokenProvider jwtTokenProvider;
    private final UserService userService;

    @Autowired
    public AuthController(AuthenticationManager authenticationManager, JwtTokenProvider jwtTokenProvider, UserService userService) {
        this.authenticationManager = authenticationManager;
        this.jwtTokenProvider = jwtTokenProvider;
        this.userService = userService;
    }
    @PostMapping("/login")
    public ResponseEntity login(@RequestBody UserEntity user) {
        Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
        if (authentication.isAuthenticated()) {
            user = userService.findByUsername(user.getUsername());
            return ResponseEntity.ok(jwtTokenProvider.createToken(user.getUsername()));
        } else {
            throw new UsernameNotFoundException("Invalid user request");
        }
    }

    @PostMapping("/registration")
    public ResponseEntity register(@RequestBody UserEntity user) throws InvalidFieldsException {
        user = userService.register(user);
        return ResponseEntity.ok(jwtTokenProvider.createToken(user.getUsername()));
    }

}

ДОПОЛНЕНИЕ

JwtUserDetailsServices.class:

@Service
public class JwtUserDetailsServices implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserEntity user = userService.findByUsername(username);

        if (user == null) {
            throw new UsernameNotFoundException("Пользователь с указанным username: " + username + " не найден!");
        }

        return JwtUserFactory.create(user);
    }

}

ДОПОЛНЕНИЕ 2:

JwtUserFactory.class:

public final class JwtUserFactory {

    public JwtUserFactory() {
    }

    public static JwtUserDetails create(UserEntity user) {
        return new JwtUserDetails(
                user.getId(),
                user.getUsername(),
                user.getLastName(),
                user.getFirstName(),
                user.getPassword(),
                user.getEmail(),
                user.getStatus().equals(UserStatus.ACTIVE),
                user.getUpdatedAt(),
                mapToGrantedAuthorities(user.getRoles()));
    }

    private static List<GrantedAuthority> mapToGrantedAuthorities(List<RoleEntity> userRoles) {
        return userRoles.stream()
                .map(role -> new SimpleGrantedAuthority(role.getName()))
                .collect(Collectors.toList());
    }

}

JwtTokenFilter.class:

public class JwtTokenFilter extends GenericFilterBean {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        String token = jwtTokenProvider.resolveToken((HttpServletRequest) servletRequest);

        if (token != null && jwtTokenProvider.validateToken(token)) {
            Authentication authentication = jwtTokenProvider.getAuthentication(token);

            if (authentication != null) {
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }
}

JwtConfigurer.class:

public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    public void configure(HttpSecurity httpSecurity) throws Exception {
        JwtTokenFilter jwtTokenFilter = new JwtTokenFilter();
        httpSecurity.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

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

Автор решения: Azamat

Вопрос решен!

Решение: Проблема была в том, что у меня в JwtTokenProvider в методе resolveToken читается заголовок - "Authentication", хотя нужно было читать "Authorization".

→ Ссылка