如何使用基本身份验证配置 Spring 数据 SolrCloud 连接

How to configure Spring data SolrCloud connection with Basic Authentication

我已经将 Solr 6.2.1 配置为 SolrCloud。后来我配置了基本身份验证。 我将使用 Solrj 6.2 配置 Spring data solr 2.0.4.RELEASE,这是我的代码:

@Configuration
@EnableSolrRepositories(basePackages = { "ir.saeed.server.solr" }, multicoreSupport = true)

public class SearchContext {

    @Value("${solr.host}")

    private String host;


    @Value("${solr.port}")

    private Integer port;


    @Value("${solr.username}")

    private String username;


    @Value("${solr.password}")

    private String password;


    @Value("${zkHost}")

    private String zkHost;


    @Value("${solr.coreName}")

    private String collectionName;


    @Bean

    public SolrTemplate solrTemplate() {

        return new SolrTemplate(solrClientFactory());

    }


    @Bean

    public BasicCredentialsProvider credentialsProvider() {

        BasicCredentialsProvider provider = new BasicCredentialsProvider();

        provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));

        return provider;

    }


    @Bean

    public SolrClientFactory solrClientFactory() {

        return new HttpSolrClientFactory(solrClient(), "", credentialsProvider().getCredentials(AuthScope.ANY), "BASIC");

    }


    @Bean

    public SolrClient solrClient() {

        return new CloudSolrClient.Builder().withZkHost(zkHost).build();

    }

}

但是当我 运行 我的网络应用程序出现这个异常时:

 10:51:48,110 org.springframework.data.solr.UncategorizedSolrException: nested exception is java.lang.NullPointerException


 10:51:48,111   at org.springframework.data.solr.core.SolrTemplate.execute(SolrTemplate.java:172)


 10:51:48,111   at org.springframework.data.solr.core.SolrTemplate.executeSolrQuery(SolrTemplate.java:509)


 10:51:48,111   at org.springframework.data.solr.core.SolrTemplate.query(SolrTemplate.java:504)


 10:51:48,111   at org.springframework.data.solr.core.SolrTemplate.doQueryForPage(SolrTemplate.java:338)


 10:51:48,111   at org.springframework.data.solr.core.SolrTemplate.queryForPage(SolrTemplate.java:350)

我该如何解决这个问题? 我认为我的配置不正确

您的代码无法运行的原因有多种,但主要是 spring-data-solr 缺少功能。

首先spring-data-solr版本2.0.4不支持Solr 5(云)特性。 所以这就是为什么你在方法 org.springframework.data.solr.server.support.SolrClientUtils#cloneLBHttpSolrClient

中得到 NullPointerException 的原因

我已经尝试查看您公开的场景是否适用于 spring-data-solr 的最新 SNAPSHOT (2.1.0-SNAPSHOT) 并且在对 SolrContext spring 配置 class :

@Configuration
@EnableSolrRepositories(basePackages = {"com.acme.solr"})
// notice that the multicoresupport is left false
// see org.springframework.data.solr.repository.config.SolrRepositoryConfigExtension#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource) for details
public class SolrContext {

    @Bean
    public Credentials credentials(@Value("${solr.username}") String username, @Value("${solr.password}") String
            password) {
        return new UsernamePasswordCredentials(username, password);
    }

    @Bean
    public BasicCredentialsProvider credentialsProvider(Credentials credentials) {
        BasicCredentialsProvider provider = new BasicCredentialsProvider();
        provider.setCredentials(AuthScope.ANY, credentials);

        return provider;
    }

    @Bean
    public SolrClientFactory solrClientFactory(SolrClient solrClient, Credentials credentials) {
        return new HttpSolrClientFactory(solrClient, "", credentials, "BASIC");

    }

    // create a solrtemplate bean, so that it is used in
    // org.springframework.data.solr.repository.support.SolrRepositoryFactoryBean#doCreateRepositoryFactory method
    // for using org.springframework.data.solr.repository.support.SolrRepositoryFactory#SolrRepositoryFactory(org.springframework.data.solr.core.SolrOperations) constructor
    @Bean
    public SolrTemplate solrTemplate(SolrClientFactory solrClientFactory){
        return new SolrTemplate(solrClientFactory);
    }

    @Bean
    public CloudSolrClient solrClient(@Value("${zkHost}") String zkHost) {
        CloudSolrClient solrClient = new CloudSolrClient.Builder().withZkHost(zkHost).build();
        solrClient.setDefaultCollection("gettingstarted");
        return solrClient;
    }
}

我在执行 solr 请求时仍然收到 401 身份验证问题(在 solr 上启用了基本身份验证)。

在 vanilla solrj 应用程序中,这是您发出经过身份验证的请求的方式:

    CloudSolrClient solr = new CloudSolrClient.Builder()
            .withZkHost("localhost:9983")
            .build();

    SolrQuery query = new SolrQuery();
    query.setQuery("*:*");
    SolrRequest<QueryResponse> req = new QueryRequest(query);
    req.setBasicAuthCredentials("solr", "SolrRocks");
    QueryResponse rsp = req.process(solr, "gettingstarted");

    System.out.println("numFound: " + rsp.getResults().getNumFound());

在查看 spring-data-solr 的代码中是否使用了方法 SolrRequest#setBasicAuthCredentials(String, String) 时,我没有注意到任何地方使用了此方法。因此,很可能这个功能还没有实现,甚至在 spring-data-solr.

的 SNAPSHOT 版本上也没有实现。

我在 spring-data-solr 项目上创建了一个 feature request 以添加对此功能的支持。

我为遇到相同问题的人找到了解决方法 扩展您自己的 HttpSolrClientFactory。由 LBHttpSolrClient httpClient 未正确设置引起的问题。正确的设置应该像下面的块 if (solrClient instanceof LBHttpSolrClient) {...}

AuthHttpSolrClientFactory.java

@SuppressWarnings("deprecation") public class AuthHttpSolrClientFactory 扩展了 HttpSolrClientFactory {

public AuthHttpSolrClientFactory(SolrClient solrClient, String core, Credentials credentials, String authPolicy) {
    super(solrClient, core, credentials, authPolicy);
    Assert.notNull(solrClient, "solrClient must not be null");
    if (authPolicy != null) {
        Assert.hasText(authPolicy);
    }
    appendBasicAuthentication(credentials, authPolicy, this.getSolrClient());
}

private void appendBasicAuthentication(Credentials credentials, String authPolicy,  SolrClient solrClient) {
    if( credentials != null) {
        if (solrClient instanceof HttpSolrClient) {
            HttpSolrClient httpSolrClient = (HttpSolrClient) solrClient;
            if (assertHttpClientInstance(httpSolrClient.getHttpClient())) {
                AbstractHttpClient httpClient = (AbstractHttpClient) httpSolrClient.getHttpClient();
                httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), credentials);
                httpClient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF, Arrays.asList(authPolicy));
            }
        }
        else if (solrClient instanceof LBHttpSolrClient) {
            LBHttpSolrClient lbhttpSolrClient = (LBHttpSolrClient) solrClient;
            if (assertHttpClientInstance(lbhttpSolrClient.getHttpClient())) {
                AbstractHttpClient httpClient = (AbstractHttpClient) lbhttpSolrClient.getHttpClient();
                httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), credentials);
                httpClient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF, Arrays.asList(authPolicy));
            }
        }
    }
}

private boolean assertHttpClientInstance(HttpClient httpClient) {
    Assert.isInstanceOf(AbstractHttpClient.class, httpClient,
            "HttpClient has to be derivate of AbstractHttpClient in order to allow authentication.");
    return true;
}

}

豆子-solr.xml

<solr:solr-client id="solrClient" url="${solr.host}" />
<bean id="credentials" class="org.apache.http.auth.UsernamePasswordCredentials">
    <constructor-arg type="java.lang.String" value="${solr.credentials}"/>
</bean>
<bean id="solrClientFactory" class="com.example.solr.AuthHttpSolrClientFactory" scope="singleton">
    <constructor-arg ref="solrClient" />
    <constructor-arg name="core">
        <null />
    </constructor-arg>
    <constructor-arg ref="credentials" />
    <constructor-arg type="java.lang.String" value="BASIC"/>
</bean>
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate" scope="singleton">
    <constructor-arg ref="solrClientFactory" />
    <constructor-arg name="requestMethod">
        <value type="org.springframework.data.solr.core.RequestMethod">POST</value>
    </constructor-arg>
</bean>

是的,您的配置似乎不正确。我遇到了和你一样的问题

我想使用 apache solr 版本 6.6.0 和 spring data solr 版本 2.0.8(由 spring boot starter 购买)。事实证明,来自 spring data solr 的版本不支持 > 5 的 apache solr 版本,因为当您跟踪

在org.springframework.data.solr.core.SolrTemplate.execute(SolrTemplate.java:172),很明显solrTemplate在createClientForCore的时候会从我们配置好的cloudSolrClient克隆

问题出在 String zkHost = (String)readField(solrClient, "zkHost"); * 它将 return null 因为在 apache solr 版本 > 5 中,zkHost 存储在 "clusterStateProvider" 中,与 "cloudSolrClient"

不在同一级别

已解决: 如果你想继续使用spring data solr version 2,你需要降级apache solr version

使用 Solr 7.7 和 spring-boot-starter-data-solr 2.1.14(使用 spring-data-solr-4.0.17.RELEASE)有同样的问题

尝试了几种方法,包括创建自定义 HttpSolrClientFactory。它有效,但实际上对 Solr 进行了 2 次调用,第一个 returns 401 未经授权。

我解决了扩展 CloudSolrClient 的问题(尝试按照 Basic Auth with SolrJ 中所述进行正确的身份验证)

它只调用一次 Solr 并使用基本身份验证

public class BasicAuthCloudSolrClient extends CloudSolrClient {

private final Credentials credentials;

/**
 * Create a new client object that connects to Zookeeper using BASIC Authentication and is always aware
 * of the SolrCloud state. If there is a fully redundant Zookeeper quorum and
 * SolrCloud has enough replicas for every shard in a collection, there is no
 * single point of failure. Updates will be sent to shard leaders by default.
 *
 * @param builder a {@link BasicAuthCloudSolrClient.Builder} with the options used to create the client.
 */
protected BasicAuthCloudSolrClient(Builder builder) {
    super(builder);
    this.credentials = builder.credentials;
}

@Override
public QueryResponse query(String collection, SolrParams params, SolrRequest.METHOD method)
        throws
        SolrServerException, IOException {
    QueryRequest request = new QueryRequest(params, method);
    request.setBasicAuthCredentials(credentials.getUserPrincipal().getName(),
            credentials.getPassword());

    return request.process(this, collection);
}


/**
 * Constructs {@link BasicAuthCloudSolrClient} instances from provided configuration.
 */
public static class Builder extends CloudSolrClient.Builder {
    protected Credentials credentials;

    /**
     * @deprecated use other constructors instead.  This constructor will be changing visibility in an upcoming release.
     */
    @Deprecated
    public Builder() {
    }

    /**
     * Provide a series of ZK hosts which will be used when configuring {@link CloudSolrClient} instances.
     *
     * @param zkHosts     a List of at least one ZooKeeper host and port (e.g. "zookeeper1:2181")
     * @param credentials a credentials to connect to Solr.
     */
    public Builder(List<String> zkHosts, Credentials credentials) {
        super(zkHosts, empty());
        this.credentials = credentials;
    }


    /**
     * Create a {@link CloudSolrClient} based on the provided configuration.
     */
    public BasicAuthCloudSolrClient build() {

        if (Objects.isNull(credentials)) {
            throw new IllegalArgumentException(
                    "Credentials must be provided to initialize BasicAuthCloudSolrClient");
        }
        if (stateProvider == null) {
            if (!zkHosts.isEmpty()) {
                stateProvider = new ZkClientClusterStateProvider(zkHosts, zkChroot);
            } else if (!this.solrUrls.isEmpty()) {
                try {
                    stateProvider = new HttpClusterStateProvider(solrUrls, httpClient);
                } catch (Exception e) {
                    throw new RuntimeException(
                            "Couldn't initialize a HttpClusterStateProvider (is/are the "
                                    + "Solr server(s), " + solrUrls + ", down?)", e);
                }
            } else {
                throw new IllegalArgumentException("Both zkHosts and solrUrl cannot be null.");
            }
        }
        return new BasicAuthCloudSolrClient(this);
    }

    @Override
    public BasicAuthCloudSolrClient.Builder getThis() {
        return this;
    }
}

配置如下所示:

@Bean
public Credentials solrCredentials(@Value("${solr.username}") String username, @Value("${solr.password}") String
        password) {
    return new UsernamePasswordCredentials(username, password);
}


@Bean
public SolrClientFactory solrClientFactory(SolrClient solrClient,
        Credentials solrCredentials) {
    return new HttpSolrClientFactory(solrClient, solrCredentials, AuthSchemes.BASIC);
}

@Bean
public SolrTemplate solrTemplate(SolrClientFactory solrClientFactory){
    return new SolrTemplate(solrClientFactory);
}

@Bean
public SolrClient solrClient(Credentials solrCredentials) {
    if (isNotEmpty(properties.getZkHosts())) {

        BasicAuthCloudSolrClient solrClient =
                new BasicAuthCloudSolrClient.Builder(properties.getZkHosts(), solrCredentials).build();
        solrClient.setDefaultCollection(properties.getCollection());
        return solrClient;
    } else {
        throw new IllegalStateException("ZkHosts is required for application startup.");
    }
}