Solr Identity authentication and authorization update exception solution

In the previous solr tutorial, permission verification is not enabled for solr, and authentication is not required for all operations. At that time, it was mentioned that if solr enabled permission verification, you can change solr’s host with the user name/password. However, the real situation is not quite the same. If the query is OK, the modification operation will be thrown exception

This article will take you to understand, what is this ghost animal phenomenon

I. Solr Configures user login

1. Install

In previous solr series tutorials, solr installed by Docker, the following steps are also directly for solr configuration in Docker, the basic steps are the same

For details, please refer to: [Search series] Solr environment construction and simple test

If you don’t want to see it, just use the following command:

docker pull solr
docker run --name my-solr -d -p 8983:8983 -t solr
Copy the code

2. The configuration

How to configure user login authentication for Solr8

To access the instance, use the root user; otherwise, some operations may not be authorized

docker exec  -u root -it my-solr /bin/bash
Copy the code

Create an authentication file

vim server/etc/verify.properties
Copy the code

The content is in the following format: user name: password, permission, one account per row

root:123,admin
Copy the code

Configure the authentication file

vim server/contexts/solr-jetty-context.xml
Copy the code

Add the following to the Configure tag

<Get name="securityHandler">
   <Set name="loginService">
           <New class="org.eclipse.jetty.security.HashLoginService">
                  <Set name="name">Verify the name -</Set>
                  <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/verify.properties</Set>
           </New>
   </Set>
</Get>
Copy the code

Modify the web. The XML

vim server/solr-webapp/webapp/WEB-INF/web.xml
Copy the code

Under the security-Constraint TAB, add

<login-config>
		<auth-method>BASIC</auth-method>
		<! -- Notice that the name is the same as the name in the Set tag -->
		<realm-name>verify-name</realm-name>
</login-config>
Copy the code

Restart Solr for the configuration to take effect

docker restart my-solr
Copy the code

II. Scene replaying

Let’s talk about our environment

  • Springboot: 2.2.1. RELEASE
  • Solr: 8.0

1. Project environment

To build a simple Springboot project, XML depends on the following

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1. RELEASE</version>
    <relativePath/> <! -- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-solr</artifactId>
    </dependency>

    <! -- Please note that when solr turns on login authentication, this dependency must have -->
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
    </dependency>
</dependencies>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/libs-snapshot-local</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/libs-milestone-local</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-releases</id>
        <name>Spring Releases</name>
        <url>https://repo.spring.io/libs-release-local</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
Copy the code

The corresponding configuration file application.yml

spring:
  data:
    solr:
      Please note that the username and password are written directly in the URL
      host: http://root:[email protected]:8983/solr
Copy the code

2. The repetition

If you have any questions about the basic operation of Solr, you can refer to my previous search series of blog posts to meet your literacy needs.

The core solr operation example is as follows:

@Data
public class DocDO implements Serializable {
    private static final long serialVersionUID = 7245059137561820707L;
    @Id
    @Field("id")
    private Integer id;
    @Field("content_id")
    private Integer contentId;
    @Field("title")
    private String title;
    @Field("content")
    private String content;
    @Field("type")
    private Integer type;
    @Field("create_at")
    private Long createAt;
    @Field("publish_at")
    private Long publishAt;
}

@Component
public class SolrOperater {

    @Autowired
    private SolrTemplate solrTemplate;


    public void operate(a) {
        testAddByDoc();
        queryById();
    }

    public void testAddByDoc(a) {
        SolrInputDocument document = new SolrInputDocument();
        document.addField("id".999999);
        document.addField("content_id".3);
        document.addField("title"."testAddByDoc!");
        document.addField("content"."Add da da da");
        document.addField("type".2);
        document.addField("create_at", System.currentTimeMillis() / 1000);
        document.addField("publish_at", System.currentTimeMillis() / 1000);

        UpdateResponse response = solrTemplate.saveDocument("yhh", document, Duration.ZERO);
        solrTemplate.commit("yhh");
        System.out.println("over:" + response);
    }

    private void queryById(a) {
        DocDO ans = solrTemplate.getById("yhh".999999, DocDO.class).get();
        System.out.println("queryById: "+ ans); }}Copy the code

SolrTemplat is defined as follows

@Configuration
public class SearchAutoConfig {
    @Bean
    @ConditionalOnMissingBean(SolrTemplate.class)
    public SolrTemplate solrTemplate(SolrClient solrClient) {
        return newSolrTemplate(solrClient); }}Copy the code

To begin testing

@SpringBootApplication
public class Application {

    public Application(SolrOperater solrOperater) {
        solrOperater.operate();
    }

    public static void main(String[] args) { SpringApplication.run(Application.class); }}Copy the code

Note that when the above scenario is replicated, the query is found to be fine, and modifications throw an exception

3. Solutions

A. version

When I used Solr before, it was the same as the above operation mode, but there was no such problem, which was a bit painful;

Find the previous project to check the version, found that the previous solr-Solrj used 6.6.5, try to change the version (the default version is 8.2.0)

<dependency>
    <groupId>org.apache.solr</groupId>
    <artifactId>solr-solrj</artifactId>
    <version>6.6.5</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-solr</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.apache.solr</groupId>
            <artifactId>solr-solrj</artifactId>
        </exclusion>
    </exclusions>
</dependency>
Copy the code

Witness the miracle of the moment, the execution is normal, although the saveDocument method call red, but does not affect the specific execution oh

b. SystemDefaultHttpClient

Through a debugging, step by step, finally find out why 6.6.5 solr-Solrj works normally, but 8.2.0 doesn’t (if you want to know this boring process, please let me know in the comments, otherwise I don’t know when I can see 😂).

The key problem is that older versions use SystemDefaultHttpClient to implement solR communication; The new version uses InternalHttpClient

One possible solution is to specify Solr’s HttpClient instead of downgrading

In the configuration class, do the following:

@Bean
public HttpSolrClient solrClient(a) {
    HttpClient httpClient = new SystemDefaultHttpClient();
    return new HttpSolrClient.Builder(url).withHttpClient(httpClient).build();
}
Copy the code

Then test, also normal execution, output results do not screen shots, you can test yourself

C. HttpClient interceptor

Preemptive Basic Authentication with Apache HttpClient 4 Preemptive Basic authentication with Apache HttpClient 4

SystemDefaultHttpClient has a delete annotation, which means it is not recommended to use it directly, so we can use it to meet our needs, so we can do as follows

@Value("${spring.data.solr.host}")
private String url;

@Data
public static class UrlDo {
    private String url;

    private String user;
    private String pwd;

    private String host;
    private int port;

    public static UrlDo parse(String url) throws MalformedURLException {
        / / http://root:[email protected]:8983/solr
        URL u = new URL(url);
        UrlDo out = new UrlDo();
        out.setHost(u.getHost());
        out.setPort(u.getPort());

        String userInfo = u.getUserInfo();
        if(! StringUtils.isEmpty(userInfo)) { String[] users = org.apache.commons.lang3.StringUtils.split(userInfo,":");
            out.setUser(users[0]);
            out.setPwd(users[1]);
        }
        out.setUrl(url);
        returnout; }}public class SolrAuthInterceptor implements HttpRequestInterceptor {
    @Override
    public void process(final HttpRequest request, final HttpContext context) {
        AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
        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);
            authState.update(newBasicScheme(), creds); }}}@Bean
public HttpSolrClient solrClient(a) throws MalformedURLException {
    UrlDo urlDo = UrlDo.parse(url);
    CredentialsProvider provider = new BasicCredentialsProvider();
    provider.setCredentials(new AuthScope(urlDo.getHost(), urlDo.getPort()),
            new UsernamePasswordCredentials(urlDo.getUser(), urlDo.getPwd()));

    HttpClientBuilder builder = HttpClientBuilder.create();
    // Notice the following line, specifying the interceptor, which is used to set authentication information
    builder.addInterceptorFirst(new SolrAuthInterceptor());
    builder.setDefaultCredentialsProvider(provider);
    CloseableHttpClient httpClient = builder.build();
    return new HttpSolrClient.Builder(url).withHttpClient(httpClient).build();
}
Copy the code

The above implementation is a bit long, so let’s break it down

  • UrlDoParse solr’s URL to get what we needhost + port + user + password
  • solrClient: in creatingSolrClientBean instance, specify the appropriate authorization information
  • SolrAuthInterceptor: Custom interceptor, updatedauthStateinformation

d. SolrRequest

The above three methods are applicable to solr using SolrClient or SolrTemplate. Of course, I can discard them completely and use SolrRequest directly, as follows

SolrInputDocument document = new SolrInputDocument();
document.addField("id".999999);
document.addField("content_id".3);
document.addField("title"."testAddByDoc!");
document.addField("content"."Add da da da");
document.addField("type".2);
document.addField("create_at", System.currentTimeMillis() / 1000);
document.addField("publish_at", System.currentTimeMillis() / 1000);

UpdateRequest updateRequest = new UpdateRequest();
updateRequest.setBasicAuthCredentials("root"."123");
updateRequest.add(document);
UpdateResponse response = updateRequest.process(solrClient, "yhh");
updateRequest.commit(solrClient, "yhh");
Copy the code

4. Summary

This post focuses on four solutions for solr update exceptions that require login authentication

  • dropsolr-solrjVersion to6.6.0
  • The specifiedSolrClienttheHttpClientforSystemDefaultHttpClient
  • HttpClient interceptor
  • SolrRequest Specifies the user name and password

The solution is given above, but why is this a problem?

Directly test the solr update operation by using curl. It returned normally and there was no problem. Then what is the cause of this problem and whose pot is it

II. The other

Series of blog posts & project source code

Refer to the post

  • Configure user login authentication for SolR8
  • Preemptive Basic authentication with Apache HttpClient 4

Series of blog posts

  • 200115-SpringBoot series Solr query using posture summary
  • 200114-SpringBoot series tutorial Solr documentation removed
  • 190526-SpringBoot Advanced Search for Solr documentation added and modified using posture
  • 190510-SpringBoot Advanced Search Solr environment setup and simple test

Engineering source

  • Project: github.com/liuyueyi/sp…
  • Source: github.com/liuyueyi/sp…

1. An ashy Blog

As far as the letter is not as good, the above content is purely one’s opinion, due to the limited personal ability, it is inevitable that there are omissions and mistakes, if you find bugs or have better suggestions, welcome criticism and correction, don’t hesitate to appreciate

Below a gray personal blog, record all the study and work of the blog, welcome everyone to go to stroll

  • A grey Blog Personal Blog blog.hhui.top
  • A Grey Blog-Spring feature Blog Spring.hhui.top