不得不说,Solr这个组件的坑很多,能使用Elasticsearch就尽量别使用Solr。
如果在使用Solr的过程中出现401错误,一般就是因为Solr开启了基本认证。注意:是基本认证,不是Kerberos认证!
首先是CDH版本的4.10.3的Solr添加基本认证登录之后的增删改查。
private static void loginSolr(String username,String password) {
HttpClientUtil.setConfigurer(new PreemptiveBasicAuthConfigurer());
ModifiableSolrParams params = new ModifiableSolrParams();
params.set(HttpClientUtil.PROP_BASIC_AUTH_USER,username);
params.set(HttpClientUtil.PROP_BASIC_AUTH_PASS, password);
}
通过该方法,将用户和密码传入即可进行查询修改。
如果是通过HttpSolrServer进行连接的solr,那么就需要进行另一种认证方式,是HttpClient的抢先认证。
HttpSolrServer server = new HttpSolrServer("http://hadoop01:8983/solr/collection1");
HttpClientUtil.setBasicAuth((DefaultHttpClient) server.getHttpClient(), "user", "pass");
DefaultHttpClient m_client =(DefaultHttpClient)server.getHttpClient();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("user", "pass");
m_client.addRequestInterceptor(new PreemptiveAuthInterceptor(),0);
m_client.getCredentialsProvider().setCredentials(new AuthScope("hadoop01",8983), credentials);
server.setRequestWriter(new BinaryRequestWriter());
server.setAllowCompression(true);
该种方法必须单另创建一个类如下:
class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
// If no auth scheme avaialble yet, try to initialize it
// preemptively
if (authState.getAuthScheme() == null) {
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
if (creds == null) {
throw new HttpException("No credentials for preemptive authentication");
}
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
在你认证过程中如果出现这种报错:
Caused by: org.apache.http.client.ClientProtocolException: null
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:187)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
at org.apache.solr.client.solrj.impl.HttpSolrClient.executeMethod(HttpSolrClient.java:564)
... 12 common frames omitted
Caused by: org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity.
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:225)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
... 15 common frames omitted
那么证明你的认证程序大致方向是对的。需要进一步调整某些对象。
在Solr官网,目前提供的认证方式就是
SolrRequest req ;//create a new request object
req.setBasicAuthCredentials(userName, password);
solrClient.request(req);
QueryRequest req = new QueryRequest(new SolrQuery("*:*"));
req.setBasicAuthCredentials(userName, password);
QueryResponse rsp = req.process(solrClient);
我只能说,这个根本没屌用。只支持Solr5以上的,而且对于HttpSolrServer的一些其他操作,根本不起作用。局限性大的一批…
接下来是Solr5.X以上的版本的基本认证。
上面的那个登录的方法还是可以用的,但是需要重新添加两个类,因为Solr5.x没有那两个类,而且官方也没说更新版本之后PreemptiveBasicAuthConfigurer类被什么替换了。
其次是官方给的方法:
CloudSolrClient client = new CloudSolrClient(zkHost);
client.setDefaultCollection("collection1");
SolrQuery query = new SolrQuery();
query.set("q","*:*");
QueryRequest req = new QueryRequest(query);
req.setBasicAuthCredentials("username","password");
QueryResponse rsp = req.process(client);
SolrDocumentList list = rsp.getResults();
最后是Solr5.X之后的HttpSolrClient的基本认证:
在HttpSolrClient下面进行抢先认证:
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(config.getUser(), config.getPassword());
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
AuthScope.ANY,
creds);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.addInterceptorFirst(new PreemptiveAuthInterceptor());
builder.setDefaultCredentialsProvider(credsProvider);
CloseableHttpClient httpClient = builder.build();
client = new HttpSolrClient.Builder().withBaseSolrUrl(config.getSolrUrl())
.withHttpClient(httpClient)
.build();
class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
// If no auth scheme available yet, try to initialize it
// preemptively
if (authState.getAuthScheme() == null) {
CredentialsProvider credsProvider = (CredentialsProvider)
context.getAttribute(HttpClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
Credentials creds = credsProvider.getCredentials(authScope);
if(creds == null){
}
authState.update(new BasicScheme(), creds);
}
}
}
也有人提供这种方式:
CredentialsProvider provider = new BasicCredentialsProvider();
if (config.hasCredentials()) {
UsernamePasswordCredentials credentials
= new UsernamePasswordCredentials(config.getUser(), config.getPassword());
provider.setCredentials(AuthScope.ANY, credentials);
}
HttpClient httpClient = HttpClientBuilder.create()
.setDefaultCredentialsProvider(provider)
.build();
try (HttpSolrClient client = new HttpSolrClient.Builder().withBaseSolrUrl(config.getSolrUrl())
.withHttpClient(httpClient)
.build()) {
具体还是需要多次尝试。。。