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 шт):
Вопрос решен!
Решение: Проблема была в том, что у меня в JwtTokenProvider в методе resolveToken читается заголовок - "Authentication", хотя нужно было читать "Authorization".