Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 475|回复: 0

Spring Cloud 自定义ConfigServer 解决敏感信息存储问题

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-9-4 14:21:04 | 显示全部楼层 |阅读模式

    公司需要将系统配置信息中的敏感信息独立存放。

    现有系统采用Spring Cloud Config提供配置信息,其中敏感信息主要是Db配置,分解本次需求:

    (1)数据库配置信息分离(主要是Db信息)。

    (2)原有Config Server功能继续可用。

    (3)对特定环境(这里是仿真环境-Demo、生产环境)可以定制配置信息。

     

    思路有如下几种:

    (1)Spring Aop 拦截Config Server中配置返回方法,改写方法返回值。

    (2)Spring Aop 拦截Config Server中读取配置方法,读取更多文件源。

    (3)Filter拦截Config Server中数据返回方法,改写方法返回值。

    其中:

    方法1与方法3都是对返回结果进行处理,可以兼容Spring Cloud Config的版本升级,因方法1需要绑定到特定方法,而方法3无需考虑具体方法,耦合性更低。

    方法2需要改写Config Server的代码,或覆盖Config Server的Bean,较1和2复杂,且无法随这主版本升级,直接pass。

     

    综合考虑采用方法3,以下是处理与代码:

    公司统一标准,将Db配置信息存储到Json文件中,因此需要一个解析Json文件的Service:

    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.core.env.MapPropertySource;
    import org.springframework.stereotype.Component;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.Date;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    @Component
    public class JsonSourcePropertiesLoader {
        @Value("${Config.Json.FileLocation}")
        String FileLocation;
    
        public MapPropertySource load() throws IOException {
            File jsonFile = new File(FileLocation);
            final Map<String, Object> source = process(jsonFile);
            System.out.println(new Date().getTime());
            return new MapPropertySource("dbConfigMap",source);
        }
    
        private Map<String, Object> process(final File resourceFile){
            Map<String, Object> map = null;
            try{
                map = new ObjectMapper().readValue(resourceFile, LinkedHashMap.class);
            }catch (IOException e){
                e.printStackTrace();
            }
            return map;
        }
    }

    需要一个类拦截并获取Config Server在正常处理请求后返回的内容,以便接下来的改写:

    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    public class MyResponseWrapper extends HttpServletResponseWrapper {
        private ByteArrayOutputStream byteArrayOutputStream;
        private ServletOutputStream servletOutputStream;
        private PrintWriter printWriter;
    
        public MyResponseWrapper(HttpServletResponse response) {
            super(response);
            byteArrayOutputStream = new ByteArrayOutputStream(4096);
            printWriter = new PrintWriter(byteArrayOutputStream);
            servletOutputStream = new MyServletOutputStream(byteArrayOutputStream);
        }
    
        @Override
        public PrintWriter getWriter() throws IOException{
            return printWriter;
        }
    
        @Override
        public ServletOutputStream getOutputStream() throws IOException{
            return servletOutputStream;
        }
    
        @Override
        public void flushBuffer() throws IOException{
            if(printWriter!=null){
                printWriter.flush();
            }
            if(servletOutputStream !=null){
                servletOutputStream.flush();
            }
        }
    
        @Override
        public void reset(){
            byteArrayOutputStream.reset();
        }
    
        public byte[] getResult(){
            try{
                flushBuffer();
            }catch (IOException e){
    
            }
            return byteArrayOutputStream.toByteArray();
        }
    }

    对ServletOutputStream的封装类:

    import javax.servlet.ServletOutputStream;
    import javax.servlet.WriteListener;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    
    public class MyServletOutputStream extends ServletOutputStream {
        ByteArrayOutputStream output;
    
        public MyServletOutputStream(ByteArrayOutputStream output) {
            this.output = output;
        }
    
        @Override
        public void write(int b) throws IOException {
            output.write(b);
        }
    
        public void write(byte[] data, int offset, int length) {
            output.write(data, offset, length);
        }
    
        @Override
        public boolean isReady() {
            return false;
        }
    
        @Override
        public void setWriteListener(WriteListener listener) {
    
        }
    }

    有了拦截与处理的方法,最后就是把功能组合到拦截器中:

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    import com.fang.microservice.cloud.Loader.JsonSourcePropertiesLoader;
    import com.fang.microservice.cloud.Wrapper.MyResponseWrapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    import org.springframework.core.env.MapPropertySource;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    
    @Component
    @ServletComponentScan
    @WebFilter(urlPatterns = "/{name}/{profiles:.*[^-].*}", filterName = "ConfigServletFilter")
    public class ConfigAspectFilter implements Filter {
        @Autowired
        JsonSourcePropertiesLoader jsonConfig;
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            MyResponseWrapper wrapper = new MyResponseWrapper((HttpServletResponse) response);
            chain.doFilter(request, wrapper);
            byte[] respBytes = wrapper.getResult();
            String result = Byte2String(respBytes);
            if (response.getContentType().toLowerCase().contains("application/json")) {
                if (!result.toLowerCase().contains("datasource.mysql.")) {
                    JSONObject configObj = (JSONObject) JSON.parse(result);
                    JSONArray propertySourcesObjs = configObj.getJSONArray("propertySources");
                    if (propertySourcesObjs.size() > 0) {
                        JSONObject propertySourcesObj = (JSONObject) propertySourcesObjs.get(0);
                        JSONObject sourceObj = propertySourcesObj.getJSONObject("source");
                        MapPropertySource mps = jsonConfig.load();
                        for (String key : mps.getPropertyNames()) {
                            sourceObj.put(key, mps.getProperty(key));
                        }
                        result = JSON.toJSONString(configObj);
                    }
                }
            }
    
            response.setContentLength(-1);
            PrintWriter out = response.getWriter();
            out.write(result);
            out.flush();
            out.close();
        }
    
        public static String Byte2String(byte[] buff) {
            ByteArrayOutputStream result = new ByteArrayOutputStream();
            int length;
            try {
                    result.write(buff, 0, buff.length);
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                return result.toString("UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return "";
        }
    
        @Override
        public void destroy() {
    
        }
    }

    作为敏感数据存储的文件内容:

    { "testDbName":"testDb","userName":"userName","password":"pass"}

    测试效果如下:

    最后不能忘记特定敏感文件的路径,需要配置在配置文件中:

    Config.Json.FileLocation=/xx/test/test.json

    这样,一个既可以规避敏感文件,又能不失Config Server方便性的简单处理方案就这样可以投入使用了。

     
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-30 18:08 , Processed in 0.068537 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表