Spring 启动授权 returns 403 对于使用@RolesAllowed、@Secured 或@PreAuthorize 的任何授权请求
Spring boot authorization returns 403 for any authorization request using @RolesAllowed, @Secured or @PreAuthorize
我一直在研究这篇文章(以及其他一些类似的文章):https://medium.com/omarelgabrys-blog/microservices-with-spring-boot-authentication-with-jwt-part-3-fafc9d7187e8
客户端是一个 Angular 8 应用程序,它从一个独立的微服务中获取一个 Jwt。尝试将过滤器添加到不同的微服务以通过 jwt 角色要求特定授权。
持续收到 403 错误。
安全配置:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true,
securedEnabled = true,
jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurityConfig() {}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and().csrf().disable()
// make sure we use stateless session; session won't be used to store user's state.
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// Add a filter to validate the tokens with every request
.addFilterAfter(new JwtAuthorizationFilter2(), UsernamePasswordAuthenticationFilter.class)
// authorization requests config
.authorizeRequests()
// Any other request must be authenticated
.anyRequest().authenticated();
}
}
过滤器:
public class JwtAuthorizationFilter2 extends OncePerRequestFilter {
private final String HEADER = "Authorization";
private final String PREFIX = "Bearer ";
private final String SECRET = "foo";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String token = request.getHeader(SecurityConstants.HEADER_STRING);
if (token != null) {
// parse the token.
DecodedJWT decoded = JWT.require(Algorithm.HMAC512(SecurityConstants.SECRET.getBytes()))
.build()
.verify(token.replace(SecurityConstants.TOKEN_PREFIX, ""));
String user = decoded.getSubject();
List<SimpleGrantedAuthority> sgas = Arrays.stream(
decoded.getClaim("roles").asArray(String.class))
.map( s -> new SimpleGrantedAuthority(s))
.collect( Collectors.toList());
if (sgas != null) {
sgas.add(new SimpleGrantedAuthority("FOO_Admin"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
user,
null,
sgas);
SecurityContextHolder.getContext().setAuthentication(auth);
}
else {
SecurityContextHolder.clearContext();
}
chain.doFilter(request, response);
}
}
}
此代码在没有定义任何授权要求的情况下工作正常,但如果在 WebSecurityConfig 中定义了授权,或者通过装饰控制器方法,范围内的所有请求都会收到 http 403。
示例:
.authorizeRequests().antMatchers("/**").hasRole("FOO_Admin")
// or any of these
@PreAuthorize("hasRole('FOO_Admin')")
@RolesAllowed({"FOO_Admin"})
@Secured({"FOO_Admin"})
Device get(@PathVariable String id) {
// some code
}
当代码在 SecurityContextHolder.getContext().setAuthentication(auth)
停止时,
auth.authenticated = true
和
auth.authorities
包括 "FOO_Admin"
的 SimpleGrantedAuthority
所以我想知道是否:
FilterChain 需要一个身份验证过滤器(或者身份验证是否发生在 JwtAuthorizationFilter2 中?)?
角色名称没有拼写、格式或大小写差异。
我傻眼了。任何帮助将不胜感激。
@PreAuthorize("hasRole('FOO_Admin'))
期望用户具有 ROLE_FOO_Admin
权限,该权限将以 ROLE_
为前缀。但是用户只有 FOO_Admin
权限,访问方法失败
您有多种选择:
(1) 通过声明一个 GrantedAuthorityDefaults
bean 来更改前缀:
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("FOO");
}
并使用@PreAuthorize(hasRole('Admin'))
保护方法。
(2) 或者更简单的是使用 @PreAuthorize("hasAuthority('FOO_Admin')")
,直接检查用户是否有权限 FOO_Admin
,不加任何前缀。
P.S JwtAuthorizationFilter2
只是验证一个用户是否合法,并获取相关的用户信息,为后面授权用户做准备。这是一种身份验证,我会将其重命名为 JwtAuthenticationFilter2
以更准确地描述它的实际作用。
我一直在研究这篇文章(以及其他一些类似的文章):https://medium.com/omarelgabrys-blog/microservices-with-spring-boot-authentication-with-jwt-part-3-fafc9d7187e8
客户端是一个 Angular 8 应用程序,它从一个独立的微服务中获取一个 Jwt。尝试将过滤器添加到不同的微服务以通过 jwt 角色要求特定授权。
持续收到 403 错误。
安全配置:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true,
securedEnabled = true,
jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurityConfig() {}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and().csrf().disable()
// make sure we use stateless session; session won't be used to store user's state.
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// Add a filter to validate the tokens with every request
.addFilterAfter(new JwtAuthorizationFilter2(), UsernamePasswordAuthenticationFilter.class)
// authorization requests config
.authorizeRequests()
// Any other request must be authenticated
.anyRequest().authenticated();
}
}
过滤器:
public class JwtAuthorizationFilter2 extends OncePerRequestFilter {
private final String HEADER = "Authorization";
private final String PREFIX = "Bearer ";
private final String SECRET = "foo";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String token = request.getHeader(SecurityConstants.HEADER_STRING);
if (token != null) {
// parse the token.
DecodedJWT decoded = JWT.require(Algorithm.HMAC512(SecurityConstants.SECRET.getBytes()))
.build()
.verify(token.replace(SecurityConstants.TOKEN_PREFIX, ""));
String user = decoded.getSubject();
List<SimpleGrantedAuthority> sgas = Arrays.stream(
decoded.getClaim("roles").asArray(String.class))
.map( s -> new SimpleGrantedAuthority(s))
.collect( Collectors.toList());
if (sgas != null) {
sgas.add(new SimpleGrantedAuthority("FOO_Admin"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
user,
null,
sgas);
SecurityContextHolder.getContext().setAuthentication(auth);
}
else {
SecurityContextHolder.clearContext();
}
chain.doFilter(request, response);
}
}
}
此代码在没有定义任何授权要求的情况下工作正常,但如果在 WebSecurityConfig 中定义了授权,或者通过装饰控制器方法,范围内的所有请求都会收到 http 403。
示例:
.authorizeRequests().antMatchers("/**").hasRole("FOO_Admin")
// or any of these
@PreAuthorize("hasRole('FOO_Admin')")
@RolesAllowed({"FOO_Admin"})
@Secured({"FOO_Admin"})
Device get(@PathVariable String id) {
// some code
}
当代码在 SecurityContextHolder.getContext().setAuthentication(auth)
停止时,
auth.authenticated = true
和
auth.authorities
包括 "FOO_Admin"
所以我想知道是否: FilterChain 需要一个身份验证过滤器(或者身份验证是否发生在 JwtAuthorizationFilter2 中?)? 角色名称没有拼写、格式或大小写差异。
我傻眼了。任何帮助将不胜感激。
@PreAuthorize("hasRole('FOO_Admin'))
期望用户具有 ROLE_FOO_Admin
权限,该权限将以 ROLE_
为前缀。但是用户只有 FOO_Admin
权限,访问方法失败
您有多种选择:
(1) 通过声明一个 GrantedAuthorityDefaults
bean 来更改前缀:
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("FOO");
}
并使用@PreAuthorize(hasRole('Admin'))
保护方法。
(2) 或者更简单的是使用 @PreAuthorize("hasAuthority('FOO_Admin')")
,直接检查用户是否有权限 FOO_Admin
,不加任何前缀。
P.S JwtAuthorizationFilter2
只是验证一个用户是否合法,并获取相关的用户信息,为后面授权用户做准备。这是一种身份验证,我会将其重命名为 JwtAuthenticationFilter2
以更准确地描述它的实际作用。