1.Mavne导入加密解密所需的依赖

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-io</artifactId>
  <version>1.3.2</version>
</dependency>

2.在aspect切面包下建立 入参切面与出参切面

DecodeRequestBodyAdvice.class  入参拦截 
流程:获取本次请求->判断是否包含加密/解密注解->对请求字符串解密->重构请求过来的加密字符串为实体类对象
package com.iliebe.web.aspect;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.icbc.api.internal.util.codec.Base64;
import com.iliebe.web.annotation.SecurityParameter;
import com.iliebe.web.constants.SystemConstant;
import com.iliebe.web.util.EncryptForPKCS7Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import org.apache.commons.io.IOUtils; import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.security.NoSuchAlgorithmException; /**
* @Date: 2020/6/27 下午3:59
* @Description: TODO(请求参数加密)
*/
@Slf4j
@ControllerAdvice(basePackages = "com.iliebe.web.controller")
public class DecodeRequestBodyAdvice implements RequestBodyAdvice { @Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
} @Override
public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return body;
} @Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
try {
boolean encode = false;
String apiPath = "";
// 方法是否 包含注解
if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {
//获取注解配置的包含和去除字段
SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);
//入参是否需要解密
encode = serializedField.inDecode();
apiPath = serializedField.apiPath();
}
if (encode) {
return new MyHttpInputMessage(inputMessage,apiPath);
} else {
return inputMessage;
}
} catch (Exception e) {
e.printStackTrace();
log.error("方法method :【{}】参数解密出现异常:{}", methodParameter.getMethod().getName(), e.getMessage());
return inputMessage;
}
} @Override
public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return body;
} class MyHttpInputMessage implements HttpInputMessage { private HttpHeaders headers; private InputStream body; public MyHttpInputMessage(HttpInputMessage inputMessage,String apiPath) throws Exception {
this.headers = inputMessage.getHeaders();
// 获取加密 json中 值
String src = easpString(IOUtils.toString(inputMessage.getBody(), "UTF-8"));
// 解密加密字符串
String decryptStr = null;
if (StringUtils.isBlank(apiPath)){
decryptStr = EncryptForPKCS7Util.Decrypt(src, SystemConstant.AES_KEY);
}
if (StringUtils.isBlank(decryptStr)){
throw new RuntimeException("参数【requestData】解密异常!");
}
this.body = IOUtils.toInputStream(decryptStr, "UTF-8");
} @Override
public InputStream getBody() {
return body;
} @Override
public HttpHeaders getHeaders() {
return headers;
} /**
* 获取requestData数据
*/
public String easpString(String requestData) {
if (StrUtil.isNotEmpty(requestData)) {
JSONObject jsonObject = new JSONObject(requestData);
String requestDataStr = jsonObject.getStr("requestData");
if (StrUtil.isBlank(requestDataStr)) {
throw new RuntimeException("参数【requestData】缺失异常!");
}
return requestDataStr;
}
return "";
}
} public static String genAesSecret(){
try {
KeyGenerator kg = KeyGenerator.getInstance("AES");
//下面调用方法的参数决定了生成密钥的长度,可以修改为128, 192或256
kg.init(128);
SecretKey sk = kg.generateKey();
byte[] b = sk.getEncoded();
String secret = Base64.encodeBase64String(b);
return secret;
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("没有此算法");
}
} public static void main(String[] args) {
System.out.println(EncryptForPKCS7Util.Decrypt("jH+iZFzwlWyStzAfHaLW4eQ2MoStfSAg0hsXk6miSvfm7C+oRED1zSI1uT0wkYbX4Q0YeVqA7JM/kxC0kSrOJA3v3mByGBo8DmlS349DvpY=", "Q6Oxh8pknkr3/B+0ukKVqg=="));
} }
EncodeResponseBodyAdvice.class 出参拦截
流程:获取本次响应->判断是否包含加密/解密注解->对响应数据加密->返回响应数据为加密后的字符串
package com.iliebe.web.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.iliebe.web.annotation.SecurityParameter;
import com.iliebe.web.constants.SystemConstant;
import com.iliebe.web.util.EncryptForPKCS7Util;
import com.iliebe.web.util.JsonConfig;
import lombok.extern.slf4j.Slf4j; import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; /**
* @Date: 2020/6/27 下午3:59
* @Description: TODO(返回数据解密)
*/
@Slf4j
@ControllerAdvice(basePackages = "com.iliebe.web.controller")
public class EncodeResponseBodyAdvice implements ResponseBodyAdvice { @Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
} @Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
boolean encode = false;
if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {
//获取注解配置的包含和去除字段
SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);
//出参是否需要加密
encode = serializedField.outEncode();
}
if (encode) {
ObjectMapper objectMapper = JsonConfig.getObjectMapper();
try {
String result = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(body);
return EncryptForPKCS7Util.Encrypt(result, SystemConstant.AES_KEY);
} catch (Exception e) {
e.printStackTrace();
log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());
}
}
return body;
}
}

3.加密注解,加在controller层中需要加密/解密的接口

SecurityParameter 
package com.iliebe.web.annotation;

import org.springframework.web.bind.annotation.Mapping;

import java.lang.annotation.*;

/**
* @Date: 2020/6/27 下午3:59
* @Description: TODO(参数加密注解)
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Mapping
@Documented
public @interface SecurityParameter { /**
* 入参是否解密,默认解密
*/
boolean inDecode() default true; /**
* 出参是否加密,默认加密
*/
boolean outEncode() default true; /**
* 方法路径名
*/
String apiPath() default "";
}

4.配置类 配置Json数据编码为UTF-8

package com.iliebe.web.util;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.context.annotation.Configuration; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; /**
* 配置返回的json字符 key为字符串时null则不返回
*
* @Date: 2019/5/13 13:45
*/
@Configuration
public class JsonConfig { /**
* 网络层需要标识gbk,utf-8
* @return
*/
public static ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
JavaTimeModule timeModule = new JavaTimeModule();
timeModule.addDeserializer(LocalDate.class,
new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
timeModule.addSerializer(LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(timeModule);
return objectMapper;
}
}

5.加密/解密方法

package com.iliebe.web.util;

import org.apache.commons.io.IOUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64; /**
* @Date: 2020/6/27 下午3:59
* @Description: TODO()
*/
public class EncryptForPKCS7Util { private static final String SECRET = "AES";
private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding"; static {
Security.addProvider(new BouncyCastleProvider());
}

/**
* AES加密ECB模式PKCS7Padding填充方式
*
* @param str 字符串
* @param key 密钥
* @return 加密字符串
* @throws Exception 异常信息
*/
public static String Encrypt(String str, String key) {
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, SECRET));
byte[] doFinal = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));
return new String(Base64.getEncoder().encode(doFinal));
} catch (Exception e) {
e.printStackTrace();
}
return "";
} /**
* AES解密ECB模式PKCS7Padding填充方式
*
* @param str 字符串
* @param key 密钥
* @return 解密字符串
* @throws Exception 异常信息
*/
public static String Decrypt(String str, String key) {
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, SECRET));
byte[] doFinal = cipher.doFinal(Base64.getDecoder().decode(str));
return new String(doFinal);
} catch (Exception e) {
e.printStackTrace();
}
return "";
} }

注意事项:

在Web项目中如果使用了全局异常拦截器,需要在全局异常捕获的地方也处理,如果是只针对某端的请求处理,可以通过请求header中加入标识,再根据标识判断是否加密/解密。

最新文章

  1. React阶段开发总结
  2. C# 调用网易“易盾” Web API
  3. 【UER #1】[UOJ#12]猜数 [UOJ#13]跳蚤OS [UOJ#14]DZY Loves Graph
  4. Spring Boot 环境变量读取 和 属性对象的绑定
  5. UI学习笔记---第十四天数据持久化
  6. struts2请求过程源码分析(转)
  7. 网页 HTML
  8. 手把手教你用vue-cli搭建vue项目
  9. [QNAP crontab 定時執行程式
  10. [转]GO 开发rest api 接口
  11. Windows Community Toolkit 3.0 - InfiniteCanvas
  12. Ganglia 入门介绍及解决fsockopen error: Connection refused问题
  13. X86和X64环境下的基本类型所占用的字节大小
  14. nodejs 最受欢迎的orm sequelize
  15. curl_multi_select解决curl_multi网页假死问题
  16. Jquery操作select选项集合,判断集合中是否存在option
  17. 查看webdriver针对浏览器的一些函数
  18. http://blog.csdn.net/steveguoshao/article/details/38414145
  19. PCI、PCI-x,PCI-E兼容以及他们之间的区别详细图解
  20. .net web api应用遇到的一些问题

热门文章

  1. Hadoop详解(05) – MapReduce
  2. VUE assets里的scss没有引用会被打包进代码里,本地代码和打包后的代码样式不一致解决办法
  3. antd 3.x升4.x踩坑之路~
  4. 我们从 CircleCI 安全事件获得的3个经验教训
  5. 自动化运维:使用Maven与Docker运行Selenium测试
  6. linux-各个目录下重要文件、用户、用户组
  7. 经典this指向问题
  8. 多变量两两相互关系联合分布图的Python绘制
  9. Seleniumweb自动化测试01
  10. 2.4.rpx单位有内置的视图容器组件