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 服务端解密客户端加密的数据 附加工具类 1、定义需要加解密的Annotation @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public@interfaceEnableSecurity{ /** * *@return */ booleanignored()defaultfalse; /** * *@return */ booleanserverSide()defaulttrue; /** * *@return */ Classtarget()defaultObject.class; //方法参数加密字段 String[]encryptFields()default{}; //解密方法返回值字段 String[]decryptFields()default{}; } 2、服务端实现–数据加解密 1、证书生成 https://blog.csdn.net/iteye_7030/article/details/81965895 2、服务端证书读取配置 publicclassServerSecurityConfig{ privateStringpassword; privateStringalias; privateStringcertificatePath; privateStringkeyStorePath; @PostConstruct publicvoidafterPropertiesSet()throwsException{ initCfg(); } //@PostConstruct publicvoidinitCfg(){ 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"); } // //getset.... } 3、服务端SecurityServerRestTemplate publicclassSecurityServerRestTemplateextendsRestTemplate{ @Autowired(required=false) privateServerSecurityConfigconfig; privatestaticfinalLoggerlog=LoggerFactory.getLogger(SecurityServerRestTemplate.class); publicSecurityServerRestTemplate(){ super(); this.getMessageConverters().add(newStringHttpMessageConverter(){ @Override protectedStringreadInternal(Class<?extendsString>clazz,HttpInputMessageinputMessage) throwsIOException{ Stringdata=super.readInternal(clazz,inputMessage); try{ byte[]decrypt=CertificateCoder.decryptByPrivateKey( CertificateCoder.decryptBASE64(data), config.getKeyStorePath(),config.getAlias(),config.getPassword()); data=newString(decrypt); }catch(Exceptionex){ log.error("error-encode-data:{}",data,ex); } returndata; } @Override protectedvoidwriteInternal(Stringstr,HttpOutputMessageoutputMessage)throwsIOException{ //服务端加密str try{ byte[]encodedData=CertificateCoder.encryptByPrivateKey(str.getBytes(), config.getKeyStorePath(),config.getAlias(),config.getPassword()); str=CertificateCoder.encryptBASE64(encodedData); }catch(Exceptionex){ log.error("error-encode-data:{}",str,ex); } super.writeInternal(str,outputMessage); } }); }} 3、客户端实现-数据加解密 1、客户端证书读取配置 publicclassClientSecurityConfig{ privateStringcertificatePath; @PostConstruct publicvoidafterPropertiesSet()throwsException{ initCfg(); } publicvoidinitCfg(){ certificatePath=ContextConfig.get("aits.security.client.file", "/wls/envconfig/aits/server.cer"); } publicStringgetCertificatePath(){ returncertificatePath; } publicvoidsetCertificatePath(StringcertificatePath){ this.certificatePath=certificatePath; } } 2、客户端SecurityClientRestTemplate /** *response实体整个加密传输,读取后整体解密 *note传输过程中,必须base64加解密 *@authorWongBin *@date2019/2/26 */publicclassSecurityClientRestTemplateextendsRestTemplate{ privatestaticfinalLoggerlog=LoggerFactory.getLogger(SecurityServerRestTemplate.class); publicSecurityClientRestTemplate(){ super(); this.getMessageConverters().clear(); this.getMessageConverters().add(0,newStringHttpMessageConverter(){ @Override publicStringreadInternal(Class<?extendsString>clazz,HttpInputMessageinputMessage)throwsIOException{ Stringdata=super.readInternal(clazz,inputMessage); //客户端解密 try{ log.info("==========data-size:{}",data.length()); byte[]bts=CertificateCoder.decryptBASE64(data); byte[]decrypt=CertificateCoder.decryptByPublicKey(bts,config.getCertificatePath()); data=newString(decrypt); log.info("==========read-decrypted"); }catch(Exceptionex){ log.error("error-decode-data:{}",data,ex); } returndata; } @Override protectedvoidwriteInternal(Stringdata,HttpOutputMessageoutputMessage)throwsIOException{ //客户端加密str try{ Stringd=CertificateCoder.encryptBASE64(data.getBytes()); byte[]encodedData=CertificateCoder.encryptByPublicKey(d.getBytes(),config.getCertificatePath()); //str=newString(encodedData); data=newString(encodedData); log.info("==========to-write-encrypted"); }catch(Exceptionex){ log.error("error-encode-data:{}","",ex); } super.writeInternal(data,outputMessage); } }); //this.getMessageConverters().add(newStringHttpMessageConverter()); } @Autowired(required=false) privateClientSecurityConfigconfig; } 相关辅助类 /** *RSA加密的数据,网络传输之前必须base64加密,本地获取后首先base64解密,再做后续解密操作 * *@authorWongBin *@date2019/2/26 */publicabstractclassCoder{ publicstaticStringencryptBASE64(byte[]data){ returnnewString(Base64Utils.encode(data)); } publicstaticbyte[]decryptBASE64(Stringdata){ returnBase64Utils.decode(data.getBytes()); } } CertificateCoder实现参考以下文章:https://blog.csdn.net/iteye_7030/article/details/819658951 、客户端数据加解密组件: /** *@authorWongBin *@date2019/2/27 *///@Component调用方负责实例化 publicclassClientDataResolver{ privatestaticfinalLoggerlog=LoggerFactory.getLogger(ClientDataResolver.class); @Autowired(required=false) privateClientSecurityConfigconfig; /*** *获取服务端数据后解密 *@paramserverData *@return */ publicStringdecode(StringserverData){ try{ returnnewString(CertificateCoder.decryptByPublicKey( CertificateCoder.decryptBASE64(serverData),config.getCertificatePath())); }catch(Exceptionex){ log.error("decode-server-data-error:{}",serverData,ex); returnserverData; } } /** *发送给服务端之前加密 *@paramclientData *@return */ publicStringencode(StringclientData){ try{ returnnewString(CertificateCoder.encryptBASE64( CertificateCoder.encryptByPublicKey(clientData.getBytes(), config.getCertificatePath()))); }catch(Exceptionex){ log.error("encode-client-data-error:{}",clientData,ex); returnclientData; } } /*** *解密服务端返回的加密对象 * *@paramdata */ publicvoidresolveSecurityFields(Objectdata)throwsException{ if(data!=null&&data.getClass().isAnnotationPresent(EnableSecurity.class)){ EnableSecuritytag=data.getClass().getAnnotation(EnableSecurity.class); if(!tag.serverSide()){ Class<?>resultClz=data.getClass(); Field[]fieldInfo=resultClz.getDeclaredFields(); try{ for(Stringf:tag.decryptFields()){ for(Fieldfield:fieldInfo){ if(f.equals(field.getName())){ field.setAccessible(true); Stringt=(String)field.get(data); try{ byte[]bts=CertificateCoder.decryptBASE64(t); byte[]temp=CertificateCoder.decryptByPublicKey(bts,config.getCertificatePath()); field.set(data,newString(temp)); log.info("decrypt-server-data-done:...{}",f); }catch(Exceptionex){ //log.error("decrypt-server-data-error:{}",data,ex); throwex; } break; } } } }catch(Exceptionex){ log.error("解密服务端数据出错:{}",data,ex); throwex; } } } } } 2、服务端数据加解密组件 /** *@authorWongBin *@date2019/2/27 */ publicclassServerDataResolver{ privatestaticfinalLoggerlog=LoggerFactory.getLogger(ServerDataResolver.class); @Autowired privateSecurityServerRestTemplatetemplate; @Autowired privateServerSecurityConfigconfig; /*** *获取客户端数据后解密 *@paramdata *@return */ publicStringdecode(Stringdata){ try{ returnnewString(CertificateCoder.decryptByPrivateKey( CertificateCoder.decryptBASE64(data), config.getKeyStorePath(),config.getAlias(),config.getPassword())); }catch(Exceptionex){ log.error("decode-client-data-error:{}",data,ex); returndata; } } /** *发送给客户端之前加密 *@paramdata *@return */ publicStringencode(Stringdata){ try{ returnnewString(CertificateCoder.encryptBASE64( CertificateCoder.encryptByPrivateKey(data.getBytes(), config.getKeyStorePath(),config.getAlias(),config.getPassword()))); }catch(Exceptionex){ log.error("encode-server-data-error:{}",data,ex); returndata; } } /*** *解密客户端返回的加密对象 * *@paramdata */ publicvoidresolveSecurityFields(Objectdata){ if(data!=null&&data.getClass().isAnnotationPresent(EnableSecurity.class)){ EnableSecuritytag=data.getClass().getAnnotation(EnableSecurity.class); if(!tag.serverSide()){ Class<?>resultClz=data.getClass(); Field[]fieldInfo=resultClz.getDeclaredFields(); try{ for(Stringf:tag.decryptFields()){ for(Fieldfield:fieldInfo){ if(f.equals(field.getName())){ field.setAccessible(true); Stringt=(String)field.get(data); try{ byte[]bts=CertificateCoder.decryptBASE64(t); byte[]temp=CertificateCoder.decryptByPrivateKey(bts, config.getKeyStorePath(),config.getAlias(),config.getPassword()); field.set(data,newString(temp)); log.info("decrypt-client-data-done:...{}",f); }catch(Exceptionex){ log.error("decrypt-client-data-error:{}",data,ex); } break; } } } }catch(Exceptionex){ log.error("解密客户端返回的数据出错:{}",data,ex); } } } } } 4、使用说明 1 客户端实例化相关组件 @Bean @Lazy publicClientSecurityConfigclientSecurityConfig(){ returnnewClientSecurityConfig(); } @Bean //@DependsOn({"clientSecurityConfig"}) @Lazy publicClientDataResolverclientDataResolver(){ returnnewClientDataResolver(); } @Bean //@DependsOn({"clientSecurityConfig"}) @Lazy publicSecurityClientRestTemplatesecurityClientRestTemplate(){ returnnewSecurityClientRestTemplate(); } @Lazy @Primary @Bean publicRestTemplaterestTemplate(){ returnnewRestTemplate(); } 定义需要向服务端加密传输的对象 @EnableSecurity(serverSide=false,decryptFields={"srcDbIp","srcDbPort","srcDbUsername","srcDbPasswd","srcDbname"}) publicclassDbConfigVO{ privateStringdbType; privateStringdbFile; privateStringprojectCode; privateStringsrcDbIp; //getsettoString... } 加密 @AutowiredprivateClientDataResolverclientDataResolver; ....clientDataResolver.resolveSecurityFields(vo);.... 服务端实例化相关组件 @ConfigurationpublicclassSecurityConfig{ /*数据加密相关组件*/ @Bean //@DependsOn({"serverSecurityConfig"}) @Lazy publicSecurityServerRestTemplatesecurityTemplate(){ returnnewSecurityServerRestTemplate(); } @Bean @Primary publicRestTemplaterestTemplate(){ returnnewRestTemplate(); } @Bean @Lazy publicServerDataResolverresolver(){ returnnewServerDataResolver(); } @Lazy @Bean publicServerSecurityConfigserverSecurityConfig(){ returnnewServerSecurityConfig(); } } 服务端解密客户端加密的数据 @AutowiredprivateServerDataResolverdataResolver; dataResolver.resolveSecurityFields(...) 当然,服务端加密数据给客户端,可以定义Aspect统一处理EnableSecurity标记的类,目前已实现内部项目,有需要留言沟通。 服务端加密传输,客户端解密 客户端加密传输,服务端解密 最终双向加密传输都可以实现了,有类似需求的可以参考实现之。 附加工具类 /** *String转公钥PublicKey *@paramkey *@return *@throwsException */ publicstaticPublicKeygetPublicKey(Stringkey){ byte[]keyBytes; try{ keyBytes=(newBASE64Decoder()).decodeBuffer(key); X509EncodedKeySpeckeySpec=newX509EncodedKeySpec(keyBytes); KeyFactorykeyFactory=KeyFactory.getInstance("RSA"); PublicKeypublicKey=keyFactory.generatePublic(keySpec); returnpublicKey; }catch(Exceptionex){ thrownewRuntimeException("getPublicKey",ex); } } /** *String转私钥PrivateKey *@paramkey *@return *@throwsException */ publicstaticPrivateKeygetPrivateKey(Stringkey){ byte[]keyBytes; try{ keyBytes=(newBASE64Decoder()).decodeBuffer(key); PKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(keyBytes); KeyFactorykeyFactory=KeyFactory.getInstance("RSA"); PrivateKeyprivateKey=keyFactory.generatePrivate(keySpec); returnprivateKey; }catch(Exceptionex){ thrownewRuntimeException("getPrivateKey-error",ex); } }