春季启动会返回403,尽管我具有管理员权限

时间:2020-06-17 10:19:58

标签: java spring redirect spring-security jwt

我将/admin端点添加到antMatchers("/admin").hasAuthority("ADMIN")时遇到了这个问题 它只是不会向/admin发出 GET 请求并返回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";

}

奇怪的是,使用邮递员,我被重定向了!

我在这里想念什么?


编辑:第一个电话是在/authenticate/web,我告诉spring我已通过身份验证

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

编辑2:

使情况更清晰:

访问网络 的流程:

  1. 发布/authenticate/web
  2. 使用.js重定向到/authority(GET)
  3. 不会重定向到/admin(GET)-> 403

访问邮递员 ,流程:

  1. 发布/authenticate/web
  2. 获取JWT并将其包含在标题中,然后对/authority进行
  3. 我正在看到 admin 模板。 -> 200

真的很奇怪,我每次在{strong> web 流中将jwtresponse.addHeader添加在一起。


更新

  • 这些是邮递员的响应标头:

enter image description here

加上JWT

  • 来自网络的响应标头

enter image description here

尽管现在我注意到我从网上获得了 302 ,而不是 200

您将看到admin页面是 403

enter image description here


更新2:

我已经设法分解了几件事, 首先

  • 通过对我的安全性使用httpSecurity.addFilterBefore 配置意味着spring将寻找JWT并在指定过滤器类的位置之前添加过滤器

  • 权限已正确分配给用户,因此没有问题 在那里

  • 我将hasAuthority()更改为hasRole()

如果您获得当前用户,则可以如下所示自动访问其权限

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

因为authenticationjwt filter覆盖, 意味着我只会在请求标头时设法获取用户 包含有效的jwt

这就是为什么它在postman上有效但在web上无效的原因。


另一个问题是在控制器中,我试图在jwt标头上添加response ,仅在控制器完成其工作时才会添加到标头中,我无法进入用户委托人的下一行是因为请求标头中没有jwt

此屏幕快照表示一个网络呼叫和来自邮递员的呼叫,两个人都访问/authority端点。

enter image description here

  • 在邮递员中,您将ADMIN视为授权机构
  • 但是从网络上我有一个ROLE_ANONYMOUS

所以我有两种选择来解决这个问题:

  1. 将其添加到请求标头中。
  2. 使用REST保护JWT端点,并为Web部件使用默认的弹簧安全性(hasRole()等)。

4 个答案:

答案 0 :(得分:2)

经过反复试验,我设法分解了几件事,并使用JWT和默认的 authentication 弹簧使安全性正常工作。

要使其正常工作,我必须完全更改我的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 :(得分:1)

  1. 在浏览器中启用开发人员工具,并从Web捕获GET /authority的请求和响应标头。即您的网络流程的第二步

  2. 捕获来自邮递员的GET /authority的请求和响应标头。即。邮递员流程的第二步

  3. 比较它们(或在此处发布)

基于发布的屏幕截图进行更新

从网络访问:

  1. POST /authenticate/web-这就是成功
  2. 使用.js重定向到GET /authority-这也是成功的,因为它确实完成了将其重定向到/admin 的工作。
    (来自权威机构的响应将浏览器重定向到/admin,因此这里没有问题)
  3. /admin的自动重定向请求以403失败(因为按预期,没有人向请求添加jwt标头,因为它不是cookie或不是重定向URL或某些会话的查询参数的一部分会话保持)(这是正确的预期行为)。看到这个问题。 How to forward headers on HTTP redirect

邮递员的来访:

  1. POST /authenticate/web这就是成功
  2. 获取JWT并将其包含在标题中,然后对/ authority进行GET
  3. 邮递员将其重定向到/admin,就像在网络流程中一样 (邮递员保留了原始的jwt标头,并将其也转发到重定向的url,但它不应该这样做)

摘要

总而言之,postman和web在重定向上的行为不同。即在重定向上,postman发送原始标头(在您的情况下为jwt),而浏览器不保留原始http标头。

因此,您必须重新设计应用程序。 例如,

  1. 您可以在JavaScript中捕获来自/authority端点的响应,并使用http标头将其手动转发到/admin
  2. 或者可以是cookie。因为它即使在重定向时也会自动发送

答案 2 :(得分:0)

如Spring Security所建议的那样,在不安全的映射之前进行安全保护的映射,在这种情况下,

.antMatchers(“ / admin”)。hasAuthority(“ ADMIN”)

必须先出现在不安全的映射(“ /”,“ / authority”及其他)之前,在“授权”部分中,这可能是导致问题的原因。

答案 3 :(得分:-1)

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

它可以代替"/admin"使用"/admin/**"