如何将 GlobalMethodSecurityConfiguration 迁移到 Reactive Spring?
Posted
技术标签:
【中文标题】如何将 GlobalMethodSecurityConfiguration 迁移到 Reactive Spring?【英文标题】:How to migrate the GlobalMethodSecurityConfiguration to Reactive Spring? 【发布时间】:2021-01-28 15:51:42 【问题描述】:我在我的项目中覆盖了SecurityExpressionRoot
,公开了一种验证当前用户是否有权访问给定资源的方法。
然后我覆盖了 GlobalMethodSecurityComfiguration.createExpressionHandler(),然后 createSecurityExpressionRoot() 返回了被覆盖的 SecurityExpressionRoot 的实例。 这在 servlet 场景中有效,不幸的是,这似乎不适用于响应式场景。
如何将下面的方法安全设置转换为响应式场景?
在我的测试中出现以下错误:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:379) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.ui.LogoutPageGeneratingWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.ui.LoginPageGeneratingWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.csrf.CsrfWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers$MutatorFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP GET "/things/1/jobs/1/log" [ExceptionHandlingWebHandler]
@配置 @EnableGlobalMethodSecurity(prePostEnabled = true) @RequiredArgsConstructor 公共类 MethodSecurityConfig 扩展 GlobalMethodSecurityConfiguration 私有最终 ThingsRepository thingsRepository;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler()
return new DefaultMethodSecurityExpressionHandler()
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation)
DeployPermissionSecurityExpressionRoot root = new ThingsPermissionSecurityExpressionRoot(thingsRepository, authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(getTrustResolver());
root.setRoleHierarchy(getRoleHierarchy());
return root;
;
我的安全配置:
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http)
// @formatter:off
http
.csrf().disable()
.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt(jwt ->
jwt.jwtDecoder(jwtDecoder());
jwt.jwtAuthenticationConverter(customJwtAuthConverter());
)
.and()
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance());
// @formatter:on
return http.build();
SecurityExpressionRoot 实现:
class ThingsPermissionSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations
private final ThingsRepository ThingsRepository;
private Object filterObject;
private Object returnObject;
private Object target;
ThingsPermissionSecurityExpressionRoot(ThingsRepository thingsRepository, Authentication authentication)
super(authentication);
this.thingsRepository = thingsRepository;
public boolean hasThingsWritePrivilege(Long thingsId)
控制器:
public class ThingsController
private final JobPublisherProvider jobPublisherProvider;
@GetMapping("thingsId/jobs/jobId/log")
@PreAuthorize("hasThingsWritePrivilege(#thingsId)")
public Flux<DataBuffer> retrieveJobLog(@PathVariable String thingsId, @PathVariable int jobId)
控制器测试方法
@Test
@WithMockUser(roles = "ROLE_JAR_W")
public void logValueProperlyRetrieved()
【问题讨论】:
您是否尝试过在配置类中使用“@EnableReactiveMethodSecurity”代替“@EnableGlobalMethodSecurity”? 是的,问题出在依赖关系上。 【参考方案1】:无法在反应式 Spring 应用程序(当前 Spring Security 版本 5.5.2)中定义 GlobalSecurityConfiguration。
为了具有相同的功能,您需要将 ReactiveMethodSecurityConfiguration 中定义的 methodSecurityExpressionHandler 替换为您自己的方法安全表达式处理程序。
为此,您可以扩展 DefaultMethodSecurityExpressionHandler 并将其定义为 @Primary bean。 对于您的情况,它将如下所示。
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation)
DeployPermissionSecurityExpressionRoot root = new ThingsPermissionSecurityExpressionRoot(thingsRepository, authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(getTrustResolver());
root.setRoleHierarchy(getRoleHierarchy());
root.setDefaultRolePrefix(getDefaultRolePrefix());
return root;
@Bean
@Primary
public MethodSecurityExpressionHandler methodSecurityExpressionHandler()
return new CustomMethodSecurityExpressionHandler();
参考资料:
https://github.com/spring-projects/spring-security/issues/5046#issuecomment-427097710【讨论】:
以上是关于如何将 GlobalMethodSecurityConfiguration 迁移到 Reactive Spring?的主要内容,如果未能解决你的问题,请参考以下文章