尽管我有管理员权限,但 Spring Boot 返回 403

Posted

技术标签:

【中文标题】尽管我有管理员权限,但 Spring Boot 返回 403【英文标题】:Spring boot returns 403 although i have admin authority 【发布时间】:2020-10-07 03:16:43 【问题描述】:

我在将/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";
 


这是我在@Controller 中的/admin,它永远不会被调用。

@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:

为了让事情更清楚:

网络访问,流程:

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

来自邮递员的访问,流程:

    发布/authenticate/web 获取JWT 并将其包含在标头中,并对/authority 进行GET 我看到的是 admin 模板。 -> 200

这真的很奇怪,我每次都在 web 流上添加jwtresponse.addHeader


更新:

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

加上JWT

来自网络的响应标头

虽然现在我注意到我从网上得到 302 而不是 200

你可以看到admin页面是403


更新 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 中无效。


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

此屏幕截图代表一个网络呼叫和来自邮递员的呼叫,两者都访问/authority 端点。

您从邮递员那里看到ADMIN 是权威 但是从网上我有一个ROLE_ANONYMOUS

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

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

【问题讨论】:

显示您的令牌。通常,使用 OAuth,您将使用 scope_ADMIN 作为您的授权名称。 您的意思是我用于生成和验证用户的自定义端点? 不,我的字面意思是您用来进行身份验证和授权的 JWT。具体来说,Spring Security 将寻找 OAuth 范围,并将这些范围映射到名为 scope_<scopename> 的权限中。 这是众多Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuaWNrc3Rlcmc5IiwiZXhwIjoxNTkyNDI2NTE4LCJpYXQiOjE1OTIzOTA1MTh9.cgT3ZjZxEFr__e_Woyouw9M86uoCFKWFTDahxTXXOP4之一 我应该包括JwtUtilJwtRequestFilter 吗? 【参考方案1】:

经过大量试验和错误,我设法解决了一些问题,并使用JWT 以及默认的弹簧身份验证使安全工作。

为了让它工作,我必须完全改变我的 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)智威汤逊 @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();
 


我们基本上说这些配置是:

    /api/** 寻找JWT/web/**寻找权威

因此,所有/web 端点(如/web/admin)现在都需要authorities 而不是JWT

来自/api/** 的所有敏感信息都需要JWT

【讨论】:

【参考方案2】:

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

    从邮递员那里捕获GET /authority的请求和响应标头。 IE。邮递员流程的第二步

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

根据发布的屏幕截图进行更新

通过网络访问:

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

邮递员来访:

    POST /authenticate/web 成功了 获取 JWT 并将其包含在标头中并对 /authority 进行 GET Postman 将其重定向到/admin,就像在网络流中一样 (Postman 保留原始的 jwt 标头并将其转发到重定向的 url,但它不应该这样做)

总结

总之,邮递员和网络在重定向上的行为不同。即在重定向时,邮递员发送原始标头(在您的情况下,它是 jwt)并且浏览器不保留原始 http 标头。

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

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

【讨论】:

在响应标题中都包含我的有效JWT,我会用他们更新我的问题 @phill-alexakis,如您所见,问题不在 Web 流程中重定向。实际上它还使用302 状态码重定向到/admin 端点。所以现在的问题是为什么重定向的请求/admin端点在邮递员中成功而在网络中失败。 @phill-alexakis 了解这一点,您能否再次从邮递员和网络中捕获所有请求标头。即,您有从 web 捕获 admin 的响应标头的屏幕截图。但是您没有捕获请求标头。我很确定/admin 的请求标头中有一些不同(注意问题不是authority 端点,因为在两个流程中,它都重定向到/admin 是的,我添加了它,但我没有看到 JWT 表示未调用控制器来分配它 通过使用邮递员,控制器以某种方式被调用,然后分配JWT。从网络上,控制器没有被调用,因为在响应头中没有设置JWT【参考方案3】:

由于 Spring Security 建议将安全的映射放在不安全的映射之前,在这种情况下,

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

必须出现在不安全的映射(“/”,“/authority”等)之前,在授权部分,这可能会导致问题。

【讨论】:

情况并非如此......使用 Postman 通过按照流程我通过身份验证并返回管理员,尽管它没有通过网络【参考方案4】:
.antMatchers("/admin").hasAuthority("ADMIN")

而不是"/admin" 使用"/admin/**" 它可以工作

【讨论】:

不是,/admin 是 403,为什么/admin/something 应该返回 200? System.out.println(loggedinUser.getRoles()); 打印出来..?? .antMatchers("/admin").access("hasRole('ROLE_ADMIN')") .. 因为ROLE_ 是您可以使用的默认前缀,或者ADMIN 可以授予权限@ 987654321@帮你 我知道它可以像这样获得授权,尽管它只使用 ADMIN 而不是来自网络,仅通过从 postman 调用它 httpSecurity.csrf().disable() .authorizeRequests().antMatchers("/authority").permitAll() .antMatchers("/admin/**").hasRole("ADMIN" ) .anyRequest().authenticated().and() .exceptionHandling().and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

以上是关于尽管我有管理员权限,但 Spring Boot 返回 403的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot + Spring Cloud 实现权限管理系统 后端篇(二十一):服务网关(Zuul)

Spring boot + Spring Security实现权限管理

对其他 Spring Boot 应用程序的集中会话管理

spring boot shiro -权限管理

spring-boot整合shiro实现权限管理

Spring Boot Shiro 权限管理