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

时间:2016-10-29 07:50:15

标签: basic-authentication solrj solrcloud spring-data-solr

我已将Solr 6.2.1配置为SolrCloud。后来我配置了基本身份验证。 我将使用Solrj 6.2配置Spring数据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();

    }

}

但是,当我运行我的Web应用程序时,会发生此异常:

 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)

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

4 个答案:

答案 0 :(得分:0)

您的代码无法正常工作有几个原因,但主要是因为spring-data-solr缺少功能。

首先,spring-data-solr版本2.0.4不支持Solr 5(云)功能。 因此,这就是您在方法NullPointerException

中获得org.springframework.data.solr.server.support.SolrClientUtils#cloneLBHttpSolrClient的原因

我试图看看你公开的场景是否适用于spring-data-solr的最新SNAPSHOT(2.1.0-SNAPSHOT)以及SolrContext spring配置类的一些修改:

@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请求时(在solr上启用基本身份验证时),我仍然收到401身份验证问题。

在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来添加对此功能的支持。

答案 1 :(得分:0)

我为那些有同样问题的人找到了解决方法 扩展您自己的HttpSolrClientFactory。 LBHttpSolrClient httpClient未正确设置导致的问题。正确的设置应该类似于以下块 if(solrClient instanceof LBHttpSolrClient){...}

<强> AuthHttpSolrClientFactory.java

@SuppressWarnings(&#34;弃用&#34) 公共类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>

答案 2 :(得分:0)

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

我想使用6.6.0版的apache solr和2.0.8版的spring数据solr(由spring boot starter购买)。原来,spring data solr的版本不支持apache solr version> 5,因为当您跟踪

在org.springframework.data.solr.core.SolrTemplate.execute(SolrTemplate.java:172)上,很明显,当solrTemplate打算创建createClientForCore时,它将从我们已配置的cloudSolrClient中克隆

enter image description here

问题出在字符串zkHost =(String)readField(solrClient,“ zkHost”); *由于在apache solr版本> 5中,它将返回null,因为zkHost存储在“ clusterStateProvider”中,而不与“ cloudSolrClient”处于同一级别

已解决: 如果要继续使用spring data solr版本2,则需要降级apache solr版本

答案 3 :(得分:0)

在使用 Solr 7.7 和 spring-boot-starter-data-solr 2.1.14(使用 spring-data-solr-4.0.17.RELEASE)时遇到了同样的问题

尝试了几种方法,包括创建自定义 HttpSolrClientFactory。它有效,但实际上对 Solr 进行了 2 次调用,第一个返回 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.");
    }
}