Не проверяет роли пользователя из jwt на выполнение методов через spring security
Есть класс с конфигурацией, где проверяется роль на выполнение определенных endpoint , все работает , jwt формируется , роль записывается в базе и при входе возвращается , но когда я указываю кому что разрешено выполнять , то он этого не делает. Можно указать токен любой роли , хоть админа хоть юзера и метод выполнится. Ошибка forbidden выходит если я слеш где то не правильно укажу , если токен вообще не указать , то будет 401 ошибка , что логично. В чем может быть проблема ? Как предоставлять использование метода по определенной роли ? На данный момент указание роли бессмысленно. Ошибки в логах нету , роль приходит при выполнении метода , я указал System.out.println(userDetails.getAuthorities()); в классе AuthTokenFilter. endpoint разные указывал и с ** и * и без них , он на них реагирует, но не на указанные роли.
Класс security :
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
@Autowired
UserDetailsServiceImpl userDetailsService;
@Autowired
private AuthEntryPointJwt unauthorizedHandler;
@Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/api/res/*").hasRole("USER")
.antMatchers("/api/auth/*").hasRole("ADMIN")
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
метод authenticationJwtTokenFilter() класса AuthTokenFilter :
public class AuthTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsServiceImpl userDetailsService;
// однократное выполнение каждого запроса API
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
// из токена получаю email
String email = jwtUtils.getEmailFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
System.out.println(userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Не получается настроить аутентификацию пользователя: ", e);
}
filterChain.doFilter(request, response);
}
// проверка jwt токена по параметру header в заголовке
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7, headerAuth.length());
}
return null;
}
}
JwtUtils:
public class JwtUtils {
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
private String jwtSecret;
private int jwtExpirationMs;
public String generateJwtToken(Authentication authentication) {
UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
return Jwts.builder().setSubject((userPrincipal.getEmail())).setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)).signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getEmailFromJwtToken(String token) {
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
}
// Валидация JWT токена
public boolean validateJwtToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
logger.error("Неверная JWT сигнатура: {}", e.getMessage());
} catch (MalformedJwtException e) {
logger.error("Неверный JWT токен: {}", e.getMessage());
} catch (ExpiredJwtException e) {
logger.error("Срок действия JWT токена истек: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
logger.error("JWT токен не поддерживается: {}", e.getMessage());
} catch (IllegalArgumentException e) {
logger.error("JWT токен пуст: {}", e.getMessage());
}
return false;
}
}
public class AuthEntryPointJwt implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
// метод будет запускаться каждый раз, когда не аутентифицированный пользователь запрашивает защищенный HTTP-ресурс
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException {
logger.error("Ошибка авторизации: {}", authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Ошибка! Пользователь не авторизован");
}
}
UserDetailsServiceImpl:
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
RepositoryUs repositoryUs;
@Override
@Transactional
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = repositoryUs.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("Пользователь с Email: " + email + " не найден"));
return UserDetailsImpl.build(user);
}
}
Ответы (1 шт):
Добавьте в БД к своим наименованиям ролям ROLE_ так как если вы используете hasRole, то spring security сам его добавляет и проверяет значение с БД (а если там без префиксов ROLE_) то может быть такая ситуация.
И в enum добавьте , где хранятся роли тоже эти префиксы . В security оставьте без них , если используете hasRole