Я потратил несколько дней, пытаясь заставить РОЛИ работать в Spring Security с использованием токенов JWT . Кажется, аутентификация работает нормально, но авторизация , похоже, все еще не работает.
Моя текущая настройка заключается в том, что у меня есть ...
- База данных в памяти для Пользователи (у меня есть один единственный пользователь с именем «лев» и одна роль «KING»)
- конечная точка аутентификации, которая аутентифицирует пользователя и возвращает JWT в ответе.
My WebCofig выглядит следующим образом:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/hello").permitAll()
.antMatchers("/authenticate").permitAll()
.anyRequest().authenticated()
.and().exceptionHandling()
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
Я хочу сказать, что
- каждый может получить доступ (/)
- любой может получить доступ к "hello" и "authenticate"
Мой веб-контроллер выглядит следующим образом:
@Controller
public class WebController {
@PreAuthorize("permitAll()")
@RequestMapping("/hello")
@ResponseBody
public String hello() {
return "Hello! ALL USERS logged in or not are allowed to see this!";
}
@PreAuthorize("hasRole('KING')")
@RequestMapping("/king")
@ResponseBody
public String king() {
return "Hello King";
}
@PreAuthorize("hasRole('ROLE_KING')")
@RequestMapping("/king2")
@ResponseBody
public String king2() {
return "Hello King2";
}
}
Мой веб-токен, возвращенный через мою аутентификацию, выглядит хорошо (я думаю ..). Декодированное тело выглядит так:
{
"sub": "lion",
"scopes": "KING",
"iat": 1580645802,
"exp": 1580663802
}
... и позволит мне получить доступ куда угодно, где мне просто нужно пройти аутентификацию. "anyRequest (). authenticated ()".
Однако конечные точки REST king и king2 дают мне 403 - "Отказано в доступе" , даже если у моего пользователя есть роль KING (Я пробовал и KING, и ROLE_KING)
Мне кажется, что мне не хватает части головоломки.
Мой JWT-фильтр тоже выглядит неплохо. Объект аутентификации (.. который устанавливается в обработчике контекста безопасности, например, так: SecurityContextHolder.getContext (). SetAuthentication (аутентификация);) имеет ровно ОДИН SimpleGrantedAuthority с ролью = KING.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
userDetails.getAuthorities(); //has exactly ONE SimpleGrantedAuthority with value "KING"
SimpleGrantedAuthority s = new SimpleGrantedAuthority("asfd");
s.getAuthority(); //string
UsernamePasswordAuthenticationToken authentication = jwtUtil.getAuthentication(jwt,
SecurityContextHolder.getContext().getAuthentication(), userDetails);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
logger.info("authenticated user " + username + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
Мой UserDetailsService выглядит вот так:
public class UserService implements UserDetailsService {
@Autowired
private UserRepository repository;
@Autowired
private PasswordValidationService passwordValidationService;
@Override
public User loadUserByUsername(String userName) throws UsernameNotFoundException {
UserEntity u = repository.findByUserNameIgnoreCase(userName);
SimpleGrantedAuthority a = new SimpleGrantedAuthority(u.getRole().toString());
ArrayList<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
auths.add(a);
return new User(u.getUserName(), u.getPassword(), auths);
}
}
Все выглядит хорошо для меня, поэтому я сейчас растерялся: (
*********** ОБНОВЛЕНИЕ ***** *******
Если я использую hasAuthority вместо hasRole , тогда это работает:
@PreAuthorize("hasAuthority('KING')")
@RequestMapping("/king3")
@ResponseBody
public String king3() {
return "Hello King";
}
Так как мне получить hasRole для работы? Я пропускаю добавление "ROLE_" где-то в моем коде?