对json接口进行加密解密, 这里实现的方式是 解密使用自定义过滤器
1、启动类加注解@ServletComponentScan
@SpringBootApplication
@ServletComponentScan
public class TestWebApplication {
public static void main(String[] args) {
SpringApplication.run(TestWebApplication.class, args);
}
}
2、创建一个自定义Filter
/**
* 解密过滤器
*/
@Order(1)
@WebFilter({"/*"})
public class DecodeFilter extends OncePerRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(DecodeFilter.class);
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException {
String contentType = req.getContentType();
String url = req.getRequestURI();
//判断是否是json形式请求
if (StringUtils.isNotBlank(contentType) && contentType.contains("application/json")) {
//获取request的请求数据
BufferedReader streamReader = new BufferedReader(new InputStreamReader(req.getInputStream(), "UTF-8"));
StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = streamReader.readLine()) != null)
responseStrBuilder.append(inputStr);
//请求json
JSONObject jsonObject = JSONObject.parseObject(responseStrBuilder.toString());
//是否加密, 是则进行解密
if (jsonObject != null && StringUtils.isNotBlank(jsonObject.getString("isEnc"))
&& jsonObject.getString("isEnc").equals("Y")) {
//获取request的请求数据
String bodyInfoEn = jsonObject.getString("content");
//解密密钥
String sKey = jsonObject.getString("sKey");
if (StringUtils.isNotEmpty(bodyInfoEn)) {
logger.info("参数解密前: {}", bodyInfoEn);
//解密后字符串
String bodyInfoDe = AESOperator.getInstance().decrypt(bodyInfoEn, sKey);
logger.info("url: {}, 解密后: {}", req.getRequestURI(), bodyInfoDe);
if (StringUtils.isNotEmpty(bodyInfoDe)) {
//由于request请求没有修改参数的权限,使用篡改后的request代替原先的
BodyRequestWrapper requestWrapper = new BodyRequestWrapper(req, bodyInfoDe);
chain.doFilter(requestWrapper, res);
return;
}
}
//如果content不存在则返回错误信息
res.setCharacterEncoding("UTF-8");
res.setContentType("application/json; charset=utf-8");
PrintWriter out = res.getWriter();
try {
//加密返回
String encrypt = AESOperator.getInstance().encrypt(("缺少必要参数"), AESOperator.skey);
out.print(JSONObject.toJSONString(new ApiResult<>(encrypt)));
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
} else {
//非加密 直接请求
String param = jsonObject.toJSONString();
BodyRequestWrapper requestWrapper = new BodyRequestWrapper(req, param);
chain.doFilter(requestWrapper, res);
}
} else {
//过滤请求
chain.doFilter(req, res);
}
}
}
篡改request
public class BodyRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
public BodyRequestWrapper(HttpServletRequest request, String json) throws IOException {
super(request);
body = json.getBytes();
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
* 在使用@RequestBody注解的时候,其实框架是调用了getInputStream()方法,所以我们要重写这个方法
*
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
}
使用AES 加密解密
/**
* AES 是一种可逆加密算法,对用户的敏感信息加密处理 对原始数据进行AES加密后,在进行Base64编码转化;
*/
public class AESOperator {
/*
* 加密用的Key 可以用26个字母和数字组成 此处使用AES-128-CBC加密模式,key需要为16位。
*/
public static final String skey = "smkldospdosldaaa";//key,可自行修改
private String ivParameter = "1016449182158477";//偏移量,可自行修改
private static AESOperator instance = null;
private AESOperator() {
}
public static AESOperator getInstance() {
if (instance == null)
instance = new AESOperator();
return instance;
}
public static String Encrypt(String encData, String secretKey, String vector) throws Exception {
if (secretKey == null) {
return null;
}
if (secretKey.length() != 16) {
return null;
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] raw = secretKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
IvParameterSpec iv = new IvParameterSpec(vector.getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(encData.getBytes("utf-8"));
return new BASE64Encoder().encode(encrypted);// 此处使用BASE64做转码。
}
// 加密
public String encrypt(String sSrc, String sKey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] raw = sKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
return replace(new BASE64Encoder().encode(encrypted));// 此处使用BASE64做转码。
}
// 解密
public String decrypt(String sSrc, String sKey) {
try {
byte[] raw = sKey.getBytes("ASCII");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "utf-8");
return originalString;
} catch (Exception ex) {
return null;
}
}
public String decrypt(String sSrc, String key, String ivs) throws Exception {
try {
byte[] raw = key.getBytes("ASCII");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(ivs.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "utf-8");
return originalString;
} catch (Exception ex) {
return null;
}
}
public static String encodeBytes(byte[] bytes) {
StringBuffer strBuf = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
strBuf.append((char) (((bytes[i] >> 4) & 0xF) + ((int) 'a')));
strBuf.append((char) (((bytes[i]) & 0xF) + ((int) 'a')));
}
return strBuf.toString();
}
/**
* 去除 换行符、制表符
* @param str
* @return
*/
public String replace(String str) {
if (!StringUtil.isEmpty(str)) {
return str.replaceAll("\r|\n", "");
}
return str;
}
public static void main(String[] args) throws Exception {
// 需要加密的字串
String cSrc = "{\"loginName\":\"test\",\"secret\":\"123456\"}";
// 加密
String enString = AESOperator.getInstance().encrypt(cSrc, skey);
System.out.println("加密后的字串是:" + enString);
// 解密
String DeString = AESOperator.getInstance().decrypt(enString, skey);
System.out.println("解密后的字串是:" + DeString);
}
3、这里加密返回用aop
<!-- AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Aspect // 表示该类是一个通知类
@Configuration //spring注解方式bean注入 交给spring管理
public class ResultAspect {
private static Logger logger = LoggerFactory.getLogger(ResultAspect.class);
// 定义切点Pointcut
@Pointcut("execution(* com.test.controller.api..*(..))")
public void excudeService() {
}
@Around("excudeService()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
// result的值就是被拦截方法的返回值
Object result = pjp.proceed();
logger.info("请求结束,controller的返回值是 " + JSONObject.toJSONString(result));
String contentType = request.getContentType();
String url = request.getRequestURI();
String sKey = AESOperator.skey;
logger.info("加密返回:url = {} skey = {}", url, sKey);
String encrypt = AESOperator.getInstance().encrypt(JSONObject.toJSONString(result), sKey);
return new ApiResult<>(encrypt);
}
}
测试请求:
{
"isEnc": "Y",
"content": "n7XJKiXTxMOBBwaSIRLtbJPtLy6tZ4AsVPimyOctSzglYO4pmQng4lb9wVnjmihb",
"sKey": "smkldospdosldaaa"
}