Skip to content

增加一些功能 #10

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

Open
wants to merge 4 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
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,40 @@ public @interface Decrypt{

```

- **@EnDecrypt**, 一个注解实现入参/响应加密
```java
@Decrypt
@Encrypt
public @interface EnDecrypt {

/**
* 请求参数一定要是加密内容
*/
@AliasFor(annotation = Decrypt.class, value = "required")
boolean required() default false;

/**
* 请求数据时间戳校验时间差
* 超过(当前时间-指定时间)的数据认定为伪造
* 注意应用程序需要捕获 {@link cn.shuibo.exception.EncryptRequestException} 异常
*/
@AliasFor(annotation = Decrypt.class, value = "timeout")
long timeout() default 3000;
}
```

- **AbstractResponseCovert**, 依赖方可自定义JSON响应转换工具
```java
@Component
public class ResponseCovert extends AbstractResponseCovert {

@Override
public String covert(Object body) {
return JSON.toJSONString(body);
}
}
```

### 3.About author
![码农届首家互娱自媒体](https://images.gitee.com/uploads/images/2020/0529/112357_aac5a702_1674154.jpeg "kxmn.jpg")
- Blog:https://shuibo.cn
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>cn.shuibo</groupId>
<artifactId>rsa-encrypt-body-spring-boot</artifactId>
<version>1.0.2.RELEASE</version>
<version>1.1.0.RELEASE</version>
<name>rsa-encrypt-body-spring-boot</name>
<description>Spring Boot 接口请求参数自动加解密</description>

Expand Down
52 changes: 38 additions & 14 deletions src/main/java/cn/shuibo/advice/DecryptHttpInputMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.*;
import java.util.stream.Collectors;

/**
Expand All @@ -24,6 +21,11 @@
**/
public class DecryptHttpInputMessage implements HttpInputMessage {

/**
* 时间戳字段
*/
private static final String TIMESTAMP = "timestamp";

private Logger log = LoggerFactory.getLogger(this.getClass());
private HttpHeaders headers;
private InputStream body;
Expand Down Expand Up @@ -71,20 +73,42 @@ public DecryptHttpInputMessage(HttpInputMessage inputMessage, SecretKeyConfig se
}

// 开启时间戳检查
if (timestampCheck) {
// 容忍最小请求时间
long toleranceTime = System.currentTimeMillis() - decrypt.timeout();
long requestTime = JsonUtils.getNode(decryptBody, "timestamp").asLong();
// 如果请求时间小于最小容忍请求时间, 判定为超时
if (requestTime < toleranceTime) {
log.error("Encryption request has timed out, toleranceTime:{}, requestTime:{}, After decryption:{}", toleranceTime, requestTime, decryptBody);
throw new EncryptRequestException("request timeout");
}
}
this.timeCheck(decrypt, timestampCheck, decryptBody);

this.body = new ByteArrayInputStream(decryptBody.getBytes());
}

private void timeCheck(Decrypt decrypt, boolean timestampCheck, String decryptBody) throws IOException {

if (!timestampCheck) {
return;
}

if (!JsonUtils.hasNode(decryptBody, TIMESTAMP)) {
log.error("Cipher text does not contain timestamp, After decryption:{}", decryptBody);
throw new EncryptRequestException("Encrypted package error");
}

long currTime = System.currentTimeMillis();
long requestTime = JsonUtils.getNode(decryptBody, TIMESTAMP).asLong();

// 请求时间为当前服务器未来时间
if (requestTime > currTime) {
log.error("The request time is the future time of the current server, " +
"currTime:{}, requestTime:{}, After decryption:{}", currTime, requestTime, decryptBody);
throw new EncryptRequestException("request timeout");
}

// 最小容忍请求时间
long toleranceTime = currTime - decrypt.timeout();
// 如果请求时间小于最小容忍请求时间, 判定为超时
if (requestTime < toleranceTime) {
log.error("Encryption request has timed out, toleranceTime:{}, requestTime:{}, After decryption:{}",
toleranceTime, requestTime, decryptBody);
throw new EncryptRequestException("request timeout");
}
}

@Override
public InputStream getBody(){
return body;
Expand Down
7 changes: 5 additions & 2 deletions src/main/java/cn/shuibo/advice/EncryptRequestBodyAdvice.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package cn.shuibo.advice;

import cn.shuibo.annotation.Decrypt;
import cn.shuibo.annotation.EnDecrypt;
import cn.shuibo.config.SecretKeyConfig;
import cn.shuibo.exception.EncryptRequestException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
Expand Down Expand Up @@ -38,9 +40,10 @@ public boolean supports(MethodParameter methodParameter, Type targetType, Class<
encrypt = false;
return false;
}
if (method.isAnnotationPresent(Decrypt.class) && secretKeyConfig.isOpen()) {
Decrypt decrypt = AnnotatedElementUtils.findMergedAnnotation(EnDecrypt.class, Decrypt.class);
if (Objects.nonNull(decrypt) && secretKeyConfig.isOpen()) {
encrypt = true;
decryptAnnotation = methodParameter.getMethodAnnotation(Decrypt.class);
decryptAnnotation = decrypt;
return true;
}
// 此处如果按照原逻辑直接返回encrypt, 会造成一次修改为true之后, 后续请求都会变成true, 在不支持时, 需要做修正
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/cn/shuibo/advice/EncryptResponseBodyAdvice.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package cn.shuibo.advice;

import cn.shuibo.annotation.EnDecrypt;
import cn.shuibo.annotation.Encrypt;
import cn.shuibo.config.SecretKeyConfig;
import cn.shuibo.covert.AbstractResponseCovert;
import cn.shuibo.util.Base64Util;
import cn.shuibo.util.JsonUtils;
import cn.shuibo.util.RSAUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
Expand All @@ -33,6 +35,8 @@ public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {

@Autowired
private SecretKeyConfig secretKeyConfig;
@Autowired
private AbstractResponseCovert responseCovert;

private static ThreadLocal<Boolean> encryptLocal = new ThreadLocal<>();

Expand All @@ -42,7 +46,8 @@ public boolean supports(MethodParameter returnType, Class<? extends HttpMessageC
if (Objects.isNull(method)) {
return encrypt;
}
encrypt = method.isAnnotationPresent(Encrypt.class) && secretKeyConfig.isOpen();
Encrypt mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(EnDecrypt.class, Encrypt.class);
encrypt = Objects.nonNull(mergedAnnotation) && secretKeyConfig.isOpen();
return encrypt;
}

Expand All @@ -59,7 +64,7 @@ public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType
if (encrypt) {
String publicKey = secretKeyConfig.getPublicKey();
try {
String content = JsonUtils.writeValueAsString(body);
String content = responseCovert.covert(body);
if (!StringUtils.hasText(publicKey)) {
throw new NullPointerException("Please configure rsa.encrypt.privatekeyc parameter!");
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/cn/shuibo/annotation/Decrypt.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
* Author:Bobby
* DateTime:2019/4/9 16:45
**/
@Target(ElementType.METHOD)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Decrypt{
public @interface Decrypt {

/**
* 请求参数一定要是加密内容
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/cn/shuibo/annotation/EnDecrypt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cn.shuibo.annotation;

import cn.shuibo.exception.EncryptRequestException;
import org.springframework.core.annotation.AliasFor;

import java.lang.annotation.*;

/**
* 加解密, 一个注解实现{@link Decrypt} 和 {@link EnDecrypt}
* @author imyzt
* @date 2020/07/12
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Decrypt
@Encrypt
public @interface EnDecrypt {

/**
* 请求参数一定要是加密内容
*/
@AliasFor(annotation = Decrypt.class, value = "required")
boolean required() default false;

/**
* 请求数据时间戳校验时间差
* 超过(当前时间-指定时间)的数据认定为伪造
* 注意应用程序需要捕获 {@link EncryptRequestException} 异常
*/
@AliasFor(annotation = Decrypt.class, value = "timeout")
long timeout() default 3000;
}
10 changes: 7 additions & 3 deletions src/main/java/cn/shuibo/annotation/EnableSecurity.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import cn.shuibo.advice.EncryptRequestBodyAdvice;
import cn.shuibo.advice.EncryptResponseBodyAdvice;
import cn.shuibo.config.SecretKeyConfig;
import cn.shuibo.covert.AbstractResponseCovert;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;
Expand All @@ -15,9 +16,12 @@
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import({SecretKeyConfig.class,
@Import({
SecretKeyConfig.class,
EncryptResponseBodyAdvice.class,
EncryptRequestBodyAdvice.class})
public @interface EnableSecurity{
EncryptRequestBodyAdvice.class,
AbstractResponseCovert.class
})
public @interface EnableSecurity {

}
2 changes: 1 addition & 1 deletion src/main/java/cn/shuibo/annotation/Encrypt.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Author:Bobby
* DateTime:2019/4/9 16:45
**/
@Target(ElementType.METHOD)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Encrypt{
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/cn/shuibo/covert/AbstractResponseCovert.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cn.shuibo.covert;

import cn.shuibo.util.JsonUtils;
import org.springframework.stereotype.Component;

/**
* 响应转换实现, 可自定义该接口实现自定义json框架转换Javabean对象
* 考虑到转换时包含enum对象/jsonView等注解的处理, 默认使用Jackson
* @author imyzt
* @date 2020/07/12
*/
@Component
public class AbstractResponseCovert {

/**
* 响应对象为JSON转换
* @param body JavaBean对象
* @return json文本
* @throws Exception 异常信息
*/
public String covert(Object body) throws Exception {
return JsonUtils.writeValueAsString(body);
}
}
5 changes: 5 additions & 0 deletions src/main/java/cn/shuibo/util/JsonUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public static JsonNode getNode(String content, String key) throws IOException {
return jsonNode.get(key);
}

public static boolean hasNode(String content, String key) throws IOException {
JsonNode jsonNode = OBJECT_MAPPER.readTree(content);
return jsonNode.has(key);
}

public static String writeValueAsString(Object body) throws JsonProcessingException {
return OBJECT_MAPPER.writeValueAsString(body);
}
Expand Down