基于springboot防篡改拦截器

基于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) {
        ......
      }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值