Spring MVC接口防数据篡改和重复提交

网友投稿 668 2023-07-28

Spring MVC接口防数据篡改和重复提交

Spring MVC接口防数据篡改和重复提交

本文实例为大家分享了Spring MVC接口防数据篡改和重复提交的具体代码,供大家参考,具体内容如下

一、自定义一个注解,此注解可以使用在方法上或类上

使用在方法上,表示此方法需要数据校验

使用在类上,表示此类下的所有方法需要数据校验

此注解对无参数方法不起作用

import org.springframework.stereotype.Component;

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Component

public @interface DataValidate {

}

二、自定义拦截,拦截前端所有请求

1、检查此接口调用的方法或方法所在的类是否使用了DataValidate注解,若没有使用,表示此接口不需要校验数据;

2、若使用了注解,再检查此方法有没有入参,若没有入参,不需要校验数据,否在需要校验;

3、把前端传来的所有参数 (除了签名参数)按照参数升序生成一个json字符串(使用TreeMap方式自动排序);

4、把生成的json字符串通过MD5加密的结果和前端传的签名值对比,若不相等,表示此数据被篡改过,否在没有被篡改过;

5、数据是否被篡改校验完毕,若前端传了用户唯一标示(token),表示需要校验数据是否重复提交;

6、若签名和上次提交的数据的签名相等,表示是重复提交数据,若不相等,把签名保存下来,表示数据不是重复提交。

import java.security.MessageDigest;

import java.util.Map;

import java.util.Set;

import java.util.TreeMap;

import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PreDestroy;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.core.MethodParameter;

import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.fastjson.JSON;

/**

* 防数据被篡改和重复提交

*/

public class DataValidateInterceptor extends HandlerInterceptorAdapter implements Runnable {

public static Map userToken = new ConcurrentHashMap<>();

// 过期时间

private static long EXPIRED_TIME = 3600000;

private static String TOKEN_NAME = "token";

private static String SIGN_NAME = "sign";

private volatile boolean shutDown;

public DataValidateInterceptor(@Value("${data_interceptor.expired_time}") String expiredTime,

@Value("${data_interceptor.token_name}") String tokenName,

@Value("${data_interceptor.sign_name}") String signName) {

if (null != expiredTime && !"".equals(expiredTime)) {

EXPIRED_TIME = Long.parseLong(expiredTime);

}

if (null != tokenName && !"".equals(tokenName)) {

TOKEN_NAME = tokenName;

}

if (null != signName && !"".equals(signName)) {

SIGN_NAME = signName;

}

}

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

if (validate(request, response, handler)) {

/**

* 实现返回提示数据

*/

response.setContentType("application/json");

response.setCharacterEncoding("UTF-8");

response.getWriter().write("参数验证失败!");

return false;

}

return true;

}

private boolean validate(HttpServletRequest request, HttpServletResponse response, Object handler) {

if (handler instanceof HandlerMethod) {

Class> clazz = ((HandlerMethod) handler).getBeanType();

DataValidate dataValidatehttp:// = clazz.getAnnotation(DataValidate.class);

if (null == dataValidate) {

dataValidate = ((HandlerMethod) handler).getMethodAnnotation(DataValidate.class);

}

if (dataValidate != null) {

MethodParameter[] methodParameters = ((HandlerMethod) handler).getMethodParameters();

if (null == methodParameters || methodParameters.length <=0) {

// 方法没有入参不需要校验

return false;

}

// 需要校验

String sign = request.getParameter(SIGN_NAME);

Map params = request.getParameterMap();

Set paramNames = params.keySet();

Map paramsMap = new TreeMap<>();

for (String paramName : paramNames) {

if (paramName.equals(SIGN_NAME)) {

continue;

}

paramsMap.put(paramName, request.getParameter(paramName));

}

String paramString = JSON.toJSONString(paramsMap).replaceAll(" ", "");

String MD5Sign = MD5.getMD5(paramString);

if (!sign.equals(MD5Sign)) {

// 数据被篡改

return true;

}

String token = request.getParameter(TOKEN_NAME);

if (token != null) {

if (userToken.containsKey(token)) {

TokenValue tokenValue = userToken.get(token);

if (tokenValue.getValue().equals(sign)) {

// 数据已经提交过

return true;

} else {

tokenValue.setValue(sign);

}

} else {

userToken.put(token, new TokenValue(sign));

}

}

}

}

return false;

}

@Override

public void run() {

try {

while (!shutDown) {

synchronized (this) {

wait(EXPIRED_TIME);

Set keys = userToken.keySet();

for (String key : keys) {

if ((userToken.get(key).getExpiredTime() + EXPIRED_TIME) <= System.currentTimeMillis()) {

userToken.remove(key);

}

}

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

@PreDestroy

public void custDestroy() {

shutDown = true;

synchronized (this) {

notifyAll();

}

}

private static class MD5 {

/**

* 向getMD5方法传入一个你需要转换的原始字符串,将返回字符串的MD5码

*

* @param code 原始字符串

* @return 返回字符串的MD5码

*/

private static String getMD5(String code) {

try {

MessageDigest messageDigest = MessageDigest.getInstance("MD5");

byte[] bytes = code.getBytes();

byte[] results = messageDigest.digest(bytes);

StringBuilder stringBuilder = new StringBuilder();

for (byte result : results) {

// 将byte数组转化为16进制字符存入stringbuilder中

stringBuilder.append(String.format("%02x", result));

}

return stringBuilder.toString();

} catch (Exception e) {

e.printStackTrace();

return "";

}

}

}

}

public class TokenValue {

private String value;

private long expiredTime;

public TokenValue(String value) {

this.value = value;

this.expiredTime = System.currentTimeMillis();

}

public String getValue() {

return value;

}

public void setValue(String value) {

this.value = value;

this.expiredTime = System.currentTimeMillis();

}

public long getExpiredTime() {

return expiredTime;

}

}

三、使用

后端使用:

1.在需要数据校验的方法或类上使用DataValidate注解(若在类上使用,表示此类下的所有方法需要验证)

2.配置 DataValidateInterceptor -

3.配置前端签名参数,默认是sign

4.配置用户唯一标示参数,默认是token(防止数据重复提交需要)

5.配置用户唯一标示过期时间,默认是1h,单位是ms(防止数据重复提交需要)

前端使用:

1.获取用户唯一标示(防止数据重复提交需要)

2.把需要提交的数据根据参数(包括用户唯一标示)升序排序然后生成一个json字符串(不能有空格),最后把json字符串进行MD5加密生成32位小写加密结果签名

eg:需要提交的数据为{messageType: "userQueryWait", companyCode: "test_app", token:"123213213"},排序后生成json字符串 {"companyCode":"test_app","messageType":"userQueryWait","token":"123213213"}, md5生成签名

3.把签名和需要提交的数据一起传到后台

eg:{messageType: "userQueryWait", companyCode: "test_app", token:"123213213", sign:"719bdb1fb769efb68e40440d1628ed5b"}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot实现子类的反序列化示例代码
下一篇:Spring条件注解@Conditional示例详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~