Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CUSTOM encryption and decryption support #22

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
- - [x] AES
- - [x] DES
- - [x] RSA
- - [x] CUSTOM

- 可进行解密的方式有:
- - [x] AES
- - [x] DES
- - [x] RSA
- - [x] CUSTOM
## 引入注册
### 导入依赖
在项目的`pom.xml`中引入依赖:
Expand Down Expand Up @@ -116,6 +119,41 @@ public class User implements Serializable {

}
```

## 使用自定义加密/解密
````java
@Data
@EncryptBody
@FieldBody
public class User implements Serializable {

@CustomDecryptBody(providerClassName = "com.myorg.mypkg.MyCryptoProvider", decryptMethodName = "myDecryptionFunction")
private String name;

@CustomEncryptBody(providerClassName = "com.myorg.mypkg.MyCryptoProvider", encryptMethodName = "myEncryptionFunction")
private String numberValue;



}
````

使用 myEncryptionFunction 实现 MyCryptoProvider 类,如下所示。
```java
package com.myorg.mypkg;

public class MyCryptoProvider {
String myEncryptionFunction(String input) {
// 用于加密输入并返回预期加密数据的代码
}

String myDecryptionFunction(String input) {
// 解密输入并返回预期解密数据的代码
}

}
```

## 注解一览表
- [编码/加密注解一览表](https://github.com/Licoy/encrypt-body-spring-boot-starter/wiki/加密注解一览表)
- [解密注解一览表](https://github.com/Licoy/encrypt-body-spring-boot-starter/wiki/解密注解一览表)
Expand Down
39 changes: 39 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
- - [x] AES
- - [x] DES
- - [x] RSA
- - [x] CUSTOM
- The methods that can be decrypted are:
- - [x] AES
- - [x] DES
- - [x] RSA
- - [x] CUSTOM
## Import registration
### Import dependencies
Introduce dependencies in the project's `pom.xml`:
Expand Down Expand Up @@ -116,6 +118,43 @@ public class User implements Serializable {

}
````

## Using CUSTOM encryption/decryption
````java
@Data
@EncryptBody
@FieldBody
public class User implements Serializable {

@CustomDecryptBody(providerClassName = "com.myorg.mypkg.MyCryptoProvider", decryptMethodName = "myDecryptionFunction")
private String name;

@CustomEncryptBody(providerClassName = "com.myorg.mypkg.MyCryptoProvider", encryptMethodName = "myEncryptionFunction")
private String numberValue;



}
````

Implement the `MyCryptoProvider` class with `myEncryptionFunction` as shown below.
```java
package com.myorg.mypkg;

public class MyCryptoProvider {
String myEncryptionFunction(String input) {
// code to encrypt input and return the expected encrypted data
}

String myDecryptionFunction(String input) {
// code to decrypt input and return the expected decrypted data
}

}
```



## Annotation list
- [Encryption/Encryption Annotation List](https://github.com/Licoy/encrypt-body-spring-boot-starter/wiki/加密注解一览表)
- [Decryption Annotation List](https://github.com/Licoy/encrypt-body-spring-boot-starter/wiki/解密注解一览表)
Expand Down
26 changes: 26 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,32 @@
</execution>
</executions>
</plugin>
<!-- maven shade plugin to combine all dependencies -->
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</plugin>

</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import cn.hutool.crypto.asymmetric.RSA;
import cn.licoy.encryptbody.annotation.FieldBody;
import cn.licoy.encryptbody.annotation.decrypt.AESDecryptBody;
import cn.licoy.encryptbody.annotation.decrypt.CustomDecryptBody;
import cn.licoy.encryptbody.annotation.decrypt.DESDecryptBody;
import cn.licoy.encryptbody.annotation.decrypt.DecryptBody;
import cn.licoy.encryptbody.annotation.decrypt.RSADecryptBody;
Expand Down Expand Up @@ -203,6 +204,14 @@ private DecryptAnnotationInfoBean getDecryptAnnotation(AnnotatedElement annotate
return DecryptAnnotationInfoBean.builder().decryptBodyMethod(DecryptBodyMethod.RSA).key(decryptBody.key()).rsaKeyType(decryptBody.type()).build();
}
}

if (annotatedElement.isAnnotationPresent(CustomDecryptBody.class)) {
CustomDecryptBody decryptBody = annotatedElement.getAnnotation(CustomDecryptBody.class);
if (decryptBody != null) {
return DecryptAnnotationInfoBean.builder().decryptBodyMethod(DecryptBodyMethod.CUSTOM).providerClassName(decryptBody.providerClassName()).decryptMethodName(decryptBody.decryptMethodName()).build();
}
}

return null;
}

Expand Down Expand Up @@ -232,6 +241,15 @@ private String switchDecrypt(String formatStringBody, DecryptAnnotationInfoBean
RSA rsa = CommonUtils.infoBeanToRsaInstance(infoBean);
return rsa.decryptStr(formatStringBody, infoBean.getRsaKeyType().toolType);
}
if (method == DecryptBodyMethod.CUSTOM) {
try {
Class<?> clazz = Class.forName(infoBean.getProviderClassName());
Method m = clazz.getMethod(infoBean.getDecryptMethodName(), String.class);
return m.invoke(null, formatStringBody).toString();
} catch( Exception e) {
return "failed to encrypt: " + formatStringBody;
}
}
throw new DecryptBodyFailException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.lang.reflect.Method;



/**
* 响应数据的加密处理<br>
* 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.ResponseBody}</strong>
Expand Down Expand Up @@ -75,45 +76,55 @@ private boolean hasEncryptAnnotation(AnnotatedElement annotatedElement) {
if (annotatedElement == null) {
return false;
}
return annotatedElement.isAnnotationPresent(EncryptBody.class) || annotatedElement.isAnnotationPresent(AESEncryptBody.class) || annotatedElement.isAnnotationPresent(DESEncryptBody.class) || annotatedElement.isAnnotationPresent(RSAEncryptBody.class) || annotatedElement.isAnnotationPresent(MD5EncryptBody.class) || annotatedElement.isAnnotationPresent(SHAEncryptBody.class);
return annotatedElement.isAnnotationPresent(EncryptBody.class)
|| annotatedElement.isAnnotationPresent(AESEncryptBody.class)
|| annotatedElement.isAnnotationPresent(DESEncryptBody.class)
|| annotatedElement.isAnnotationPresent(RSAEncryptBody.class)
|| annotatedElement.isAnnotationPresent(MD5EncryptBody.class)
|| annotatedElement.isAnnotationPresent(SHAEncryptBody.class)
|| annotatedElement.isAnnotationPresent(CustomEncryptBody.class);
}



@Override
public String beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body == null) {
return null;
}
String str = CommonUtils.convertToStringOrJson(body, objectMapper);
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);

Method method = returnType.getMethod();
if (method != null) {
// 从方法上

//check if only some fields is response should be encrypted or the whole response should be encrypted.
//if only field encryption is required, we should not change the content-type and keep the returned object intact.
Class<?> methodReturnType = method.getReturnType();
if (methodReturnType.isAnnotationPresent(FieldBody.class)) {
return this.eachClassField(body, method.getReturnType());
}

EncryptAnnotationInfoBean methodAnnotation = this.getEncryptAnnotation(method);
if (methodAnnotation != null) {
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
return switchEncrypt(str, methodAnnotation);
}
// 从方法返回值上
Class<?> methodReturnType = method.getReturnType();
if (methodReturnType.isAnnotationPresent(FieldBody.class)) {
Object encryptResult = this.eachClassField(body, method.getReturnType());
try {
return objectMapper.writeValueAsString(encryptResult);
} catch (JsonProcessingException e) {
throw new EncryptBodyFailException(e.getMessage());
}
} else {
EncryptAnnotationInfoBean returnTypeClassAnnotation = this.getEncryptAnnotation(methodReturnType);
if (returnTypeClassAnnotation != null) {
return switchEncrypt(str, returnTypeClassAnnotation);
}

EncryptAnnotationInfoBean returnTypeClassAnnotation = this.getEncryptAnnotation(methodReturnType);
if (returnTypeClassAnnotation != null) {
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
return switchEncrypt(str, returnTypeClassAnnotation);
}
}

// 从声明类上
EncryptAnnotationInfoBean classAnnotation = this.getEncryptAnnotation(returnType.getDeclaringClass());
if (classAnnotation != null) {
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
return switchEncrypt(str, classAnnotation);
}


throw new EncryptBodyFailException();
}

Expand Down Expand Up @@ -197,6 +208,12 @@ private EncryptAnnotationInfoBean getEncryptAnnotation(AnnotatedElement annotate
return EncryptAnnotationInfoBean.builder().encryptBodyMethod(EncryptBodyMethod.RSA).key(encryptBody.key()).rsaKeyType(encryptBody.type()).build();
}
}
if (annotatedElement.isAnnotationPresent(CustomEncryptBody.class)) {
CustomEncryptBody encryptBody = annotatedElement.getAnnotation(CustomEncryptBody.class);
if (encryptBody != null) {
return EncryptAnnotationInfoBean.builder().encryptBodyMethod(EncryptBodyMethod.CUSTOM).providerClassName(encryptBody.providerClassName()).encryptMethodName(encryptBody.encryptMethodName()).build();
}
}
return null;
}

Expand Down Expand Up @@ -236,6 +253,16 @@ private String switchEncrypt(String formatStringBody, EncryptAnnotationInfoBean
RSA rsa = CommonUtils.infoBeanToRsaInstance(infoBean);
return rsa.encryptHex(formatStringBody, infoBean.getRsaKeyType().toolType);
}
if (method == EncryptBodyMethod.CUSTOM) {
try {
Class<?> clazz = Class.forName(infoBean.getProviderClassName());
Method m = clazz.getMethod(infoBean.getEncryptMethodName(), String.class);
return m.invoke(null, formatStringBody).toString();
} catch(Exception e) {
return "failed to encrypt: " + formatStringBody;
}

}
throw new EncryptBodyFailException();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cn.licoy.encryptbody.annotation.decrypt;

import java.lang.annotation.*;

@Target(value = {ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented

public @interface CustomDecryptBody {
/**
* Custom encryption/decryption provider class name.
*
* @return provider class name
*/
public String providerClassName() default "";

/**
* Name of the static method in provider class to be used for encryption.
* NOTE: encryption method should have a signature like `public String encrypt(String)`.
*
* @return
*/
public String encryptMethodName() default "encrypt";

/**
* Name of the static method in provider class to be used for decryption.
* NOTE: decryption method should have a signature like `public String decrypt(String)`.
*
* @return
*/
public String decryptMethodName() default "decrypt";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cn.licoy.encryptbody.annotation.encrypt;




import java.lang.annotation.*;

@Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomEncryptBody {

/**
* Custom encryption/decryption provider class name.
*
* @return provider class name
*/
public String providerClassName() default "";

/**
* Name of the static method in provider class to be used for encryption.
* NOTE: encryption method should have a signature like `public String encrypt(String)`.
*
* @return
*/
public String encryptMethodName() default "encrypt";

/**
* Name of the static method in provider class to be used for decryption.
* NOTE: decryption method should have a signature like `public String decrypt(String)`.
*
* @return
*/
public String decryptMethodName() default "decrypt";

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@ public class DecryptAnnotationInfoBean implements ISecurityInfo {

private RSAKeyType rsaKeyType;

private String providerClassName;

private String encryptMethodName;

private String decryptMethodName;

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,11 @@ public class EncryptAnnotationInfoBean implements ISecurityInfo {

private RSAKeyType rsaKeyType;

private String providerClassName;

private String encryptMethodName;

private String decryptMethodName;


}
Loading