如何将安全上下文身份验证注入自定义 ConstraintValidator?

How to inject security context authentication into custom ConstraintValidator?

提问人:DLL 提问时间:9/15/2023 最后编辑:DLL 更新时间:11/1/2023 访问量:105

问:

在使用 Spring Boot 3 和 WebFlux 的 Kotlin 中,我有一个自定义验证器,它需要两个输入:一个是要验证的值,但我还需要身份验证主体来验证用户是否有权合法访问该值。

我在使用时得到一个.NullPointerExceptiongetContext()

@Component
class OrgInstanceValidator(val validator: OrgPermissionValidator) : ConstraintValidator<ValidOrgInstance, InstanceId> {

    override fun isValid(value: InstanceId, context: ConstraintValidatorContext): Boolean = runBlocking {
        val accessToken = ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication).map {
            it.principal as Jwt
        }.awaitSingle()
        validator.validateInstanceAccess(accessToken.subject, value).awaitSingle()
    }.isGranted
}

我也接受在控制器上使用 AOP,在此示例中值为 a。PathParam

有人有解决这个问题的经验吗?

使用似乎不起作用,降低 Bean 优先级也不起作用。ReactiveSecurityContextHolder

spring-boot spring-security nullpointerexception spring-webflux

评论

0赞 Toerktumlare 9/15/2023
您是否尝试过访问常规存储主体,这意味着它在响应式应用程序中不起作用,另一个使用响应式上下文来存储ReactiveSecurityContextHolderSecurityContextHolderThreadLocalPrincipal
0赞 DLL 9/15/2023
@Toerktumlare我尝试了 ReactiveSecurityContextHolder,但我仍然得到 NPE。我将更新描述。

答:

1赞 DLL 11/1/2023 #1

我们放弃了尝试在反应式/Webflux 配置上使用 ConstraintValidator。

我们最终使用了与大多数 RestController 注解(如 AuthenticationPrincipal、PathVariable 等)相同的机制,方法是创建我们自己的注解并为处理注解类的 HandlerMethodArgumentResolver 类型创建 bean:

import org.springframework.web.reactive.HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE
import java.security.Principal
...

class MethodAnnotationValidator<T : Annotation>(
    adapterRegistry: ReactiveAdapterRegistry,
    private val annotation: Class<T>,
    private val validator: (Principal, String) -> Mono<Any>,
) : HandlerMethodArgumentResolver {

    // compose this resolver to fetch the Auth context
    private val principal = PrincipalMethodArgumentResolver(adapterRegistry)

    // path var resolver isn't composable, next best thing
    private val pathParam = { paramName: String, exchange: ServerWebExchange -> exchange
        .getAttributeOrDefault(URI_TEMPLATE_VARIABLES_ATTRIBUTE, emptyMap<Any, Any>())
        .get(paramName)!!}
    
   // resolve Principal and PathParam and call validator() 
   // inside resolveArgument() override
...