springboot项目接口加解密_springboot项目 请求接口加密
/**
- 请求流支持多次获取
*/
public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
- 用于缓存输入流
*/
private ByteArrayOutputStream cachedBytes;
public InputStreamHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null) {
// 首次获取流时,将流放入 缓存输入流 中
cacheInputStream();
}
// 从 缓存输入流 中获取流并返回
return new CachedServletInputStream(cachedBytes.toByteArray());
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
- 首次获取流时,将流放入 缓存输入流 中
*/
private void cacheInputStream() throws IOException {
// 缓存输入流以便多次读取。为了方便, 我使用 org.apache.commons IOUtils
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
/**
- 读取缓存的请求正文的输入流
-
- 用于根据 缓存输入流 创建一个可返回的
*/
public static class CachedServletInputStream extends ServletInputStream {
private final ByteArrayInputStream input;
public CachedServletInputStream(byte[] buf) {
// 从缓存的请求正文创建一个新的输入流
input = new ByteArrayInputStream(buf);
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return input.read();
}
}
}
四、加解密注解
1、DecryptRequestBodyAdvice.java(解密切面)
package com.soft.common.advice;
import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.soft.common.annotation.DecryptionAnnotation;
import com.soft.common.constant.CryptoConstant;
import com.soft.common.entity.RequestBase;
import com.soft.common.entity.RequestData;
import com.soft.common.exception.ParamException;
import com.soft.common.util.AESUtil;
import com.soft.entity.User;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import sun.misc.IOUtils;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
@ControllerAdvice
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
/**
- 方法上有DecryptionAnnotation注解的,进入此拦截器
- @param methodParameter 方法参数对象
- @param targetType 参数的类型
- @param converterType 消息转换器
- @return true,进入,false,跳过
*/
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return methodParameter.hasMethodAnnotation(DecryptionAnnotation.class);
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
return inputMessage;
}
/**
- 转换之后,执行此方法,解密,赋值
- @param body spring解析完的参数
- @param inputMessage 输入参数
- @param parameter 参数对象
- @param targetType 参数类型
- @param converterType 消息转换类型
- @return 真实的参数
*/
@SneakyThrows
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 获取request
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
if (servletRequestAttributes == null) {
throw new ParamException(“request错误”);
}
HttpServletRequest request = servletRequestAttributes.getRequest();
// 获取数据
ServletInputStream inputStream = request.getInputStream();
RequestData requestData = objectMapper.readValue(inputStream, RequestData.class);
if (requestData == null || StringUtils.isBlank(requestData.getText())) {
throw new ParamException(“参数错误”);
}
// 获取加密的数据
String text = requestData.getText();
// 放入解密之前的数据
request.setAttribute(CryptoConstant.INPUT_ORIGINAL_DATA, text);
// 解密
String decryptText = null;
try {
decryptText = AESUtil.decrypt(text);
} catch (Exception e) {
throw new ParamException(“解密失败”);
}
if (StringUtils.isBlank(decryptText)) {
throw new ParamException(“解密失败”);
}
// 放入解密之后的数据
request.setAttribute(CryptoConstant.INPUT_DECRYPT_DATA, decryptText);
// 获取结果
Object result = objectMapper.readValue(decryptText, body.getClass());
// 强制所有实体类必须继承RequestBase类,设置时间戳
if (result instanceof RequestBase) {
// 获取时间戳
Long currentTimeMillis = ((RequestBase) result).getCurrentTimeMillis();
// 有效期 60秒
long effective = 60*1000;
// 时间差
long expire = System.currentTimeMillis() - currentTimeMillis;
// 是否在有效期内
/if (Math.abs(expire) > effective) {
throw new ParamException(“时间戳不合法”);
}/
// 返回解密之后的数据
return result;
} else {
throw new ParamException(String.format(“请求参数类型:%s 未继承:%s”, result.getClass().getName(), RequestBase.class.getName()));
}
}
/**
- 如果body为空,转为空对象
- @param body spring解析完的参数
- @param inputMessage 输入参数
- @param parameter 参数对象
- @param targetType 参数类型
- @param converterType 消息转换类型
- @return 真实的参数
*/
@SneakyThrows
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
String typeName = targetType.getTypeName();
Class<?> bodyClass = Class.forName(typeName);
return bodyClass.newInstance();
}
}
2、DecryptionAnnotation.java(解密注解)
package com.soft.common.annotation;
import java.lang.annotation.*;
/**
- @author Administrator
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DecryptionAnnotation {
}
3、EncryptResponseBodyAdvice.java(加密切面)
package com.soft.common.advice;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.soft.common.annotation.EncryptionAnnotation;
import com.soft.common.entity.RequestBase;
import com.soft.common.exception.CryptoException;
import com.soft.common.result.Result;
import com.soft.common.util.AESUtil;
import com.soft.common.util.StringUtils;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.ResponseEntity;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
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;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import java.lang.reflect.Type;
@ControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Result<?>> {
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
ParameterizedTypeImpl genericParameterType = (ParameterizedTypeImpl)returnType.getGenericParameterType();
// 如果直接是Result,则返回
if (genericParameterType.getRawType() == Result.class && returnType.hasMethodAnnotation(EncryptionAnnotation.class)) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。






既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)

还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!
王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。
对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!
【完整版领取方式在文末!!】
93道网络安全面试题



内容实在太多,不一一截图了
黑客学习资源推荐
最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
😝朋友们如果有需要的话,可以联系领取~
1️⃣零基础入门
① 学习路线
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

② 路线对应学习视频
同时每个成长路线对应的板块都有配套的视频提供:

2️⃣视频配套工具&国内外网安书籍、文档
① 工具

② 视频

③ 书籍

资源较为敏感,未展示全面,需要的最下面获取


② 简历模板

因篇幅有限,资料较为敏感仅展示部分资料,添加上方即可获取👆
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

7a99619f9c5b5.png#pic_center)
② 简历模板

因篇幅有限,资料较为敏感仅展示部分资料,添加上方即可获取👆
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-5X7ioC1y-1712548092102)]
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)