首页 文章 精选 留言 我的

精选列表

搜索[玲珑应用],共10000篇文章
优秀的个人博客,低调大师

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

优秀的个人博客,低调大师

Go 1.16 embed特性的简单应用

项目结构如下: └─ui └─embed_ui.go └─dist └─index.html └─static ├─css └─ ... ├─fonts └─ ... └─js └─ ... └─main.go └─go.mod //embed_ui.go package ui import ( `embed` ) //go:embed dist var WebUI embed.FS //main.go // 嵌入普通静态资源 type StaticResource struct { // 静态资源 staticFS embed.FS // 设置embed文件到静态资源的相对路径,也就是embed注释里的路径 path string } // 静态资源被访问逻辑 func (_this_ *StaticResource) Open(name string) (fs.File, error) { var fullName string if strings.Contains(name,`/`){ fullName = path.Join(_this_.path,"static",name) }else{ fullName = path.Join(_this_.path,name) } file, err := _this_.staticFS.Open(fullName) return file, err } func main() { // 设置静态资源 static := &StaticResource{ staticFS: ui.WebUI, path: "dist", } engine := gin.Default() { // 设置 engine.StaticFS("/static/",http.FS(static)) // 首页 engine.GET("/", func(context *gin.Context) { context.Writer.WriteHeader(http.StatusOK) indexHTML,_ := static.staticFS.ReadFile(static.path + "/" + "index.html") context.Writer.Write(indexHTML) context.Writer.Header().Add("Accept","text/html") context.Writer.Flush() }) } engine.Run() } [GIN-debug] GET /static/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers) [GIN-debug] HEAD /static/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers) [GIN-debug] GET / --> main.main.func1 (3 handlers) [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default [GIN-debug] Listening and serving HTTP on :8080 [GIN] 2021/02/17 - 17:37:07 | 200 | 530.8µs | ::1 | GET "/" [GIN] 2021/02/17 - 17:37:07 | 200 | 135.1147ms | ::1 | GET "/static/js/chunk-2d0b6337.40e74af5.js" [GIN] 2021/02/17 - 17:37:07 | 200 | 134.5926ms | ::1 | GET "/static/css/chunk-3d4a32e8.4951a1b7.css" [GIN] 2021/02/17 - 17:37:07 | 200 | 146.7519ms | ::1 | GET "/static/css/index.d72cf005.css" [GIN] 2021/02/17 - 17:37:07 | 200 | 147.2865ms | ::1 | GET "/static/js/index.15d7bf17.js" [GIN] 2021/02/17 - 17:37:07 | 200 | 151.6588ms | ::1 | GET "/static/css/chunk-vendors.16da611a.css" [GIN] 2021/02/17 - 17:37:07 | 200 | 148.8884ms | ::1 | GET "/static/js/chunk-vendors.24c0b194.js" [GIN] 2021/02/17 - 17:37:07 | 200 | 0s | ::1 | GET "/static/js/chunk-2d0d69a3.6eb93f6e.js" [GIN] 2021/02/17 - 17:37:07 | 200 | 364.8µs | ::1 | GET "/static/js/chunk-2d0e53c4.94fb2765.js" [GIN] 2021/02/17 - 17:37:07 | 200 | 382.2µs | ::1 | GET "/static/js/chunk-3d4a32e8.ced07e34.js" [GIN] 2021/02/17 - 17:37:07 | 200 | 0s | ::1 | GET "/static/fonts/element-icons.535877f5.woff" 不太习惯写长篇大论的文章,看得不是太明白的可以参考下面的链接,研究这个也是了解下用法,如果有更好的写法欢迎留言,共同讨论 参考: https://blog.csdn.net/RA681t58CJxsgCkJ31/article/details/113777516

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册