Я использую gRPC в среде SpringBoot (и SpringSecurity) и использую ServerInterceptor
для аутентификации пользователей при их подключении. Я сохраняю результат аутентификации в Spring SecurityContextHolder
. (Подписался)
private final AuthenticationManager manager;
@Autowired
public AuthenticatorInterceptor(final AuthenticationManager manager) {
this.manager = manager;
}
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall,
Metadata metadata, ServerCallHandler<ReqT, RespT> serverCallHandler) {
logger.info("Authorizing user information for GRPC access");
final String authHeader = nullToEmpty(metadata.get(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER)));
if (!authHeader.startsWith("Basic ")) {
logger.error("No Authentication Info found");
throw Status.PERMISSION_DENIED.withDescription("No Authentication Info found").asRuntimeException();
}
try {
final String[] tokens = decodeBasicAuth(authHeader);
final String userName = tokens[0];
if (authenticationIsRequired(userName)) {
final Authentication pending = new UsernamePasswordAuthenticationToken(userName, tokens[1]);
final Authentication result = manager.authenticate(pending);
logger.info("Authentication success for this user");
SecurityContextHolder.getContext().setAuthentication(result);
}
} catch (AuthenticationException e) {
SecurityContextHolder.clearContext();
logger.error("Authentication failed - No GRPC Access", e);
throw Status.UNAUTHENTICATED.withDescription(e.getMessage()).withCause(e).asRuntimeException();
}
return serverCallHandler.startCall(serverCall, metadata);
}
Но проблема в том, что аутентификация просачивается и распределяется между клиентами. Например, если клиент А подключается и аутентифицируется, а затем, когда подключается другой клиент Б, клиент Б уже аутентифицирован с учетными данными клиента А.
Я предполагаю, что это связано с тем, что по умолчанию SecurityContext
из SecurityContextHolder
использует ThreadLocal
для хранения информации аутентификации, а gRPC, вероятно, повторно использует потоки для разных соединений.
Где хранить информацию для аутентификации? Я видел класс Context
(контекст gRPC), но не мог понять (и не мог найти никакого ресурса), как правильно реализовать требуемую мне функциональность.
PS: для настройки сервера gRPC и его перехватчики.