Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to encrypt and decrypt Springboot data safely

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces you how to carry out Springboot data security transmission encryption and decryption, the content is very detailed, interested friends can refer to, hope to be helpful to you.

Environment: springboot2.2.6.RELEASE, Vue+axios

The requested content is decrypted by inheriting RequestBodyAdviceAdapter, and the corresponding content is encrypted by ResponseBodyAdvice.

Define the interface for encryption and decryption:

SecretProcess.java

Public interface SecretProcess {/ *

data encryption

*

Time: 24 December 2020-12:22:13

* @ author xg * @ param data data to be encrypted * @ return String encryption result * / String encrypt (String data); / *

Data decryption

*

Time: 24 December 2020-12:23:20

* @ author xg * @ param data data to be decrypted * @ return String decrypted data * / String decrypt (String data); / *

Encryption algorithm format: algorithm [/ mode / fill]

*

Time: 24 December 2020-12:32:49

* @ author xg * @ return String * / String getAlgorithm () Public static class Hex {private static final char [] HEX = {'0,1,' 2, 3, 4, 5, 6, 7, 8, 9, 9, 3, 3, 4, 5, 6, 7, 8, 9, a, a, b, c, d, e, f'} Public static byte [] decode (CharSequence s) {int nChars = s.length (); if (nChars% 2! = 0) {throw new IllegalArgumentException ("hexadecimal data error");} byte [] result = new byte [nChars / 2]; for (int I = 0; I

< nChars; i += 2) { int msb = Character.digit(s.charAt(i), 16); int lsb = Character.digit(s.charAt(i + 1), 16); if (msb < 0 || lsb < 0) { throw new IllegalArgumentException( "Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position"); } result[i / 2] = (byte) ((msb >

> 4]) .append (HEX [buf [I] & 0x0F]);} return sb.toString ();}

In this interface, two methods are defined as encryption and decryption, and the Hex class is used to deal with hexadecimal conversion of data.

Define an abstract class to implement the above interface, and the specific encryption and decryption implementation details are in the abstract class.

AbstractSecretProcess.java

Public abstract class AbstractSecretProcess implements SecretProcess {@ Resource private SecretProperties props; @ Override public String decrypt (String data) {try {Cipher cipher = Cipher.getInstance (getAlgorithm ()); cipher.init (Cipher.DECRYPT_MODE, keySpec ()); byte [] decryptBytes = cipher.doFinal (Hex.decode (data)); return new String (decryptBytes) } catch (Exception e) {throw new RuntimeException (e);}} @ Override public String encrypt (String data) {try {Cipher cipher = Cipher.getInstance (getAlgorithm ()); cipher.init (Cipher.ENCRYPT_MODE, keySpec ()) Return Hex.encode (cipher.doFinal (data.getBytes (Charset.forName ("UTF-8");} catch (Exception e) {throw new RuntimeException (e);}} / * *

Generate different key materials according to the key

*

Currently supports: AES, DES

*

Time: 25 December 2020-1:02:54

* @ author xg * @ param secretKey key * @ param algorithm algorithm * @ return Key * / public Key getKeySpec (String algorithm) {if (algorithm = = null | | algorithm.trim (). Length () = 0) {return null;} String secretKey = props.getKey () Switch (algorithm.toUpperCase ()) {case "AES": return new SecretKeySpec (secretKey.getBytes (), "AES"); case "DES": Key key = null; try {DESKeySpec desKeySpec = new DESKeySpec (secretKey.getBytes ()); SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance ("DES") Key = secretKeyFactory.generateSecret (desKeySpec);} catch (Exception e) {throw new RuntimeException (e);} return key; default: return null;}} / * *

Generate key material

*

Time: 25 December 2020-11:35:03

* @ author xg * @ return Key key material * / public abstract Key keySpec ();}

The key restore of symmetric encryption in 2 is provided in this abstract class, and the sub-tables are AES and DES algorithms. An abstract method, the abstract method

KeySpec this method requires a subclass implementation (specifically which symmetric encryption algorithm is used).

Implementation class of specific encryption algorithm

AESAlgorithm.java

Public class AESAlgorithm extends AbstractSecretProcess {@ Override public String getAlgorithm () {return "AES/ECB/PKCS5Padding";} @ Override public Key keySpec () {return this.getKeySpec ("AES");}}

SecretProperties.java property configuration class

@ Configuration public class SecretConfig {@ Bean @ ConditionalOnMissingBean (SecretProcess.class) public SecretProcess secretProcess () {return new AESAlgorithm ();} @ Component @ ConfigurationProperties (prefix = "secret") public static class SecretProperties {private Boolean enabled; private String key; public Boolean getEnabled () {return enabled } public void setEnabled (Boolean enabled) {this.enabled = enabled;} public String getKey () {return key;} public void setKey (String key) {this.key = key;}

The configuration file is as follows:

Secret: key: aaaabbbbccccdddd # key enabled: true # whether to enable encryption and decryption

In the project, not all methods need to encrypt and decrypt the data, so next, define an annotation. Only the Controller class or the specific API method with the annotation can encrypt and decrypt the data, as follows:

SIProtection.java

Target ({ElementType.METHOD, ElementType.TYPE}) @ Retention (RetentionPolicy.RUNTIME) @ Mapping @ Documented public @ interface SIProtection {}

Decrypt the request content and use RequestBodyAdvice

DecryptRequestBodyAdivce.java

@ ControllerAdvice @ ConditionalOnProperty (name = "secret.enabled", havingValue = "true") public class DecryptRequestBodyAdivce extends RequestBodyAdviceAdapter {@ Resource private SecretProcess secretProcess; @ Override public boolean supports (MethodParameter methodParameter, Type targetType, Class > converterType) {return methodParameter.getMethod () .isAnnotationPresent (SIProtection.class) | | methodParameter.getMethod () .getDeclaringClass () .isAnnotationPresent (SIProtection.class) } @ Override public HttpInputMessage beforeBodyRead (HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class > converterType) throws IOException {String body = secretProcess.decrypt (inToString (inputMessage.getBody ()); return new HttpInputMessage () {@ Override public HttpHeaders getHeaders () {return inputMessage.getHeaders () } @ Override public InputStream getBody () throws IOException {return new ByteArrayInputStream (body.getBytes ());}};} private String inToString (InputStream is) {byte [] buf = new byte [10 * 1024]; int leng =-1; StringBuilder sb = new StringBuilder () Try {while ((leng = is.read (buf))! =-1) {sb.append (new String (buf, 0, leng));} return sb.toString ();} catch (IOException e) {throw new RuntimeException (e);}

Note the @ ConditionalOnProperty (name = "secret.enabled", havingValue = "true") annotation here. It will take effect only if the encryption and decryption function is enabled. Notice the supports method here

Encrypt the response content

EncryptResponseBodyAdivce.java

@ ControllerAdvice @ ConditionalOnProperty (name = "secret.enabled", havingValue = "true") public class EncryptResponseBodyAdivce implements ResponseBodyAdvice {@ Resource private SecretProcess secretProcess; @ Override public boolean supports (MethodParameter returnType, Class > converterType) {return returnType.getMethod () .isAnnotationPresent (SIProtection.class) | | returnType.getMethod () .getDeclaringClass () .isAnnotationPresent (SIProtection.class) } @ Override public Object beforeBodyWrite (Object body, MethodParameter returnType, MediaType selectedContentType, Class > selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body = = null) {return body;} try {String jsonStr = new ObjectMapper () .writeValueAsString (body); return secretProcess.encrypt (jsonStr) } catch (Exception e) {throw new RuntimeException (e);}

Controller application

@ PostMapping ("/ save") @ SIProtection public R save (@ RequestBody Users users) {return R.success (usersService.save (users));} / this encrypts and decrypts specific methods @ RestController @ RequestMapping ("/ users") @ SIProtection public class UsersController {/ / encrypts all methods in the Controller}

Front end

Introduce a third-party plug-in: crypto-js

Tool method encryption and decryption:

/ * encryption method * @ param data data to be encrypted * @ returns {string | *} * / encrypt (data) {let key = CryptoJS.enc.Utf8.parse (Consts.Secret.key) if (typeof data = 'object') {data = JSON.stringify (data)} let plainText = CryptoJS.enc.Utf8.parse (data) let secretText = CryptoJS.AES.encrypt (plainText, key) {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}) .ciphertext.toString () return secretText} / * * decrypted data * @ param data data to be decrypted * / decrypt (data) {let key = CryptoJS.enc.Utf8.parse (Consts.Secret.key) let secretText = CryptoJS.enc.Hex.parse (data) let encryptedBase64Str = CryptoJS.enc.Base64.stringify (secretText) let result = CryptoJS.AES.decrypt (encryptedBase64Str, key, {mode: CryptoJS.mode.ECB Padding: CryptoJS.pad.Pkcs7}) .toString (CryptoJS.enc.Utf8) return JSON.parse (result)}

Configuration:

Let Consts = {Secret: {key: 'aaaabbbbccccdddd', / / must be 16 bits (the front and rear ends must be consistent, key) urls: [' / users/save']}} export default Consts

The urls here means to intercept (encrypt and decrypt) those requests, and you can also configure "*" to indicate all requests.

Encrypt and decrypt the data before and after the axios request:

Before sending the request:

Axios.interceptors.request.use ((config) = > {let uri = config.url if (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)) {let data = config.data let secretText = Utils.Secret.encrypt (data) config.data = secretText} return config} (error) = > {let errorMessage = 'request failed' store.dispatch (types.G_SHOW_ALERT, {title: 'request failed', content: errorMessage, showDetail: false, detailContent: String (error)}) return Promise.reject (error)}) axios.interceptors.response.use ((response) = > {let uri = response.config.url if (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)) {let data = Utils.Secret.decrypt (response.data) if (data) {response.data = data}} return response} (error) = > {console.error (`test interceptors.response is in, ${error} `) return Promise.reject (error)})

The window.cfg.enableSecret configuration here is whether the configuration file is enabled or not in my own project, which can be implemented according to your own environment.

Test:

Here you can see that the request content initiated by the front end has been encrypted.

Response content:

On how to carry out Springboot data security transmission encryption and decryption to share here, I hope that the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report