美文网首页
SpringBoot2.X连接ElasticSearch集成Se

SpringBoot2.X连接ElasticSearch集成Se

作者: 丿捺人生 | 来源:发表于2019-03-28 10:54 被阅读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;
        }
    }
    
    效果图
    • 使用colin用户访问colin数据


      image.png
    • 使用colin用户访问其他索引数据


      image.png
    • 使用admin用户访问数据


      image.png

    结语 :

    经过一天时间总算是实现了,但是为什么这样就行,我也不知道!!!
    • 使用sgadmin-keystore.jks,不需要在请求时添加消息头
    • 使用demouser-keystore.jks,需要每次请求时添加用户信息消息头
    存疑:es服务中没有配置demouser证书,为什么java client配置demouser证书后账号密码方式就生效了?

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

    相关文章

      网友评论

          本文标题:SpringBoot2.X连接ElasticSearch集成Se

          本文链接:https://www.haomeiwen.com/subject/bqofbqtx.html