• Springboot应用-具有Security特性的RestTemplate


    1、定义需要加解密的Annotation

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface EnableSecurity {
        /**
         *
         * @return
         */
        boolean ignored() default false;
    
        /**
         *
         * @return
         */
        boolean serverSide() default true;
    
        /**
         *
         * @return
         */
        Class target() default Object.class;
    
        //方法参数加密字段
        String[] encryptFields() default {};
    
        //解密方法返回值字段
        String[] decryptFields() default {};
    }
    

    2、服务端实现–数据加解密
    1、证书生成
    https://blog.csdn.net/iteye_7030/article/details/81965895
    2、服务端证书读取配置

    public class ServerSecurityConfig{
        private String password;
        private String alias;
        private String certificatePath;
        private String keyStorePath;
    
        @PostConstruct
        public void afterPropertiesSet() throws Exception{
            initCfg();
        }
    
        //@PostConstruct
        public void initCfg() {
            password = ContextConfig.get("aits.security.server.pwd", "passwd");
            alias = ContextConfig.get("aits.security.server.alias", "aabbcc.com");
            certificatePath = ContextConfig.get("aits.security.client.file", "/wls/envconfig/aits/server.cer");
            keyStorePath = ContextConfig.get("aits.security.server.file", "/wls/envconfig/aits/server.keystore");
        }
        //
        //get set ....
    }    
    

    3、服务端SecurityServerRestTemplate

    public class SecurityServerRestTemplate extends RestTemplate {
        @Autowired(required = false)
        private ServerSecurityConfig config;
        private static final Logger log = LoggerFactory.getLogger(SecurityServerRestTemplate.class);
        public SecurityServerRestTemplate() {
            super();
    
            this.getMessageConverters().add(new StringHttpMessageConverter(){
                @Override
                protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) 
                throws IOException {
                    String data =  super.readInternal(clazz, inputMessage);
                    try {
                        byte[] decrypt = CertificateCoder.decryptByPrivateKey(
                                CertificateCoder.decryptBASE64(data),
                                config.getKeyStorePath(), config.getAlias(), config.getPassword());
                        data = new String(decrypt);
                    }catch (Exception ex){
                        log.error("error-encode-data:{}",data,ex);
                    }
                    return data;
                }
    
                @Override
                protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
                    //服务端加密str
                    try {
                        byte[] encodedData = CertificateCoder.encryptByPrivateKey(str.getBytes(),
                                config.getKeyStorePath(), config.getAlias(), config.getPassword());
                        str = CertificateCoder.encryptBASE64(encodedData);
                    }catch (Exception ex){
                        log.error("error-encode-data:{}",str,ex);
                    }
                    super.writeInternal(str, outputMessage);
                }
            });
        }
    
    }
    

    3、客户端实现-数据加解密
    1、客户端证书读取配置

    public class ClientSecurityConfig {
    
        private String certificatePath;
    
        @PostConstruct
        public void afterPropertiesSet() throws Exception{
            initCfg();
        }
    
        public void initCfg() {
            certificatePath = ContextConfig.get("aits.security.client.file",
                    "/wls/envconfig/aits/server.cer");
        }
    
        public String getCertificatePath() {
            return certificatePath;
        }
    
        public void setCertificatePath(String certificatePath) {
            this.certificatePath = certificatePath;
        }
    
    }
    

    2、客户端SecurityClientRestTemplate

    /**
     * response 实体整个加密传输,读取后整体解密
     * note 传输过程中,必须base64加解密
     * @author WongBin
     * @date 2019/2/26
     */
    public class SecurityClientRestTemplate extends RestTemplate {
    
        private static final Logger log = LoggerFactory.getLogger(SecurityServerRestTemplate.class);
    
        public SecurityClientRestTemplate() {
            super();
            this.getMessageConverters().clear();
            this.getMessageConverters().add(0,new StringHttpMessageConverter(){
                @Override
                public String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
                    String data =  super.readInternal(clazz, inputMessage);
                    //客户端解密
                    try {
                        log.info("==========data-size:{}",data.length());
                        byte[] bts = CertificateCoder.decryptBASE64(data);
                        byte[] decrypt = CertificateCoder.decryptByPublicKey(bts,config.getCertificatePath());
                        data = new String(decrypt);
                        log.info("==========read-decrypted");
                    }catch (Exception ex){
                        log.error("error-decode-data:{}",data,ex);
                    }
                    return data;
                }
    
                @Override
                protected void writeInternal(String data, HttpOutputMessage outputMessage) throws IOException {
                    //客户端加密str
                    try {
                        String d = CertificateCoder.encryptBASE64(data.getBytes());
                        byte[] encodedData = CertificateCoder.encryptByPublicKey(d.getBytes(),config.getCertificatePath());
                        //str = new String(encodedData);
                        data = new String(encodedData);
                        log.info("==========to-write-encrypted");
                    }catch (Exception ex){
                        log.error("error-encode-data:{}","",ex);
                    }
                    super.writeInternal(data, outputMessage);
                }
            });
            //this.getMessageConverters().add(new StringHttpMessageConverter());
        }
    
        @Autowired(required = false)
        private ClientSecurityConfig config;
    }
    
    

    相关辅助类

    /**
     * RSA加密的数据,网络传输之前必须base64加密,本地获取后首先base64解密,再做后续解密操作
     *
     * @author WongBin
     * @date 2019/2/26
     */
    public abstract class Coder {
    
        public static String encryptBASE64(byte[] data){
            return new String(Base64Utils.encode(data));
        }
    
        public static byte[] decryptBASE64(String data){
            return Base64Utils.decode(data.getBytes());
        }
    }
    
    

    CertificateCoder实现参考以下文章:https://blog.csdn.net/iteye_7030/article/details/81965895
    1 、客户端数据加解密组件:

    /**
     * @author WongBin
     * @date 2019/2/27
     */
    //@Component 调用方负责实例化
    public class ClientDataResolver {
        private static final Logger log = LoggerFactory.getLogger(ClientDataResolver.class);
        @Autowired(required = false)
        private ClientSecurityConfig config;
    
        /***
         * 获取服务端数据后解密
         * @param serverData
         * @return
         */
        public String decode(String serverData){
            try {
                return new String(CertificateCoder.decryptByPublicKey(
                        CertificateCoder.decryptBASE64(serverData), config.getCertificatePath()));
            }catch (Exception ex){
                log.error("decode-server-data-error:{}",serverData,ex);
                return serverData;
            }
        }
    
        /**
         * 发送给服务端之前 加密
         * @param clientData
         * @return
         */
        public String encode(String clientData){
            try {
                return new String(CertificateCoder.encryptBASE64(
                        CertificateCoder.encryptByPublicKey(clientData.getBytes(),
                                config.getCertificatePath())));
            }catch (Exception ex){
                log.error("encode-client-data-error:{}",clientData,ex);
                return clientData;
            }
        }
    
        /***
         * 解密服务端返回的 加密对象
         *
         * @param data
         */
        public void resolveSecurityFields(Object data)throws Exception{
            if(data!=null && data.getClass().isAnnotationPresent(EnableSecurity.class)){
                EnableSecurity tag = data.getClass().getAnnotation(EnableSecurity.class);
                if (!tag.serverSide()) {
                    Class<?> resultClz = data.getClass();
                    Field[] fieldInfo = resultClz.getDeclaredFields();
                    try {
                        for (String f : tag.decryptFields()) {
                            for (Field field : fieldInfo) {
                                if (f.equals(field.getName())) {
                                    field.setAccessible(true);
                                    String t = (String)field.get(data);
                                    try {
                                        byte[] bts = CertificateCoder.decryptBASE64(t);
                                        byte[] temp = CertificateCoder.decryptByPublicKey(bts,config.getCertificatePath());
                                        field.set(data, new String(temp));
                                        log.info("decrypt-server-data-done:...{}", f);
                                    } catch (Exception ex) {
                                        //log.error("decrypt-server-data-error:{}", data, ex);
                                        throw ex;
                                    }
                                    break;
                                }
                            }
                        }
                    } catch (Exception ex) {
                        log.error("解密服务端数据出错:{}",data, ex);
                        throw ex;
                    }
                }
            }
        }
    }
    

    2、服务端数据加解密组件

    /**
     * @author WongBin
     * @date 2019/2/27
     */
    public class ServerDataResolver {
    
        private static final Logger log = LoggerFactory.getLogger(ServerDataResolver.class);
    
        @Autowired
        private SecurityServerRestTemplate template;
        @Autowired
        private ServerSecurityConfig config;
        /***
         * 获取客户端 数据后解密
         * @param data
         * @return
         */
        public String decode(String data){
            try {
                return new String(CertificateCoder.decryptByPrivateKey(
                        CertificateCoder.decryptBASE64(data),
                        config.getKeyStorePath(),config.getAlias(),config.getPassword()));
            }catch (Exception ex){
                log.error("decode-client-data-error:{}",data,ex);
                return data;
            }
        }
    
        /**
         * 发送给 客户端之前 加密
         * @param data
         * @return
         */
        public String encode(String data){
            try {
                return new String(CertificateCoder.encryptBASE64(
                        CertificateCoder.encryptByPrivateKey(data.getBytes(),
                        config.getKeyStorePath(),config.getAlias(),config.getPassword())));
            }catch (Exception ex){
                log.error("encode-server-data-error:{}",data,ex);
                return data;
            }
        }
    
        /***
         * 解密客户端返回的 加密对象
         * 
         * @param data
         */
        public void resolveSecurityFields(Object data){
            if(data!=null && data.getClass().isAnnotationPresent(EnableSecurity.class)){
                EnableSecurity tag = data.getClass().getAnnotation(EnableSecurity.class);
                if (!tag.serverSide()) {
                    Class<?> resultClz = data.getClass();
                    Field[] fieldInfo = resultClz.getDeclaredFields();
                    try {
                        for (String f : tag.decryptFields()) {
                            for (Field field : fieldInfo) {
                                if (f.equals(field.getName())) {
                                    field.setAccessible(true);
                                    String t = (String)field.get(data);
                                    try {
                                        byte[] bts = CertificateCoder.decryptBASE64(t);
                                        byte[] temp = CertificateCoder.decryptByPrivateKey(bts,
                                                config.getKeyStorePath(),config.getAlias(),config.getPassword());
                                        field.set(data, new String(temp));
                                        log.info("decrypt-client-data-done:...{}", f);
                                    } catch (Exception ex) {
                                        log.error("decrypt-client-data-error:{}", data, ex);
                                    }
                                    break;
                                }
                            }
                        }
                    } catch (Exception ex) {
                        log.error("解密客户端返回的数据出错:{}",data, ex);
                    }
                }
            }
        }
    }
    

    4、使用说明
    1 客户端实例化相关组件

    
        @Bean
        @Lazy
        public ClientSecurityConfig clientSecurityConfig(){
            return new ClientSecurityConfig();
        }
    
        @Bean
        //@DependsOn({"clientSecurityConfig"})
        @Lazy
        public ClientDataResolver clientDataResolver(){
            return new ClientDataResolver();
        }
        @Bean
        //@DependsOn({"clientSecurityConfig"})
        @Lazy
        public SecurityClientRestTemplate securityClientRestTemplate(){
            return new SecurityClientRestTemplate();
        }
    
        @Lazy
        @Primary
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    

    定义需要向服务端加密传输的对象

    @EnableSecurity(serverSide = false,decryptFields = {"srcDbIp","srcDbPort","srcDbUsername","srcDbPasswd","srcDbname"})
    public class DbConfigVO {
        private String dbType;
        private String dbFile;
        private String projectCode;
        private String srcDbIp;
        // get set toString...     
    }    
    

    加密

        @Autowired
        private ClientDataResolver clientDataResolver;
        ....
        clientDataResolver.resolveSecurityFields(vo);
        ....
    

    服务端实例化相关组件

    @Configuration
    public class SecurityConfig {
    
        /*数据加密相关组件*/
        @Bean
        //@DependsOn({"serverSecurityConfig"})
        @Lazy
        public SecurityServerRestTemplate securityTemplate(){
            return new SecurityServerRestTemplate();
        }
        @Bean
        @Primary
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    
        @Bean
        @Lazy
        public ServerDataResolver resolver(){
            return new ServerDataResolver();
        }
    
        @Lazy
        @Bean
        public ServerSecurityConfig serverSecurityConfig(){
            return new ServerSecurityConfig();
        }
    }
    

    服务端解密客户端加密的数据

        @Autowired
        private ServerDataResolver dataResolver; 
    	dataResolver.resolveSecurityFields(...)
    

    当然,服务端加密数据给客户端,可以定义Aspect统一处理EnableSecurity标记的类,目前已实现内部项目,不便于公开,有需要留言沟通。
    服务端加密传输,客户端解密
    客户端加密传输,服务端解密
    最终双向加密传输都可以实现了,有类似需求的可以参考实现之。

    • 附加工具类
    /**
    	 * String转公钥PublicKey
    	 * @param key
    	 * @return
    	 * @throws Exception
    	 */
    	public static PublicKey getPublicKey(String key){
    		byte[] keyBytes;
    		try {
    			keyBytes = (new BASE64Decoder()).decodeBuffer(key);
    			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
    			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    			PublicKey publicKey = keyFactory.generatePublic(keySpec);
    			return publicKey;
    		}catch (Exception ex){
    			throw new RuntimeException("getPublicKey",ex);
    		}
    	}
    
    	/**
    	 * String转私钥PrivateKey
    	 * @param key
    	 * @return
    	 * @throws Exception
    	 */
    	public static PrivateKey getPrivateKey(String key){
    		byte[] keyBytes;
    		try {
    			keyBytes = (new BASE64Decoder()).decodeBuffer(key);
    			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
    			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    			PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
    			return privateKey;
    		}catch (Exception ex){
    			throw new RuntimeException("getPrivateKey-error",ex);
    		}
    	}
    
  • 相关阅读:
    物质的物理属性·基础整理
    点分治
    洛谷 P3806 【模板】点分治1
    因式分解
    小石潭记
    反演原理及二项式反演
    《庄子》二则
    FFT快速傅里叶变换
    mysql 密码相关
    django基础
  • 原文地址:https://www.cnblogs.com/coding-now/p/14660596.html
Copyright © 2020-2023  润新知