天天看點

Springboot應用-具有Security特性的RestTemplate

版權聲明:本文為CSDN部落客「coding-now」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

[原文連結](https://blog.csdn.net/feiyingwang/article/details/96424019)

1、定義需要加解密的Annotation

2、服務端實作--資料加解密

        1、證書生成

        2、服務端證書讀取配置

        3、服務端SecurityServerRestTemplate

3、用戶端實作-資料加解密

        1、用戶端證書讀取配置

        2、用戶端SecurityClientRestTemplate

        相關輔助類

        1 、用戶端資料加解密元件:

        2、服務端資料加解密元件

4、使用說明

        1 用戶端執行個體化相關元件

        2 定義需要向服務端加密傳輸的對象

        3 加密

        4 服務端執行個體化相關元件

        5 服務端解密用戶端加密的資料

 附加工具類

@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 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);
            }
        });
    }}      

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 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);
                }
            }
        }
    }
 }      

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...     
}      

加密

    @Autowiredprivate ClientDataResolver clientDataResolver;
    
    ....clientDataResolver.resolveSecurityFields(vo);....      

服務端執行個體化相關元件

@Configurationpublic 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();
    }
}      
    @Autowiredprivate ServerDataResolver dataResolver; 
	dataResolver.resolveSecurityFields(...)      
  • 附加工具類
        /**
	 * 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);
		}
	}      

繼續閱讀