SpringBoot2.X连接ElasticSearch集成Se

2019-03-28  本文已影响0人  丿捺人生

前言

先说需求吧,我的需求:不同项目分配不同用户,用户数据独立;
但是我在网上找了很多资料都是基于证书实现连接的,疑问如下

      1.如何生成多个证书?
      2.证书如何分配权限?
      3.如何使多个证书同时生效?
      4.能否基于用户名密码方式连接
带着几点疑问开始了编程之旅!!!

废话不多数直接上代码,官方文档
版本介绍:
  JDK:1.8
  SpringBoot:2.0.1
  ElasticSearch:6.6.2
  Search Guard:6.6.2-24.2
需要准备证书(上篇文章中在线生成证书中有这些文件)
  demouser-keystore.jks
  sgadmin-keystore.jks
  truststore.jks

证书来源参考:https://www.jianshu.com/p/de341fdb2789

配置目录结构及配置文件
image.png
pom.xml文件配置
      <properties>
          <spring.boot.version>2.0.1.RELEASE</spring.boot.version>
          <java.version>1.8</java.version>
          <elasticsearch.version>6.6.2</elasticsearch.version>
       </properties>
       <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>transport</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <!-- 添加 transport-netty4-client maven 依赖之后可以成功获取到连接  -->
        <dependency>
            <groupId>org.elasticsearch.plugin</groupId>
            <artifactId>transport-netty4-client</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <dependency>
            <groupId>com.floragunn</groupId>
            <artifactId>search-guard-6</artifactId>
            <version>6.6.2-24.2-api</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.10</version>
        </dependency>
ElasticSearchClient.java
package com.isoftstone.ismart.elastic.config;

import com.floragunn.searchguard.ssl.SearchGuardSSLPlugin;
import com.floragunn.searchguard.ssl.util.SSLConfigConstants;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.InetAddress;
import java.net.URL;
import java.net.URLDecoder;

/**
 * @author Colin.Ye
 * @version 1.0
 * @ClassName ElasticSearchClient
 * @date 2019/3/25
 **/
@Configuration
public class ElasticSearchClient {

    @Value("${spring.data.elasticsearch.cluster-nodes}")
    private String nodes;

    @Value("${spring.data.elasticsearch.cluster-name}")
    private String custerName;

    @Value("${spring.data.elasticsearch.ssl-keystore-password}")
    private String sslKeystorePassword;

    @Value("${spring.data.elasticsearch.ssl-truststore-password}")
    private String sslTruststorePassword;

    //注入的ElasticSearch实例
    @Bean(name = "esClient")
    public TransportClient getclient() throws Exception {
        ClassLoader classLoader = ElasticSearchClient.class.getClassLoader();
        URL resource = classLoader.getResource("ca/demouser-keystore.jks");
//        URL resource = classLoader.getResource("ca/sgadmin-keystore.jks");
        URL truresource = classLoader.getResource("ca/truststore.jks");
        String keypath = URLDecoder.decode(resource.getPath(), "UTF-8");
        String trupath = URLDecoder.decode(truresource.getPath(), "UTF-8");
        //windows中路径会多个/ 如/E windows下需要打开注释
        try {
            String osName = System.getProperty("os.name");
            if (StringUtils.contains(osName, "Windows")) {
                if (keypath.startsWith("/")) {
                    keypath = keypath.substring(1, keypath.length());
                }
                if (trupath.startsWith("/")) {
                    trupath = trupath.substring(1, trupath.length());
                }
            }
        } catch (Exception e) {
            System.out.println(e);
        }

        Settings settings = Settings.builder()
                .put("cluster.name", custerName)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_ENABLED, true)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_KEYSTORE_FILEPATH, keypath)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_TRUSTSTORE_FILEPATH, trupath)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_KEYSTORE_PASSWORD, sslKeystorePassword)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_TRUSTSTORE_PASSWORD, sslTruststorePassword)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_HTTP_KEYSTORE_PASSWORD, sslKeystorePassword)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_HTTP_TRUSTSTORE_PASSWORD, sslTruststorePassword)
                .put("client.transport.ignore_cluster_name", true)
                .put(SSLConfigConstants.SEARCHGUARD_SSL_TRANSPORT_ENFORCE_HOSTNAME_VERIFICATION, false)
                .build();
        TransportClient client = new PreBuiltTransportClient(settings, SearchGuardSSLPlugin.class);
//        TransportClient client = new PreBuiltTransportClient(settings);
//        System.out.println("Basic " + new String(Base64.encodeBase64("admin:admin".getBytes())));
//        client.threadPool().getThreadContext().putHeader("Authorization",
//                "Basic " + new String(Base64.encodeBase64("admin:admin".getBytes())));
        try {
            String[] nodeArray = nodes.split(",");
            for (String node : nodeArray) {
                String[] nodeArr = node.split(":");
                client.addTransportAddress(new TransportAddress(InetAddress.getByName(nodeArr[0]), Integer.parseInt(nodeArr[1])));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
//        client.admin().cluster().nodesInfo(new NodesInfoRequest()).actionGet();
        return client;
    }
}
LowCreateIndexDemo.java
package com.isoftstone.ismart.elastic.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.isoftstone.ismart.elastic.model.result.Result;
import com.isoftstone.ismart.elastic.model.result.ResultCode;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.UUID;

/**
 * @author Colin.Ye
 * @version 1.0
 * @ClassName LowCreateIndexDemo
 * @date 2019/3/13
 **/
@RestController
@RequestMapping("/lowClient/v1/")
@CrossOrigin
public class LowCreateIndexDemo {

    @Autowired
    public TransportClient client;

    /**
     * 创建索引
     *
     * @param index
     * @param json
     * @return
     */
    @RequestMapping(value = "createIndex/{index}/{type}", method = {RequestMethod.POST, RequestMethod.PUT})
    public Result createIndex(HttpServletRequest httpRequest,
                              @PathVariable String index, @PathVariable String type, @RequestBody String json) {
        // 1、创建 创建索引request
        try {
            if (StringUtils.isBlank(index) || StringUtils.isBlank(type)) {
                return Result.failure(ResultCode.PARAM_NOT_INDEX);
            }
            if (StringUtils.isBlank(json)) {
                return Result.failure(ResultCode.PARAM_NOT_SETTING);
            }
            CreateIndexRequest request = new CreateIndexRequest(index);
            JSONObject reqJson;
            try {
                reqJson = JSONObject.parseObject(json);
            } catch (Exception e) {
                return Result.failure(ResultCode.PARAM_JSON_ERROR);
            }

            /**
             *  2、设置索引的settings
             *  index.number_of_shards:分片数
             *  index.number_of_replicas:副本数
             *  analysis.analyzer.default.tokenizer:默认分词器
             *      ik_max_word:会将文本做最细粒度的拆分
             *      ik_smart:会做最粗粒度的拆分
             *      standard:默认分词器
             */
            JSONObject settingJson = reqJson.getJSONObject("settings");
            Integer shards = 3;
            Integer replicas = 2;
            String analysis = "standard";
            if (settingJson != null) {
                shards = settingJson.getInteger("shards");
                replicas = settingJson.getInteger("replicas");
                analysis = settingJson.getString("analysis") == null ? "standard" : settingJson.getString("analysis");
            }
            request.settings(Settings.builder().put("index.number_of_shards", shards)
                    .put("index.number_of_replicas", replicas)
                    .put("analysis.analyzer.default.tokenizer", analysis)
            );

            JSONObject array = reqJson.getJSONObject("mapping");
            if (array != null && array.size() > 0) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put(type, new JSONObject() {{
                    put("properties", new JSONObject() {{
                        for (Entry<String, Object> obj : array.entrySet()) {
                            put(obj.getKey(), new JSONObject() {{
                                put("type", obj.getValue().toString());
                            }});
                        }
                    }});
                }});
                System.out.println(jsonObject.toJSONString());
                // 3、设置索引的mappings
                request.mapping(type, jsonObject.toJSONString(), XContentType.JSON);
            }
            // 4、 设置索引的别名
//            request.alias(new Alias("mmm"));
            // 5、 发送请求 这里和RESTful风格不同
            boolean b = setAuthHeader(httpRequest);
            if (!b) {
                return Result.failure(ResultCode.PERMISSION_NO_ACCESS);
            }
            CreateIndexResponse createIndexResponse = client.admin().indices().create(request).get();
            // 6、处理响应
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("acknowledged", createIndexResponse.isAcknowledged());
            jsonObject.put("shardsAcknowledged", createIndexResponse.isAcknowledged());
            return Result.success(jsonObject);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.failure(ResultCode.SPECIFIED_QUESTIONED_USER_NOT_EXIST, e.getMessage());
        }
    }

    /**
     * 添加文档
     *
     * @param index
     * @return
     */
    @RequestMapping(value = "/save/{index}/{type}", method = RequestMethod.POST)
    public Result save(HttpServletRequest httpRequest, @PathVariable String index, @PathVariable String type, @RequestBody String json) {
        if (StringUtils.isBlank(index) || StringUtils.isBlank(type)) {
            return Result.failure(ResultCode.PARAM_NOT_INDEX);
        }
        if (StringUtils.isBlank(json)) {
            return Result.failure(ResultCode.PARAM_NOT_SETTING);
        }
        JSONObject jsonObject = JSONObject.parseObject(json);
        if (jsonObject == null) {
            return Result.failure(ResultCode.PARAM_IS_BLANK);
        }
        String id = jsonObject.getString("id");
        if (StringUtils.isBlank(id)) {
            id = UUID.randomUUID().toString().replaceAll("-", "");
        }
        boolean b = setAuthHeader(httpRequest);
        if (!b) {
            return Result.failure(ResultCode.PERMISSION_NO_ACCESS);
        }
        IndexResponse response = client.prepareIndex(index, type, id).setSource(jsonObject.getInnerMap()).execute().actionGet();
        return Result.success(response.toString());
    }

    /**
     * 查询数据
     *
     * @param index
     * @param type
     * @param json
     * @return
     */
    @RequestMapping(value = "/search/{index}/{type}/{page}/{pageSize}", method = RequestMethod.POST)
    public Result search(HttpServletRequest httpRequest,
                         @PathVariable String index,
                         @PathVariable String type,
                         @PathVariable Integer page,
                         @PathVariable Integer pageSize, @RequestBody String json) {
        try {
            // 构造查询对象的工厂类 QueryBuilders,matchQuery全文查询,Operator.AND指定分词项之间采用AND方式连接,默认是OR
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            // 3.设置boolQueryBuilder条件
            // 子boolQueryBuilder条件条件,用来表示查询条件or的关系

            JSONObject jsonObject = JSONObject.parseObject(json);
            for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                boolQueryBuilder.must(QueryBuilders.matchQuery(entry.getKey(), entry.getValue().toString()));
            }

            BoolQueryBuilder childBoolQueryBuilder = new BoolQueryBuilder()
                    .should(QueryBuilders.matchPhraseQuery("comment_content", "1"))
                    .should(QueryBuilders.matchPhraseQuery("comment_content", "2"));
            // 4.添加查询条件到boolQueryBuilder中
//        boolQueryBuilder
//                .must(childBoolQueryBuilder);
//                .must(QueryBuilders.matchQuery());


            //构造HighlightBuilder对象,设置需要高亮的字段并自定义高亮标签
            HighlightBuilder highlighter = new HighlightBuilder()
                    .field("comment_content")
                    .preTags("<span stype=\"color:red\">")
                    .postTags("</span>");

            boolean b = setAuthHeader(httpRequest);
            if (!b) {
                return Result.failure(ResultCode.PERMISSION_NO_ACCESS);
            }
            SearchResponse response = client.prepareSearch(index)
                    .setTypes(type)
                    .setQuery(boolQueryBuilder)
                    .highlighter(highlighter)
                    .setSize(pageSize)
                    .setFrom(page)
//                .addSort("create_time", SortOrder.DESC)
                    .get();

            //通过上面获得的SearchResponse对象,取得返回结果
            SearchHits hits = response.getHits();
            //搜索到的结果数
//        System.out.println("共搜索到:" + hits.getTotalHits());

            JSONArray array = new JSONArray();
            //遍历SearchHits数组
            for (SearchHit hit : hits) {
                array.add(JSONObject.parse(hit.getSourceAsString()));
//            System.out.println("Source:" + hit.getSourceAsString());//返回String类型的文档内容
//            System.out.println("Source As Map:" + hit.getSource());//返回Map格式的文档内容
//            System.out.println("Index:" + hit.getIndex());//返回文档所在的索引
//            System.out.println("Type:" + hit.getType());//返回文档所在的类型
//            System.out.println("ID:" + hit.getId());//返回文档的id
//            System.out.println("Source:" + hit.getSource().get("price"));//从返回的map中通过key取到value
//            System.out.println("Score:" + hit.getScore());//返回文档的评分
                //getHighlightFields()会返回文档中所有高亮字段的内容,再通过get()方法获取某一个字段的高亮片段,最后调用getFragments()方法,返回Text类型的数组
//            Text[] texts = hit.getHighlightFields().get("title").getFragments();
//            if(texts != null) {
//                //遍历高亮结果数组,取出高亮内容
//                for (Text text : texts) {
//                    System.out.println(text.string());
//                }
//            }
            }
            return Result.success(array);
        }catch (Exception e){
            return Result.failure(ResultCode.SPECIFIED_QUESTIONED_USER_NOT_EXIST, e.getMessage());
        }
    }
    // 获取请求消息头中的用户信息,格式【用户名:密码】
    private boolean setAuthHeader(HttpServletRequest httpRequest) {
        String authorization = httpRequest.getHeader("Authorization");
        if (StringUtils.isNotBlank(authorization)) {
            client.threadPool().getThreadContext().putHeader("Authorization",
                    "Basic " + new String(Base64.encodeBase64(authorization.getBytes())));
            return true;
        }
        return false;
    }
}
效果图

结语 :

经过一天时间总算是实现了,但是为什么这样就行,我也不知道!!!
存疑:es服务中没有配置demouser证书,为什么java client配置demouser证书后账号密码方式就生效了?

借鉴文章如下:
SearchGuard 实践
elasticsearch系列七:ES Java客户端-Elasticsearch Java client
Elasticsearch使用searchguard后Java连接及安全验证

上一篇下一篇

猜你喜欢

热点阅读