基于springboot防篡改拦截器
定义注解类,在类中添加注解便可以对类进行防篡改拦截处理
@Target({java.lang.annotation.ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TamperProofField {
/**
* 固定签字段
*/
String value() default "";
}
首先,创建动态签类用来对传递参数进行加密,生成签名用于拦截器的判断验证。得到的签名在前台赋值给turn_field_sign参数来传递。
@RestController
@RequestMapping({"/fieldSign"})
public class FieldSignAction extends BaseAction {
@RequestMapping({"/getFieldSign"})
public ResponseVO getPublicKey(HttpServletRequest request, @RequestBody Map<String, Object> reqMap) throws Exception {
Map<String,Object> redisMap=new HashMap<>();
for(Entry<String, Object> entry:reqMap.entrySet()) {
String field=entry.getKey();
Object value=entry.getValue();
if(TamperProofConstants.LOCATION_URL.equals(field)||"_".equals(field)|| TamperProofConstants.ALL_TURN_FIELD.equals(field)||TamperProofConstants.GRID_ROWSIGN.equals(field)){
continue;
}
if("undefined".equals(value)||"null".equals(value)){
value="";
}
redisMap.put(field, value);
}
String sign=Encode64Util.convertMapToHashCode(redisMap);
HashMap<String, String> resultMap = new HashMap<String, String>();
resultMap.put(TamperProofConstants.SIGN, sign);
return this.createResponse(resultMap,ResponseVO.OK);
}
}
然后,定义防篡改拦截器。在preHandle里对传上来的参数进行处理验证判断是否有被篡改。获取注解类,判断类是否有加注解,没加则不进行拦截。取出不可篡改参数,将他们与传递的参数进行对筛选。
@Component
public class TamperProofInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
TamperProofField fieldInit = ((HandlerMethod) handler).getMethod().getAnnotation(TamperProofField.class);
//查看是否有加注解 没加注解不进行拦截
if(fieldInit==null) {
return true;
}
//获取不可篡改字段
String value = fieldInit.value();
Map<String, Object> param = new HashMap<>();
String json = null;
if (request.getMethod().equals(TamperProofConstants.POST)) {
RequestWrapper requestWrapper = new RequestWrapper(request);
json = requestWrapper.getBody();//获取参数
}
if (request.getMethod().equals(TamperProofConstants.GET)) {
param = getAllRequestParam(request);
}
}
......
//对查询的参数通过获取的签名参数进行判断
private boolean checkSign(HttpServletResponse response, String sign, Map<String, Object> param) throws Exception {
param.remove(TamperProofConstants.TURN_FIELD_SIGN);
param.remove(TamperProofConstants.LOCATION_URL);
for(Entry<String, Object> entry:param.entrySet()) {
String field=entry.getKey();
Object urlValue=entry.getValue();
urlValue=FieldSignUtil.trim(urlValue);
String oldValue= Encode64Util.getValueToKey(field, sign);
urlValue=FieldSignUtil.decode(urlValue);
String value= Encode64Util.convertToBase64(urlValue);
//匹配参数值是否被修改
if((!oldValue.equals(value))){
log.error("checkSign参数:"+field+"值被篡改");
this.returnJson(response);
return false;
}
}
return true;
}
}
定义完拦截器之后,对拦截进行注册。在springboot里通过实现WebMvcConfigurer里的addInterceptors方法来实现过滤器的注册。其中addPathPatterns()方法为添加拦截,这里配置成"/**"表示对所有接口都进行拦截。excludePathPatterns()方法为排除拦截,即括号内的路径不进行拦截。
@Configuration
public class ProffConfig implements WebMvcConfigurer {
@Autowired
private TamperProofInterceptor tamperProofInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(tamperProofInterceptor);
registration.addPathPatterns("/**");
registration.excludePathPatterns("/error");
}
}
以下是获取json参数的方法
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = 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 byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
}
处理完之后,返回给后台。由于json提取操作,会导致数据被清空,导致报错。这时便需要一个类进行操作,将去除的数据重新给他塞回去。
public class RequestReplacedFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest) {
requestWrapper = new RequestWrapper((HttpServletRequest) request);
}
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
// 在chain.doFiler方法中传递新的request对象
if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
这个类需要在启动了加上@Bean注解来进行配置
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean httpServletRequestReplacedRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new RequestReplacedFilter());
registration.addUrlPatterns("/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("httpServletRequestReporlacedFilter");
registration.setOrder(1);
return registration;
}
也可以自定义一个配置类,通过spring.factories将类映射出来,使得启动类可以扫描到。如:
## Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jeedev.msdp.itamper.config.StartConfig
最后,在任意的类上添加注解@TamperProofField(value = “xxx”)便可对该类进行防篡改处理,如:
@TamperProofField(value = "id")
public ResponseVO get(@RequestBody Map<String,Object>params) {
......
}