自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Springboot 數(shù)據(jù)安全傳輸加密與解密

開發(fā) 前端
通過繼承RequestBodyAdviceAdapter實現(xiàn)對于請求的內(nèi)容進行解密操作,實現(xiàn)ResponseBodyAdvice來對相應(yīng)內(nèi)容進行加密處理。

[[374394]]

 環(huán)境:springboot2.2.6.RELEASE、Vue+axios

通過繼承RequestBodyAdviceAdapter實現(xiàn)對于請求的內(nèi)容進行解密操作,實現(xiàn)ResponseBodyAdvice來對相應(yīng)內(nèi)容進行加密處理。

定義加密解密的接口:

SecretProcess.java

  1. public interface SecretProcess { 
  2.      
  3.     /** 
  4.      *  <p>數(shù)據(jù)加密</p> 
  5.      *  <p>時間:2020年12月24日-下午12:22:13</p> 
  6.      * @author xg 
  7.      * @param data 待加密數(shù)據(jù) 
  8.      * @return String 加密結(jié)果 
  9.      */ 
  10.     String encrypt(String data) ; 
  11.      
  12.     /** 
  13.      *  <p>數(shù)據(jù)解密</p> 
  14.      *  <p>時間:2020年12月24日-下午12:23:20</p> 
  15.      * @author xg 
  16.      * @param data 待解密數(shù)據(jù) 
  17.      * @return String 解密后的數(shù)據(jù) 
  18.      */ 
  19.     String decrypt(String data) ; 
  20.      
  21.     /** 
  22.      *  <p>加密算法格式:算法[/模式/填充]</p> 
  23.      *  <p>時間:2020年12月24日-下午12:32:49</p> 
  24.      * @author xg 
  25.      * @return String 
  26.      */ 
  27.     String getAlgorithm() ; 
  28.      
  29.     public static class Hex { 
  30.          
  31.         private static final char[] HEX = { '0''1''2''3''4''5''6''7''8''9'
  32.                 'a''b''c''d''e''f' }; 
  33.          
  34.         public static byte[] decode(CharSequence s) { 
  35.             int nChars = s.length(); 
  36.             if (nChars % 2 != 0) { 
  37.                 throw new IllegalArgumentException("16進制數(shù)據(jù)錯誤"); 
  38.             } 
  39.             byte[] result = new byte[nChars / 2]; 
  40.             for (int i = 0; i < nChars; i += 2) { 
  41.                 int msb = Character.digit(s.charAt(i), 16); 
  42.                 int lsb = Character.digit(s.charAt(i + 1), 16); 
  43.                 if (msb < 0 || lsb < 0) { 
  44.                     throw new IllegalArgumentException( 
  45.                         "Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position"); 
  46.                 } 
  47.                 result[i / 2] = (byte) ((msb << 4) | lsb); 
  48.             } 
  49.             return result; 
  50.         } 
  51.          
  52.         public static String encode(byte[] buf) { 
  53.             StringBuilder sb = new StringBuilder() ; 
  54.             for (int i = 0, leng = buf.length; i < leng; i++) { 
  55.                 sb.append(HEX[(buf[i] & 0xF0) >>> 4]).append(HEX[buf[i] & 0x0F]) ; 
  56.             } 
  57.             return sb.toString() ; 
  58.         } 
  59.          
  60.     } 
  61.      

 該接口中定義了兩個方法分別是加密與解密的方法,還有Hex類 該類用來對數(shù)據(jù)處理16進制的轉(zhuǎn)換。

定義一個抽象類實現(xiàn)上面的接口,具體的加解密實現(xiàn)細(xì)節(jié)在該抽象類中

AbstractSecretProcess.java

  1. public abstract class AbstractSecretProcess implements SecretProcess { 
  2.      
  3.     @Resource 
  4.     private SecretProperties props ; 
  5.      
  6.     @Override 
  7.     public String decrypt(String data) { 
  8.         try { 
  9.             Cipher cipher = Cipher.getInstance(getAlgorithm()) ; 
  10.             cipher.init(Cipher.DECRYPT_MODE, keySpec()) ; 
  11.             byte[] decryptBytes = cipher.doFinal(Hex.decode(data)) ; 
  12.             return new String(decryptBytes) ; 
  13.         } catch (Exception e) { 
  14.             throw new RuntimeException(e) ; 
  15.         } 
  16.     } 
  17.      
  18.     @Override 
  19.     public String encrypt(String data) { 
  20.         try { 
  21.             Cipher cipher = Cipher.getInstance(getAlgorithm()) ; 
  22.             cipher.init(Cipher.ENCRYPT_MODE, keySpec()) ; 
  23.             return Hex.encode(cipher.doFinal(data.getBytes(Charset.forName("UTF-8")))) ; 
  24.         } catch (Exception e) { 
  25.             throw new RuntimeException(e) ; 
  26.         } 
  27.     } 
  28.      
  29.     /** 
  30.      *  <p>根據(jù)密鑰生成不同的密鑰材料</p> 
  31.      *  <p>目前支持:AES, DES</p> 
  32.      *  <p>時間:2020年12月25日-下午1:02:54</p> 
  33.      * @author xg 
  34.      * @param secretKey 密鑰 
  35.      * @param algorithm 算法 
  36.      * @return Key 
  37.      */ 
  38.     public Key getKeySpec(String algorithm) { 
  39.         if (algorithm == null || algorithm.trim().length() == 0) { 
  40.             return null ; 
  41.         } 
  42.         String secretKey = props.getKey() ; 
  43.         switch (algorithm.toUpperCase()) { 
  44.             case "AES"
  45.                 return new SecretKeySpec(secretKey.getBytes(), "AES") ; 
  46.             case "DES"
  47.                 Key key = null ; 
  48.                 try { 
  49.                     DESKeySpec desKeySpec = new DESKeySpec(secretKey.getBytes()) ; 
  50.                     SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES") ; 
  51.                     key = secretKeyFactory.generateSecret(desKeySpec); 
  52.                 } catch (Exception e) { 
  53.                     throw new RuntimeException(e) ; 
  54.                 } 
  55.                 return key ; 
  56.             default
  57.                 return null ; 
  58.         } 
  59.     } 
  60.      
  61.     /** 
  62.      *  <p>生成密鑰材料</p> 
  63.      *  <p>時間:2020年12月25日-上午11:35:03</p> 
  64.      * @author xg 
  65.      * @return Key 密鑰材料 
  66.      */ 
  67.     public abstract Key keySpec() ; 
  68.      

 該抽象類中提供了2中對稱加密的密鑰還原,分表是AES和DES算法。一個抽象方法,該抽象方法

keySpec該方法需要子類實現(xiàn)(具體使用的是哪種對稱加密算法)。

具體加密算法的實現(xiàn)類

AESAlgorithm.java

  1. public class AESAlgorithm extends AbstractSecretProcess { 
  2.  
  3.     @Override 
  4.     public String getAlgorithm() { 
  5.         return "AES/ECB/PKCS5Padding"
  6.     } 
  7.      
  8.     @Override 
  9.     public Key keySpec() { 
  10.         return this.getKeySpec("AES") ; 
  11.     } 
  12.  

 SecretProperties.java 屬性配置類

  1. @Configuration 
  2. public class SecretConfig { 
  3.      
  4.     @Bean 
  5.     @ConditionalOnMissingBean(SecretProcess.class) 
  6.     public SecretProcess secretProcess() { 
  7.         return new AESAlgorithm() ; 
  8.     } 
  9.      
  10.     @Component 
  11.     @ConfigurationProperties(prefix = "secret"
  12.     public static class SecretProperties { 
  13.          
  14.         private Boolean enabled ; 
  15.         private String key ; 
  16.  
  17.         public Boolean getEnabled() { 
  18.             return enabled; 
  19.         } 
  20.  
  21.         public void setEnabled(Boolean enabled) { 
  22.             this.enabled = enabled; 
  23.         } 
  24.  
  25.         public String getKey() { 
  26.             return key
  27.         } 
  28.  
  29.         public void setKey(String key) { 
  30.             this.key = key
  31.         } 
  32.          
  33.     } 
  34.      

 配置文件中如下配置:

  1. secret: 
  2.   key: aaaabbbbccccdddd #密鑰 
  3.   enabled: true #是否開啟加解密功能 

 在項目中可能不是所有的方法都要進行數(shù)據(jù)的加密解密出來,所以接下來定義一個注解,只有添加有該注解的Controller類或是具體接口方法才進行數(shù)據(jù)的加密解密,如下:

SIProtection.java

  1. @Target({ElementType.METHOD, ElementType.TYPE}) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Mapping 
  4. @Documented 
  5. public @interface SIProtection { 
  6.  

 對請求內(nèi)容進行解密出來,通過RequestBodyAdvice

DecryptRequestBodyAdivce.java

  1. @ControllerAdvice 
  2. @ConditionalOnProperty(name = "secret.enabled", havingValue = "true"
  3. public class DecryptRequestBodyAdivce extends RequestBodyAdviceAdapter { 
  4.  
  5.     @Resource 
  6.     private SecretProcess secretProcess ; 
  7.      
  8.     @Override 
  9.     public boolean supports(MethodParameter methodParameter, Type targetType, 
  10.             Class<? extends HttpMessageConverter<?>> converterType) { 
  11.         return methodParameter.getMethod().isAnnotationPresent(SIProtection.class)  
  12.                 || methodParameter.getMethod().getDeclaringClass().isAnnotationPresent(SIProtection.class) ; 
  13.     } 
  14.  
  15.     @Override 
  16.     public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, 
  17.             Class<? extends HttpMessageConverter<?>> converterType) throws IOException { 
  18.         String body = secretProcess.decrypt(inToString(inputMessage.getBody())) ; 
  19.         return new HttpInputMessage() { 
  20.             @Override 
  21.             public HttpHeaders getHeaders() { 
  22.                 return inputMessage.getHeaders(); 
  23.             } 
  24.             @Override 
  25.             public InputStream getBody() throws IOException { 
  26.                 return new ByteArrayInputStream(body.getBytes()) ; 
  27.             } 
  28.         } ; 
  29.     } 
  30.      
  31.     private String inToString(InputStream is) { 
  32.         byte[] buf = new byte[10 * 1024] ; 
  33.         int leng = -1 ; 
  34.         StringBuilder sb = new StringBuilder() ; 
  35.         try { 
  36.             while ((leng = is.read(buf)) != -1) { 
  37.                 sb.append(new String(buf, 0, leng)) ; 
  38.             } 
  39.             return sb.toString() ; 
  40.         } catch (IOException e) { 
  41.             throw new RuntimeException(e) ; 
  42.         } 
  43.     } 
  44.  

 注意這里的:@ConditionalOnProperty(name = "secret.enabled", havingValue = "true")注解,只有開啟了加解密功能才會生效。注意這里的supports方法

對響應(yīng)內(nèi)容加密出來

EncryptResponseBodyAdivce.java

  1. @ControllerAdvice 
  2. @ConditionalOnProperty(name = "secret.enabled", havingValue = "true"
  3. public class EncryptResponseBodyAdivce implements ResponseBodyAdvice<Object>  { 
  4.  
  5.     @Resource 
  6.     private SecretProcess secretProcess ; 
  7.  
  8.     @Override 
  9.     public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { 
  10.         return returnType.getMethod().isAnnotationPresent(SIProtection.class)  
  11.                 || returnType.getMethod().getDeclaringClass().isAnnotationPresent(SIProtection.class) ; 
  12.     } 
  13.  
  14.     @Override 
  15.     public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, 
  16.             Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, 
  17.             ServerHttpResponse response) { 
  18.         if (body == null) { 
  19.             return body ; 
  20.         } 
  21.         try { 
  22.             String jsonStr = new ObjectMapper().writeValueAsString(body) ; 
  23.             return secretProcess.encrypt(jsonStr) ; 
  24.         } catch (Exception e) { 
  25.             throw new RuntimeException(e) ; 
  26.         } 
  27.     } 

 Controller應(yīng)用

  1. @PostMapping("/save"
  2.     @SIProtection 
  3.     public R save(@RequestBody Users users) { 
  4.         return R.success(usersService.save(users)) ; 
  5.     } // 這對具體方法進行加解密 
  6.  
  7. @RestController 
  8. @RequestMapping("/users"
  9. @SIProtection  
  10. public class UsersController { // 對該Controller中的所有方法進行加解密處理 

 前端

引入第三方插件:crypto-js

工具方法加解密:

  1. /** 
  2.      * 加密方法 
  3.      * @param data 待加密數(shù)據(jù) 
  4.      * @returns {string|*} 
  5.      */ 
  6.     encrypt (data) { 
  7.       let key = CryptoJS.enc.Utf8.parse(Consts.Secret.key
  8.       if (typeof data === 'object') { 
  9.         data = JSON.stringify(data) 
  10.       } 
  11.       let plainText = CryptoJS.enc.Utf8.parse(data) 
  12.       let secretText = CryptoJS.AES.encrypt(plainText, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}).ciphertext.toString() 
  13.       return secretText 
  14.     }, 
  15.     /** 
  16.      * 解密數(shù)據(jù) 
  17.      * @param data 待解密數(shù)據(jù) 
  18.      */ 
  19.     decrypt (data) { 
  20.       let key = CryptoJS.enc.Utf8.parse(Consts.Secret.key
  21.       let secretText = CryptoJS.enc.Hex.parse(data) 
  22.       let encryptedBase64Str = CryptoJS.enc.Base64.stringify(secretText) 
  23.       let result = CryptoJS.AES.decrypt(encryptedBase64Str, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8) 
  24.       return JSON.parse(result) 
  25.     } 

 配置:

  1. let Consts = { 
  2.   Secret: { 
  3.     key'aaaabbbbccccdddd', // 必須16位(前后端要一致,密鑰) 
  4.     urls: ['/users/save'
  5.   } 
  6. export default Consts 

 這里的urls表示對那些請求進行攔截出來(加解密),這里也可以配置 "*" 表示對所有的請求出來。

axios請求前和響應(yīng)后對數(shù)據(jù)進行加解密出來:

發(fā)送請求前:

  1. axios.interceptors.request.use((config) => { 
  2.       let uri = config.url 
  3.       if (uri.includes('?')) { 
  4.         uri = uri.substring(0, uri.indexOf('?')) 
  5.       } 
  6.       if (window.cfg.enableSecret === '1' && config.data && (Consts.Secret.urls.indexOf('*') > -1 || Consts.Secret.urls.indexOf(uri) > -1)) { 
  7.         let data = config.data 
  8.         let secretText = Utils.Secret.encrypt(data) 
  9.         config.data = secretText 
  10.       } 
  11.       return config 
  12.     }, (error) => { 
  13.       let errorMessage = '請求失敗' 
  14.       store.dispatch(types.G_SHOW_ALERT, {title: '請求失敗', content: errorMessage, showDetail: false, detailContent: String(error)}) 
  15.       return Promise.reject(error) 
  16.     }) 
  17. axios.interceptors.response.use((response) => { 
  18.       let uri = response.config.url 
  19.       if (uri.includes('?')) { 
  20.         uri = uri.substring(0, uri.indexOf('?')) 
  21.       } 
  22.       if (window.cfg.enableSecret === '1' && response.data && (Consts.Secret.urls.indexOf('*') > -1 || Consts.Secret.urls.indexOf(uri) > -1)) { 
  23.         let data = Utils.Secret.decrypt(response.data) 
  24.         if (data) { 
  25.           response.data = data 
  26.         } 
  27.       } 
  28.       return response 
  29.     }, (error) => { 
  30.       console.error(`test interceptors.response is in, ${error}`) 
  31.       return Promise.reject(error) 
  32.     }) 

 這里的 window.cfg.enableSecret 配置是我自己項目中有個配置文件配置是否開啟,這個大家可以根據(jù)自己的環(huán)境來實現(xiàn)。

測試:


這里可以看到前端發(fā)起的請求內(nèi)容已經(jīng)被加密了

響應(yīng)內(nèi)容:


完畢!!!

 

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2011-08-01 10:36:01

2024-06-13 08:41:41

2024-10-15 10:38:32

2021-01-29 08:19:50

HTTPS安全傳輸

2020-09-26 22:04:32

數(shù)據(jù)安全傳輸HTTPSHTTP 協(xié)議

2024-07-09 10:13:15

2024-05-08 08:16:11

2013-03-21 09:32:31

文件傳輸安全文件傳輸

2016-10-10 23:00:18

2009-11-26 13:12:01

2023-03-06 08:49:02

加密和解密SpringBoot

2016-10-10 22:48:16

2022-10-28 18:36:18

2025-03-26 08:43:17

2010-04-23 14:33:34

郵件服務(wù)器加密

2015-03-11 17:06:34

SDH網(wǎng)絡(luò)評估優(yōu)化服務(wù)華為

2017-08-14 15:14:33

2019-12-13 10:42:03

LinuxSCP命令

2024-12-31 08:54:38

2025-03-10 07:49:13

點贊
收藏

51CTO技術(shù)棧公眾號