尽管我有管理员权限,但 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 流上添加jwt
和response.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());
因为
authentication
被jwt 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
之一
我应该包括JwtUtil
和JwtRequestFilter
吗?
【参考方案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)