Solr 8.9.0云模式开启Basic Authentication后索引遇IOException
问题描述
- 环境:Solr 8.9.0 云模式,2个分片,通过
security.json开启Basic Authentication(未启用授权模块) security.json配置:
{ "authentication":{ "blockUnknown": true, "class":"solr.BasicAuthPlugin", "credentials":{"solr":"Y6xFaUPypZOWNmgaU+IkZ8eP4TJxXW50= bWtoYjk2Yjhscmx6a2Jveg=="}, "realm":"My Solr users", "forwardCredentials": false } }
- 上传至ZooKeeper的命令:
solr zk cp file:D:\Asite_work\Solr_8.9.0\shard1\bin\security.json zk:/security.json -z "localhost:12181,localhost:12182"
- 现象:Web访问
http://localhost:8983/solr可正常登录并使用,但通过代码索引时抛出错误:
org.apache.solr.client.solrj.impl.CloudSolrClient$RouteException: IOException occurred when talking to server at: http://localhost:8983/solr/documents_shard1_replica_n1
- 索引代码:
final List<String> zkServers = new ArrayList<>(); zkServers.add("localhost:12181"); zkServers.add("localhost:12182"); CredentialsProvider provider = new BasicCredentialsProvider(); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("solr", "Abc@123"); provider.setCredentials(AuthScope.ANY, credentials); HttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build(); CloudSolrClient sc = new CloudSolrClient.Builder(zkServers, Optional.empty()).withParallelUpdates(true).withHttpClient(client).build(); sc.setDefaultCollection("documents"); sc.connect(); UpdateResponse resp = sc.add(docs, SolrConstants.COMMIT_WITHIN);
请问该错误产生的原因是什么?是否需要在ZooKeeper中进行额外配置?
问题分析与解决
错误原因
你的代码中配置的HttpClient默认采用挑战-响应模式的Basic认证:即先发送不带凭据的请求,等待Solr节点返回401未授权响应后,再提交认证凭据。但在Solr云模式下,CloudSolrClient会直接与分片副本通信,加上你开启了blockUnknown: true(拒绝未认证请求),未携带凭据的初始请求会被Solr直接拦截,从而抛出IO异常。
解决方法
修改HttpClient配置,添加预emptive(先发制人)认证拦截器,确保请求一开始就携带Basic Auth凭据。修改后的代码如下:
final List<String> zkServers = new ArrayList<>(); zkServers.add("localhost:12181"); zkServers.add("localhost:12182"); CredentialsProvider provider = new BasicCredentialsProvider(); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("solr", "Abc@123"); provider.setCredentials(AuthScope.ANY, credentials); // 配置预emptive认证缓存 AuthCache authCache = new BasicAuthCache(); BasicScheme basicAuthScheme = new BasicScheme(); // 针对Solr节点地址添加缓存,若有多个节点需逐一添加或使用通配符处理 authCache.put(new HttpHost("localhost", 8983, "http"), basicAuthScheme); // 创建带预emptive认证的HttpClient HttpClient client = HttpClientBuilder.create() .setDefaultCredentialsProvider(provider) .setDefaultAuthCache(authCache) .addInterceptorFirst(new PreemptiveAuthInterceptor()) .build(); // 自定义预emptive认证拦截器 static class PreemptiveAuthInterceptor implements HttpRequestInterceptor { @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE); // 若未设置认证方案,主动添加Basic认证 if (authState.getAuthScheme() == null) { CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(HttpClientContext.CREDS_PROVIDER); HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST); Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort())); if (creds != null) { authState.update(new BasicScheme(), creds); } } } } CloudSolrClient sc = new CloudSolrClient.Builder(zkServers, Optional.empty()) .withParallelUpdates(true) .withHttpClient(client) .build(); sc.setDefaultCollection("documents"); sc.connect(); UpdateResponse resp = sc.add(docs, SolrConstants.COMMIT_WITHIN);
额外说明
不需要在ZooKeeper中进行额外配置,当前的security.json认证配置已经生效,问题完全出在客户端请求的认证方式上。
内容的提问来源于stack exchange,提问作者vishal patel
相关产品推荐
相关产品推荐

