Проблема с ролями в spring security

0

У меня есть веб-сервис, и мне нужно использовать авторизацию на основе ролей. У меня 3 роли, и все они не работают. Вот код моей конфигурации безопасности, я проверяла токен и там есть роль, видимо проблема в том что не видит headers.

`@Configuration @EnableWebSecurity public class AuthConfig {

@Bean
public HandlerMappingIntrospector mvcHandlerMappingIntrospector() {
    return new HandlerMappingIntrospector();
}
@Bean

public RestTemplate restTemplate() {
    return new RestTemplate();
}

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


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    
        http.authorizeHttpRequests(
                auth -> auth.requestMatchers("/api/auth/**").permitAll()
                        .requestMatchers("/api/admin/**").hasAuthority("ADMIN")
                        .requestMatchers("/api/student/**").hasAnyAuthority("STUDENT","ADMIN")
                        .requestMatchers("/api/teacher/**").hasAnyAuthority("TEACHER","ADMIN")
                        .anyRequest().authenticated())
                        .csrf(AbstractHttpConfigurer::disable)
                .httpBasic(withDefaults());
        return http.build();
    }

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

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

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

вот сервис студента

@Service public class StudentService {

@Autowired
private UserRepository userRepository;

@Autowired
private AssessmentItemRepository assessmentItemRepository;


public List<StudentGradeDTO> getStudentGrades(Long studentId) {
    User student = userRepository.findById(studentId).orElseThrow(() -> new RuntimeException("Student not found"));
    List<Subject> enrolledSubjects = student.getEnrolledSubjects();

    List<StudentGradeDTO> studentGrades = new ArrayList<>();
    for (Subject subject : enrolledSubjects) {
        List<AssessmentItem> assessments = assessmentItemRepository.findBySubjectAndStudent(subject, student);
        for (AssessmentItem assessment : assessments) {
            StudentGradeDTO gradeDTO = new StudentGradeDTO();
            gradeDTO.setSubjectName(subject.getName());
            gradeDTO.setType(assessment.getType());
            gradeDTO.setExaminerName(assessment.getExaminerName());
            gradeDTO.setGrade(assessment.getGrade());
            gradeDTO.setExamDate(assessment.getExamDate());

            studentGrades.add(gradeDTO);
        }
    }

    return studentGrades;
}




public List<AssessmentItem> getUpcomingExamsForStudent(Long studentId) {
    User student = userRepository.findById(studentId)
            .orElseThrow(() -> new RuntimeException("Student not found"));

    List<AssessmentItem> upcomingExams = new ArrayList<>();
    LocalDateTime currentDateTime = LocalDateTime.now();

    for (Subject subject : student.getEnrolledSubjects()) {
        List<AssessmentItem> exams = assessmentItemRepository.findBySubjectAndTypeAndExamDateAfter(
                subject, "Exam", currentDateTime);
        upcomingExams.addAll(exams);
    }

    return upcomingExams;
}
}

контроллер

@RestController
@RequestMapping("/api/student")
public class StudentController {
    @Autowired
    private StudentService studentService;

    @GetMapping("/grades/{studentId}")
    public ResponseEntity<List<StudentGradeDTO>> getStudentGrades(@PathVariable Long studentId) {
        List<StudentGradeDTO> studentGrades = studentService.getStudentGrades(studentId);
        return ResponseEntity.ok(studentGrades);
    }
    @GetMapping("/upcoming-exams/{studentId}")
    public ResponseEntity<List<AssessmentItem>> getUpcomingExams(@PathVariable Long studentId) {
        List<AssessmentItem> upcomingExams = studentService.getUpcomingExamsForStudent(studentId);
        return ResponseEntity.ok(upcomingExams);
    }

и когда я отправляю запрос например http://localhost:8080/api/student/grades/1 и с headers Authorization "Bearer + token", но я получаю ошибку 401 и вот дебаг из spring 2023-12-08T15:17:49.468+02:00 DEBUG 39093 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Securing GET /api/student/grades/1 2023-12-08T15:17:49.491+02:00 DEBUG 39093 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext 2023-12-08T15:17:49.513+02:00 DEBUG 39093 --- [nio-8080-exec-2] o.s.s.w.s.HttpSessionRequestCache : Saved request http://localhost:8080/api/student/grades/1?continue to session 2023-12-08T15:17:49.514+02:00 DEBUG 39093 --- [nio-8080-exec-2] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest] 2023-12-08T15:17:49.514+02:00 DEBUG 39093 --- [nio-8080-exec-2] s.w.a.DelegatingAuthenticationEntryPoint : No match found. Using default entry point org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint@78ae146f 2023-12-08T15:17:49.516+02:00 DEBUG 39093 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Securing GET /error 2023-12-08T15:17:49.520+02:00 DEBUG 39093 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext 2023-12-08T15:17:49.523+02:00 DEBUG 39093 --- [nio-8080-exec-2] o.s.s.w.s.HttpSessionRequestCache : Saved request http://localhost:8080/error?continue to session 2023-12-08T15:17:49.523+02:00 DEBUG 39093 --- [nio-8080-exec-2] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest] 2023-12-08T15:17:49.523+02:00 DEBUG 39093 --- [nio-8080-exec-2] s.w.a.DelegatingAuthenticationEntryPoint : No match found. Using default entry point org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint@78ae146f

и я кладу роли в JWTs

@Component
public class JwtService {

    public static final String SECRET = "5367566B59703373367639792F423F4528482B4D6251655468576D5A71347437";

    public void validateToken(final String token) {
        parserBuilder().setSigningKey(getSignKey()).build().parseClaimsJws(token);
    }

    public String generateToken(String email, UserRole role) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("role", role);
        return createToken(claims, email);
    }

    private String createToken(Map<String, Object> claims, String userName) {
        return builder()
                .setClaims(claims)
                .setSubject(userName)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 30))
                .signWith(getSignKey(), SignatureAlgorithm.HS256).compact();
    }

    private Key getSignKey() {
        byte[] keyBytes = Decoders.BASE64.decode(SECRET);
        return Keys.hmacShaKeyFor(keyBytes);
    }

}

так кто-нибудь знает, в чем проблема? с помощью PermitAll() все конечно работает, но мне нужно использовать роли


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

Автор решения: Ilya Lisov

В вашем примере Вы нигде не достаете токен из запроса, вы делали фильтр?

У Spring Boot Security есть возможность настроить фильтр на запросы, который будет авторизовывать пользователей по токену из их запроса.

В своем проекте я реализовывал это здесь

JwtTokenFilter - это собственно фильтр, который парсит токен и авторизовывает пользователя. Его необходимо указать в настройке Spring Security здесь на строке 136 - теперь все запросы, которые приходят на контроллеры проходят через фильтр и устанавливается Principal для них, который и имеет роли.

JwtUserDetailsService - класс, который нужен Spring Security для представления вашего пользователя. Там есть username, password и список его ролей.

После этого на контроллере над методом (или даже над самим классом контроллера или в конфигурации SpringSecurity), который вы хотите проверять с помощью определенной роли можете указать аннотацию @PreAuthorize("hasAuthority('ROLE_ADMIN')") для администратора или по аналогии для любой другой роли. Аналогично Вы можете использовать @PreAuthorize("hasRole('ADMIN')")

Если ответ Вам помог, проголосуйте за него, как за решение вопроса.

→ Ссылка