Проблема с ролями в 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 шт):
В вашем примере Вы нигде не достаете токен из запроса, вы делали фильтр?
У Spring Boot Security есть возможность настроить фильтр на запросы, который будет авторизовывать пользователей по токену из их запроса.
В своем проекте я реализовывал это здесь
JwtTokenFilter - это собственно фильтр, который парсит токен и авторизовывает пользователя. Его необходимо указать в настройке Spring Security здесь на строке 136 - теперь все запросы, которые приходят на контроллеры проходят через фильтр и устанавливается Principal для них, который и имеет роли.
JwtUserDetailsService - класс, который нужен Spring Security для представления вашего пользователя. Там есть username, password и список его ролей.
После этого на контроллере над методом (или даже над самим классом контроллера или в конфигурации SpringSecurity), который вы хотите проверять с помощью определенной роли можете указать аннотацию @PreAuthorize("hasAuthority('ROLE_ADMIN')") для администратора или по аналогии для любой другой роли. Аналогично Вы можете использовать @PreAuthorize("hasRole('ADMIN')")
Если ответ Вам помог, проголосуйте за него, как за решение вопроса.