Как настроить соединение Spring data SolrCloud с базовой аутентификацией

Я настроил Solr 6.2.1 как SolrCloud. Позже я настроил базовую аутентификацию. Я собираюсь настроить Spring data solr 2.0.4.RELEASE с помощью Solrj 6.2, и это мой код:

@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)

Как я могу решить проблему? Я думаю, что моя конфигурация неверна


person Saeed Shahsavan    schedule 29.10.2016    source источник
comment
Я изменил свою конфигурацию, теперь я использую solrj-5.5 и ZooKeeper 3.4.6, и я изменил эту строку: return new CloudSolrClient.Builder().withZkHost(zkHost).build(); to: вернуть новый CloudSolrClient(zkURL); но моя проблема не решена, теперь возникает это исключение. Введите content=text/html harset=utf-8/› 11:08:43,827 ‹title›Ошибка 401, требуется аутентификация‹/title› 11:08:43,827 ‹/head›   -  person Saeed Shahsavan    schedule 30.10.2016


Ответы (4)


Да, ваша конфигурация кажется неправильной. У меня была такая же проблема, как у вас

Я хотел бы использовать apache solr версии 6.6.0 и spring data solr версии 2.0.8 (куплен стартером весенней загрузки). Оказалось, что версия из spring data solr не поддерживает версию apache solr> 5, потому что при трассировке

в org.springframework.data.solr.core.SolrTemplate.execute(SolrTemplate.java:172) совершенно ясно, что когда solrTemplate собирается создатьClientForCore, он будет клонировать из настроенного нами cloudSolrClient

введите здесь описание изображения

проблема в String zkHost = (String)readField(solrClient, "zkHost"); * он вернет значение null, поскольку в версии apache solr> 5 zkHost хранится в «clusterStateProvider», а не на том же уровне, что и «cloudSolrClient».

Решено: если вы хотите продолжать использовать Spring Data Solr версии 2, вам необходимо понизить версию Apache Solr.

person thestalker    schedule 15.11.2018

Я нашел обходной путь для тех, у кого такая же проблема. Расширяет собственный HttpSolrClientFactory. проблема, вызванная неправильной настройкой LBHttpSolrClient httpClient. правильная настройка должна быть похожа на следующий блок if (solrClient instanceof LBHttpSolrClient) {...}

AuthHttpSolrClientFactory.java

@SuppressWarnings («устаревшее») открытый класс 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;
}

}

beans-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>
person Fei    schedule 16.10.2017
comment
для spring-data-data 3.0 просто удалите core из приведенного выше примера, все будет работать. - person Fei; 19.10.2017
comment
Действительно, в spring-data-solr 4 добавление else if сделало работу - person parisni; 26.09.2019

Есть несколько причин, по которым ваш код не работает, но в основном они связаны с отсутствующими функциями spring-data-solr.

Во-первых, spring-data-solr версии 2.0.4 не поддерживается для функций Solr 5 (облако). Вот почему вы получаете NullPointerException в методе org.springframework.data.solr.server.support.SolrClientUtils#cloneLBHttpSolrClient

Я попытался посмотреть, работает ли представленный вами сценарий с последним SNAPSHOT (2.1.0-SNAPSHOT) spring-data-solr и после нескольких изменений в классе конфигурации 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;
    }
}

Я по-прежнему получал ошибку аутентификации 401 при выполнении запроса solr (когда в solr была включена базовая аутентификация).

В ванильном приложении 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());

При поиске, используется ли метод SolrRequest#setBasicAuthCredentials(String, String) в коде spring-data-solr, я нигде не заметил, чтобы этот метод использовался. Таким образом, весьма вероятно, что эта функция еще не реализована даже в сборке SNAPSHOT для spring-data-solr.

Я создал запрос функции в проекте spring-data-solr, чтобы добавить поддержку этот функционал.

person marius_neo    schedule 01.11.2016

Была такая же проблема с Solr 7.7 и spring-boot-starter-data-solr 2.1.14 (использует spring-data-solr-4.0.17.RELEASE)

Пробовал несколько способов, включая создание собственного HttpSolrClientFactory. Это работает, но на самом деле делает 2 вызова Solr, и первый возвращает 401 Unauthorized.

Я исправляю проблему, расширяя CloudSolrClient (пытаясь выполнить правильную аутентификацию, как описано в Базовая аутентификация с помощью 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.");
    }
}
person Derp Derpington    schedule 14.01.2021