Spring boot возвращает 403, хотя у меня есть полномочия администратора - PullRequest
4 голосов
/ 17 июня 2020

У меня возникла эта проблема, когда я добавил /admin конечную точку к antMatchers("/admin").hasAuthority("ADMIN"), она просто не будет делать запрос GET на /admin и возвращать 200 вместо этого возвращает 403

Примечание: Я использую JWT в качестве дополнительного уровня аутентификации.

Это моя конфигурация безопасности

httpSecurity.csrf().disable()
 .authorizeRequests().antMatchers("/", "/health", "/authority", "/dashboard", "/users/login", "/logoutUser", "/manageEvents", "/manageAeds", "/manageReports",
  "/charts", "/error", "/profile", "/authenticate/**", "/login", "/403", "/userProfile", "/deleteAed", "/users/add").permitAll()
 .antMatchers("/admin").hasAuthority("ADMIN")
 .antMatchers("/css/**", "/img/**", "/js/**", "/images/**", "/error_css/**", "/scss/**", "/vendor/**").permitAll()
 .anyRequest().authenticated().and().
exceptionHandling().accessDeniedPage("/403").and()
 .sessionManagement()
 .sessionCreationPolicy(SessionCreationPolicy.STATELESS);


httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

Автор переместив его в permit.All(), он будет работать, но здесь это не так.

Здесь я обрабатываю перенаправление внутри @Controller

@GetMapping("/authority")
public String getAuth(HttpServletResponse response) {

 if (jwt == null) {
  return "redirect:/login";
 }
 if (jwtTokenUtil.isTokenExpired(jwt)) {
  return "redirect:/login?token=expired";
 }
 response.addHeader("Auth", "Bearer " + jwt);

 System.out.println(loggedinUser.getRoles());

 if (loggedinUser != null) {
  if (loggedinUser.getRoles().equalsIgnoreCase("TILEFONITIS")) {
   return "redirect:/dashboard"; //will redirect
  } else if (loggedinUser.getRoles().equalsIgnoreCase("ADMIN")) {
   System.out.println("Admin");
   return "redirect:/admin"; //won't redirect
  } else if (loggedinUser.getRoles().equalsIgnoreCase("GUEST")) {
   return "redirect:/403"; // will redirect
  } else {
   return "redirect:/dashboard"; // will redirect
  }
 } else {
  return "redirect:/login";
 }

}

, и это мой /admin внутри @Controller, который никогда не вызывается.

@GetMapping("/admin")
public String getAdmin(HttpServletResponse response) {
 if (jwt == null) {
  return "redirect:/login";
 }
 if (jwtTokenUtil.isTokenExpired(jwt)) {
  return "redirect:/login?token=expired";
 }
 response.addHeader("Auth", "Bearer " + jwt);

 System.out.println("jwt" + jwt);

 return "admin";

}

Странно, что с Postman меня перенаправляют!

Что мне здесь не хватает?

Редактировать: Первый звонок на /authenticate/web, где я говорю Spring, что я аутентифицирован

authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(auth.getUsername(), auth.getPassword()));

Редактировать 2:

Для большей ясности:

Посещение из Интернета , поток:

  1. POST /authenticate/web
  2. перенаправление с .js на /authority (GET)
  3. Не перенаправляет на /admin (GET) -> 403

Посещение с Почтальон , поток:

  1. POST /authenticate/web
  2. Получите JWT и включите его в заголовки и сделайте GET на /authority
  3. Я вижу шаблон admin . -> 200

Это действительно странно, я добавляю jwt каждый раз с response.addHeader в поток web .

Обновление:

  • Это заголовки ответа от почтальона:

enter image description here

plus the JWT .

  • Response headers from the web

enter image description here

Although now i noticed i get 302 from the web instead of a 200

and as you can see admin page is 403

enter image description here


Update 2:

I've managed to break down a few things, first of all

  • by having a httpSecurity.addFilterBefore on my security configuration means spring will look for the JWT and add a filter before the position of the specified filter class

  • authorities are correctly assigned to users, so there is no issue there

  • i changed hasAuthority() to hasRole()

If you get the current user you can automatically access it's authority as shown below

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("Principal: "+auth.getPrincipal());
System.out.println("Authorities: "+auth.getAuthorities());             

Поскольку authentication переопределяется jwt filter, это означает, что мне удастся получить пользователя только в том случае, если заголовок запроса содержит действительный jwt

поэтому он работал с postman, а не с web.

Другая проблема заключается в том, что в моем контроллере я пытаюсь добавить jwt в заголовок response , который будет добавлен к нему только тогда, когда контроллер завершит свою работу, я не могу попасть в следующую строку пользовательский принципал, потому что в его заголовке запроса нет jwt.

Этот снимок экрана представляет веб-вызов и вызов от почтальона, где оба обращаются к /authority конечной точке.

введите описание изображения здесь

  • От почтальона вы видите ADMIN как авторитет
  • Но из Интернета у меня есть ROLE_ANONYMOUS

Итак, у меня есть два варианта решения этой проблемы:

  1. Добавьте его в заголовок запроса.
  2. Защитите REST конечные точки с помощью JWT и используйте пружинную безопасность по умолчанию (hasRole() et c ) для веб-части.

Ответы [ 4 ]

2 голосов
/ 22 июня 2020

После многих проб и ошибок мне удалось сломать несколько вещей и заставить безопасность работать с JWT, а также с пружиной по умолчанию аутентификацией .

In чтобы он работал, мне пришлось полностью изменить свой SecurityConfig и изменить его на MultipleSecurity.

Вот как работает множественная безопасность:

с аннотацией @Order(1) конфигурация безопасности помечено как первое, что нужно искать.

@Order(1)
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

 @Bean
 public AuthenticationManager authenticationManagerBean() throws Exception {
  return super.authenticationManagerBean();
 }

 @Override
 protected void configure(HttpSecurity httpSecurity) throws Exception {
  httpSecurity.csrf().disable()
   .antMatcher("/api/**").authorizeRequests()
   .antMatchers("/api/authenticate/android").permitAll()
   .anyRequest().authenticated().and()
   .exceptionHandling().accessDeniedPage("/403").and().sessionManagement()
   .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
   .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
 }

}
  • @Order(1) JWT
  • @Order(2) пружинная защита по умолчанию
@Order(2)
@Configuration
public class SecurityConfiguration2 extends WebSecurityConfigurerAdapter {

 @Override
 protected void configure(HttpSecurity httpSecurity) throws Exception {
  httpSecurity.authorizeRequests()
   .antMatchers("/web/**").hasAuthority("ADMIN")
   .and().formLogin().defaultSuccessUrl("/redirect")
   .and().logout()
   .permitAll();
 }

}

В основном мы говорим с этими конфигурациями:

  1. Ищите JWT в /api/**
  2. Ищите авторитетные источники в /web/**

Таким образом, для всех конечных точек /web, таких как /web/admin, теперь потребуется authorities, а не JWT!

, а для всей конфиденциальной информации из /api/** потребуется JWT

1 голос
/ 18 июня 2020
  1. Включите инструменты разработчика в своем браузере и захватите заголовки запроса и ответа GET /authority из Интернета. ie 2-й шаг вашего веб-потока

  2. Захватите заголовки запроса и ответа GET /authority от почтальона. ie. 2-й шаг вашего почтальона

  3. Сравните их (или разместите здесь)

Обновление на основе размещенных снимков экрана

Посещение из Интернета:

  1. POST /authenticate/web - это успех
  2. перенаправление с. js на GET /authority - это тоже успех потому что он выполнил свою работу по перенаправлению его на /admin.
    (ответ от органа перенаправляет браузер на /admin, поэтому здесь нет проблем)
  3. запрос с автоматическим перенаправлением на /admin не выполняется с 403 (поскольку, как и ожидалось, никто не добавляет заголовок jwt в запрос, поскольку он не является поваром ie или не является частью параметра запроса перенаправленного URL-адреса или поддерживается какой-либо сеанс сеанса) (это правильное ожидаемое поведение) . См. Этот вопрос. Как пересылать заголовки при перенаправлении HTTP

Посещение от почтальона:

  1. POST /authenticate/web это успех
  2. Получить JWT и включите его в заголовки и сделайте GET в / Authority
  3. Почтальон перенаправляет его на /admin, как в веб-потоке (Почтальон сохраняет исходные заголовки jwt и пересылает его на перенаправленный URL-адрес, но этого делать не должно. it)

Сводка

Таким образом, почтальон и Интернет ведут себя по-разному при перенаправлении. т.е. при перенаправлении почтальон отправляет исходные заголовки (в вашем случае , это jwt), и браузер не сохраняет исходные заголовки http.

Итак, вам нужно изменить дизайн вашего приложения. Например,

  1. вы можете захватить ответ от /authority конечной точки в javascript и вручную переслать его на /admin с заголовком http
  2. или в виде файлов cookie. Потому что он будет отправлен автоматически даже при перенаправлении
0 голосов
/ 17 июня 2020

Поскольку Spring Security предлагает сопоставления, которые защищены до появления незащищенных сопоставлений, в этом случае

.antMatchers ("/ admin"). HasAuthority ("ADMIN")

имеет предшествовать незащищенным сопоставлениям («/», «/ Authority» и др.). В части авторизации это могло быть причиной проблемы.

0 голосов
/ 17 июня 2020
.antMatchers("/admin").hasAuthority("ADMIN")

Вместо "/admin" используйте "/admin/**" работает

...