I have sping-boot application with rest services written using Spring web flux.
For now I access minio using login/password authorizaton and it works fine.
For now I want to exchange application JWT token with STS minio token and I implemented method to test:
@PostMapping
public boolean test(JwtAuthenticationToken token) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient =
MinioClient.builder()
.region(...)
.endpoint(...)
.credentialsProvider(new WebIdentityProvider(
() -> new Jwt(token.getToken().getTokenValue(), 1000),
String.valueOf(...),
null,
null,
null,
null,
null))
.build();
return minioClient.bucketExists("mybucket").build());
}
This code successfully works and returns true
because mybucket
actually exists.
But it is only test and I need to move minioClient
to the configuration. The issue here that I have to have credentials provider there.
So I've created folowing configuration:
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.region(...)
.endpoint(...)
.credentialsProvider(new WebIdentityProvider(
() -> {
String block = null;
try {
block = ReactiveSecurityContextHolder
.getContext()
.map(context -> {
return context
.getAuthentication()
.getPrincipal();
}
)
.cast(Jwt.class)
.map(Jwt::token)
.block();
} catch (Exception e) {
// it fails here <=======
System.out.println(e);
}
Jwt jwt = new Jwt(String.valueOf(block),
1000);
return jwt; },
String.valueOf(...),
null,
null,
null,
null,
null))
.build();
}
But unfortunately method block()
fails with exception:
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-6
Any ideas how to fix it?
CodePudding user response:
As Numichi stated in the comment you have to stay in the reactor context.
One option is to create a bean of type Mono<MinioClient>
.
@Bean
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public Mono<MinioClient> reactiveMinio() {
return ReactiveSecurityContextHolder.getContext()
.map(securityContext ->
(Jwt)securityContext.getAuthentication().getPrincipal())
.map(jwt -> MinioClient.builder()
.region("someRegion")
.endpoint("someEndpoint")
.credentialsProvider(webIdentityProvider(jwt.token()))
.build());
}
private WebIdentityProvider webIdentityProvider(String token) {
return new WebIdentityProvider(() -> new Jwt(token, 1000),
"stsEndpoint",
null,
null,
null,
null,
null);
}
I think bean scope should be prototype since MinioClient
is bound to security context.
Here is the sample usage of reactive MinioClient
:
@RestController
public class MinioTest {
private Mono<MinioClient> minioClient;
public MinioTest(Mono<MinioClient> minioClient) {
this.minioClient = minioClient;
}
@GetMapping("/minio")
public Mono<Object> client() {
return minioClient
.map(minio -> {
try {
return minio.bucketExists(BucketExistsArgs
.builder()
.bucket("my-bucketname")
.build());
} catch (Exception e) {
return new Exception(e);
}
});
}
}