当前位置: 首页 > 科技观察

springboot数据安全传输加解密

时间:2023-03-13 17:18:48 科技观察

环境:springboot2.2.6.RELEASE,Vue+axios通过继承RequestBodyAdviceAdapter实现对请求内容的解密操作,并实现ResponseBodyAdvice对相应内容进行加密。定义加解密接口:SecretProcess.javapublicinterfaceSecretProcess{/***

数据加密

*

时间:2020年12月24日-12:22:13pm

*@authorxg*@paramdata待加密数据*@returnString加密结果*/Stringencrypt(Stringdata);/***

数据解密

*

时间:2020年12月24日-12:23:20pm

*@authorxg*@paramdata待解密数据*@returnString解密数据*/Stringdecrypt(Stringdata);/***

加密算法格式:algorithm[/mode/fill]

*

时间:2020年12月24日-下午12:32:49

*@authorxg*@returnString*/StringgetAlgorithm();publicstaticclassHex{privatestaticfinalchar[]HEX={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};publicstaticbyte[]decode(CharSequences){intnChars=s.length();if(nChars%2!=0){thrownewIllegalArgumentException("十六进制数据错误");}byte[]result=newbyte[nChars/2];for(inti=0;i>>4]).append(HEX[buf[i]&0x0F]);}returnsb.toString();}}}该接口定义了加密和解密两个方法,Hex类用于数据处理十六进制转换定义一个抽象类来实现上面的接口。具体加解密实现细节在抽象类中。密码.DECRYPT_MODE,keySpec());byte[]decryptBytes=cipher.doFinal(Hex.decode(data));returnnewString(decryptBytes);}catch(Exceptione){thrownewRuntimeException(e);}}@OverridepublicStringencrypt(Stringdata){try{Ciphercipher=Cipher.getInstance(getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE,keySpec());returnHex.encode(cipher.doFinal(data.getBytes(Charset.forName("UTF-8"))));}catch(Exceptione){thrownewRuntimeException(e);}}/***

根据key生成不同的密钥材料

*

目前支持:AES,DES

*

时间:2020年12月25日-下午1:02:54

*@authorxg*@paramsecretKeyKey*@paramalgorithmAlgorithm*@returnKey*/publicKeygetKeySpec(Stringalgorithm){if(algorithm==null||algorithm.trim().length()==0){返回ull;}StringsecretKey=props.getKey();switch(algorithm.toUpperCase()){case"AES":returnnewSecretKeySpec(secretKey.getBytes(),"AES");case"DES":Keykey=null;try{DESKeySpecdesKeySpec=newDESKeySpec(secretKey.getBytes());SecretKeyFactorysecretKeyFactory=SecretKeyFactory.getInstance("DES");key=secretKeyFactory.generateSecret(desKeySpec);}catch(Exceptione){thrownewRuntimeException(e);}returnkey;default:returnull;}}/***

生成密钥材料

*

时间:2020-12-25-11:35:03

*@authorxg*@returnKey密钥材料*/publicabstractKeykeySpec();}该抽象类为2中的对称加密提供密钥恢复。分表是AES和DES算法的抽象方法。抽象方法keySpec需要通过子类实现(具体使用哪个对称加密算法)。具体加密算法AESAlgorithm.javapublicclassAESAlgorithmextendsAbstractSecretProcess{@OverridepublicStringgetAlgorithm(){return"AES/ECB/PKCS5Padding";}@OverridepublicKeykeySpec(){returnthis.getKeySpec("AES");}}SecretProperties.java属性配置类@的实现类配置@Bean@ConditionalOnMissingBean(SecretProcess.class)publicSecretProcesssecretProcess(){returnnewAESAlgorithm();}@Component@ConfigurationProperties(prefix="secret")publicstaticclassSecretProperties{privateBooleanenabled;privateStringkey;publicBooleangetEnabled(){returnenabled;}publicvoidsetEnabled(Booleanenabled){this.enabled=enabled;}publicStringgetKey(){returnkey;}publicvoidsetKey(Stringkey){this.key=key;}}}配置文件配置如下:secret:key:aaaabbbbbccccdddd#keyenabled:true#是否开启加密和项目中的解密函数数据中的所有方法不一定都可以加解密,所以定义一个注解接下来,只能使用Controller类或者带有注解的具体接口方法对数据进行加解密,如下:运行时)@映射@Documentedpublic@interfaceSIProtection{}对请求内容进行解密出来,通过RequestBodyAdviceDecryptRequestBodyAdivce.java@ControllerAdvice@ConditionalOnProperty(name="secret.enabled",havingValue="true")publicclassDecryptRequestBodyAdivceextendsRequestBodyAdviceAdapter{@ResourceprivateSecretProcesssecretProcess;@Overridepublicbooleansupports(MethodParametermethodParameter,TypetargetType,Class>converterType){returnmethodParameter.getMethod().isAnnotationPresent(SIProtection.class)||methodParameter.getMethod().getDeclaringClass().isAnnotationPresent(SIProtection.class);}@OverridepublicHttpInputMessagebeforeBodyRead(HttpInputMessageinputMessage,MethodParameterparameter,TypetargetType,类>converterType)throwsIOException{Stringbody=secretProcess.decrypt(inToString(inputMessage.getBody()));returnnewHttpInputMessage(){@OverridepublicHttpHeadersgetHeaders(){returninputMessage.getHeaders();}@OverridepublicInputStreamgetBody()throwsIOException{returnnewByteArrayInputStream(body.getBytes());}};}privateStringinToString(InputStreamis){byte[]buf=newbyte[10*1024];intleng=-1;StringBuildersb=newStringBuilder();try{while((leng=is.read(buf))!=-1){sb.append(newString(buf,0,leng));}returnsb.toString();}catch(IOExceptione){thrownewRuntimeException(e);}}}这里注意:@ConditionalOnProperty(name="secret.enabled",havingValue="true")注解只有开启加解密功能才会生效注意这里的supports方法对响应内容加密出来EncryptResponseBodyAdivce.java@ControllerAdvice@ConditionalOnProperty(name="secret.enabled",havingValue="true")publicclassEncryptResponseBodyAdivceimplementsResponseBodyAdvice{@ResourceprivateSecretProcesssecretProcess;@Overridepublicbooleansupports(MethodParameterreturnType,Class>converterType){returnreturnType.getMethod().isAnnotationPresent(SIProtection.class)||returnType.getMethod().getDeclaringClass().isAnnotationPresent(SIProtection.class);}@OverridepublicObjectbeforeBodyWrite(Objectbody,MethodParameterreturnType,MediaTypeselectedContentType,Class>selectedConverterType,ServerHttpRequestrequest,ServerHttpResponseresponse){if(body==null){returnbody;}try{StringjsonStr=newObjectMapper().writeValueAsString(body);returnsecretProcess.encrypt(jsonStr);}catch(异常){thrownewRuntimeException(e);}}}续Rollerapplication@PostMapping("/save")@SIProtectionpublicRsave(@RequestBodyUsersusers){returnR.success(usersService.save(users));}//加解密具体方法@RestController@RequestMapping("/users")@SIProtectionpublicclassUsersController{//加密解密本Controller中的所有方法}前端引入第三方插件:crypto-js工具方法加密解密:/***加密方法*@paramdata待加密数据*@returns{string|*}*/encrypt(data){letkey=CryptoJS.enc.Utf8.parse(Consts.Secret.key)if(typeofdata==='object'){data=JSON.stringify(data)}letplainText=CryptoJS。enc.Utf8.parse(data)letsecretText=CryptoJS.AES.encrypt(plainText,key,{mode:CryptoJS.mode.ECB,padding:CryptoJS.pad.Pkcs7}).ciphertext.toString()returnsecretText},/***解密数据*@paramdata要解密的数据*/decrypt(data){letkey=CryptoJS.enc.Utf8.parse(Consts.Secret.key)letsecretText=CryptoJS.enc.Hex.parse(data)letencryptedBase64Str=CryptoJS.enc.Base64.stringify(secretText)字母结果=CryptoJS.AES.decrypt(encryptedBase64Str,key,{mode:CryptoJS.mode.ECB,padding:CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8)returnJSON.parse(result)}配置:letConsts={Secret:{key:'aaaabbbbbccccdddd',//必须是16位(前后端必须相同,key)urls:['/users/save']}}exportdefaultConsts这里的urls表示拦截那些请求(加解密),这里也可以配置"*"表示所有请求axios对请求前和响应后的数据进行加解密:发送请求前:axios.interceptors.request.use((config)=>{leturi=config.urlif(uri.includes('?')){uri=uri.substring(0,uri.indexOf('?'))}if(window.cfg.enableSecret==='1'&&config.data&&(Consts.Secret.urls.indexOf('*')>-1||Consts.Secret.urls.indexOf(uri)>-1)){letdata=config.dataletsecretText=Utils.Secret.encrypt(data)config.data=secretText}returnconfig},(error)=>{leterrorMessage='请求失败'store.dispatch(types.G_SHOW_ALERT,{title:'requestfailed',content:errorMessage,showDetail:false,detailContent:String(error)})returnPromise.reject(error)})axios.interceptors.response.use((响应)=>{leturi=response.config.urlif(uri.includes('?')){uri=uri.substring(0,uri.indexOf('?'))}if(window.cfg.enableSecret==='1'&&response.data&&(Consts.Secret.urls.indexOf('*')>-1||Consts.Secret.urls.indexOf(uri)>-1)){letdata=Utils.Secret.decrypt(response.data)if(data){response.data=data}}returnresponse},(error)=>{console.error(`testinterceptors.responseisin,${error}`)returnPromise.reject(error)})这里的window.cfg.enableSecret配置是我自己项目中是否有配置文件配置。这个可以根据自己的环境配置实现测试:这里可以看到前端发起的请求内容已经加密响应内容:完成!!!

最新推荐
猜你喜欢