Based on the latest Vue3 standard, realize the integrated solution of background and front end

Posted by elim on Wed, 05 Jan 2022 12:08:38 +0100

Based on the latest Vue3 standard, realize the integrated solution of background and front end
1. Develop encryption and decryption starter
In order to make the tool we developed more general and review the custom Spring Boot Starter, we will make this tool into a stator, which can be directly referenced in the Spring Boot project in the future.
First, we create a Spring Boot project and introduce the Spring Boot starter web dependency:
<dependency>

<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
<version>2.4.3</version>

</dependency>
Copy code
Because our tool is developed for Web projects and will be used in the Web environment in the future, the scope is set to provided when adding dependencies here.
After the dependency is added, we first define an encryption tool class for backup. There are many options for encryption, including symmetric encryption and asymmetric encryption. Among them, different algorithms such as AES, DES and 3DES can be used for symmetric encryption. Here, we use the Cipher provided by Java to realize symmetric encryption, and use the AES algorithm:
public class AESUtils {

private static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";

// Get cipher
private static Cipher getCipher(byte[] key, int model) throws Exception {
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
    cipher.init(model, secretKeySpec);
    return cipher;
}

// AES encryption
public static String encrypt(byte[] data, byte[] key) throws Exception {
    Cipher cipher = getCipher(key, Cipher.ENCRYPT_MODE);
    return Base64.getEncoder().encodeToString(cipher.doFinal(data));
}

// AES decryption
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
    Cipher cipher = getCipher(key, Cipher.DECRYPT_MODE);
    return cipher.doFinal(Base64.getDecoder().decode(data));
}

}
Copy code
This tool class is relatively simple and does not need to be explained. It should be noted that the encrypted data may not be readable, so we generally need to encode the encrypted data with Base64 algorithm to obtain a readable string. In other words, the return value of the above AES encryption method is a Base64 encoded string, and the parameters of the AES decryption method are also a Base64 encoded string. Decode the string first and then decrypt it.
Next, we encapsulate a response tool class for backup. If you often watch SongGe videos, you already know it well:
public class RespBean {

private Integer status;
private String msg;
private Object obj;

public static RespBean build() {
    return new RespBean();
}

public static RespBean ok(String msg) {
    return new RespBean(200, msg, null);
}

public static RespBean ok(String msg, Object obj) {
    return new RespBean(200, msg, obj);
}

public static RespBean error(String msg) {
    return new RespBean(500, msg, null);
}

public static RespBean error(String msg, Object obj) {
    return new RespBean(500, msg, obj);
}

private RespBean() {
}

private RespBean(Integer status, String msg, Object obj) {
    this.status = status;
    this.msg = msg;
    this.obj = obj;
}

public Integer getStatus() {
    return status;
}

public RespBean setStatus(Integer status) {
    this.status = status;
    return this;
}

public String getMsg() {
    return msg;
}

public RespBean setMsg(String msg) {
    this.msg = msg;
    return this;
}

public Object getObj() {
    return obj;
}

public RespBean setObj(Object obj) {
    this.obj = obj;
    return this;
}

}
Copy code
Next, we define two annotations @ Decrypt and @ Encrypt:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.PARAMETER})
public @interface Decrypt {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Encrypt {
}
Copy code
These two annotations are two tags. In the process of later use, the interface method that adds the @ Encrypt annotation will Encrypt and return the data of the interface, and the interface / parameter that adds the @ Decrypt annotation will Decrypt the interface / parameter. This definition is also relatively simple. There is nothing to say. It should be noted that @ Decrypt has one more usage scenario than @ Encrypt, that is, @ Decrypt can be used on parameters.
Considering that users may configure their own encrypted keys, we will define an EncryptProperties class to read the user configured keys:
@ConfigurationProperties(prefix = "spring.encrypt")
public class EncryptProperties {

private final static String DEFAULT_KEY = "www.itboyhub.com";
private String key = DEFAULT_KEY;

public String getKey() {
    return key;
}

public void setKey(String key) {
    this.key = key;
}

}
Copy code
Here, I set the default key to www.itboyhub.com COM and key are 16 bit strings. The website address of SongGe just meets the requirements. In the future, if the user wants to configure the key himself, he only needs to be in the application Configure spring in properties encrypt. Key = XXX.
After all the preparations are finished, it's time to officially encrypt and decrypt.
Because a very important purpose of SongGe's article is to share the usage of ResponseBodyAdvice and RequestBodyAdvice. RequestBodyAdvice has no problem in decryption, while ResponseBodyAdvice has some limitations in encryption, but it has little impact. As I said earlier, if you want to control everything flexibly, Let's customize the filter. Here I will use these two tools to realize it first.
In addition, it should be noted that ResponseBodyAdvice will take effect only when you use @ ResponseBody annotation, and RequestBodyAdvice will take effect only when you use @ RequestBody annotation. In other words, these two are only useful when the front and back ends are JSON interaction. However, generally speaking, the scene of interface encryption and decryption is only possible when the front and rear ends are separated.
Let's first look at interface encryption:
@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class EncryptResponse implements ResponseBodyAdvice<RespBean> {

private ObjectMapper om = new ObjectMapper();
@Autowired
EncryptProperties encryptProperties;
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    return returnType.hasMethodAnnotation(Encrypt.class);
}

@Override
public RespBean beforeBodyWrite(RespBean body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    byte[] keyBytes = encryptProperties.getKey().getBytes();
    try {
        if (body.getMsg()!=null) {
            body.setMsg(AESUtils.encrypt(body.getMsg().getBytes(),keyBytes));
        }
        if (body.getObj() != null) {
            body.setObj(AESUtils.encrypt(om.writeValueAsBytes(body.getObj()), keyBytes));
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return body;
}

}
Copy code
We customize the EncryptResponse class to implement the ResponseBodyAdvice interface. The generic type represents the return type of the interface. Here, we need to implement two methods:

supports: this method is used to determine what kind of interface needs encryption. The parameter returnType indicates the return type. Our judgment logic here is whether the method contains @ Encrypt annotation. If yes, it means that the interface needs encryption. If not, it means that the interface does not need encryption.
beforeBodyWrite: this method will be executed before the data response, that is, we will process the response data twice before it is converted to json return. The processing method here is very simple. If the status in RespBean is the status code, you don't need to encrypt it. You can re encrypt the other two fields and set the value again.
In addition, it should be noted that the customized ResponseBodyAdvice needs to be marked with the @ ControllerAdvice annotation.

Let's look at interface decryption:
@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class DecryptRequest extends RequestBodyAdviceAdapter {

@Autowired
EncryptProperties encryptProperties;
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
    return methodParameter.hasMethodAnnotation(Decrypt.class) || methodParameter.hasParameterAnnotation(Decrypt.class);
}

@Override
public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
    byte[] body = new byte[inputMessage.getBody().available()];
    inputMessage.getBody().read(body);
    try {
        byte[] decrypt = AESUtils.decrypt(body, encryptProperties.getKey().getBytes());
        final ByteArrayInputStream bais = new ByteArrayInputStream(decrypt);
        return new HttpInputMessage() {
            @Override
            public InputStream getBody() throws IOException {
                return bais;
            }

            @Override
            public HttpHeaders getHeaders() {
                return inputMessage.getHeaders();
            }
        };
    } catch (Exception e) {
        e.printStackTrace();
    }
    return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
}

}
Copy code

First of all, please note that the DecryptRequest class does not directly implement the RequestBodyAdvice interface, but inherits from the RequestBodyAdvice adapter class. This class is a subclass of the RequestBodyAdvice interface and implements some methods in the interface. In this way, when we inherit from the RequestBodyAdvice adapter, we only need to implement some methods according to our actual needs.
supports: this method is used to determine which interfaces need to handle interface decryption. Our judgment logic here is the interface with @ Decrypt annotation on the method or parameter to handle decryption.
beforeBodyRead: this method will be executed before the parameters are converted to specific objects. We first load the data from the stream, then decrypt the data, and then reconstruct the HttpInputMessage object to return.

Next, we will define an automation configuration class as follows:
@Configuration
@ComponentScan("org.javaboy.encrypt.starter")
public class EncryptAutoConfiguration {

}
Copy code
There's nothing to say about this. It's relatively simple.
Finally, META-INF is defined in the resources directory, and then spring. Inf is defined The contents of the factories file are as follows:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.javaboy.encrypt.starter.autoconfig.EncryptAutoConfiguration
Copy code
This will automatically load the configuration class when the project starts.
So far, our starter has been developed.

download

Topics: Vue.js