springboot md5加密_SpringBoot之API接口防刷机制

本文介绍了SpringBoot如何实现接口的防刷机制,通过秘钥生成MD5签名,确保请求源的合法性。适用于第三方系统回调接口和App接口请求校验,防止数据在传输过程中的篡改。
摘要由CSDN通过智能技术生成

Springboot接口防刷机制:通过秘钥生成签名,校验请求源合法性,不同源可以设置不同的秘钥

业务场景:

  1. 可用于第三方业务系统回调接口,比如s2s场景下(Server端也可以利用ip白名单,不做签名校验也可以)
  2. 可用于一些App端接口发送请求校验(无token下)

3.利用签名工具类:SignUtil.java

package com.md.demo.util.sign;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
 * 签名工具类
 */
public class SignUtil {
 
	protected static Logger logger = LoggerFactory.getLogger(SignUtil.class);
 
	/**
	 * 算法实现 将参数集合按照参数名的ASCII排列 把排序后的结果按照【参数+参数值 +
	 * &】的方式拼接,再加上secretKey=secretKeyValue
	 * 拼装好的字符串按MD5(p1=v1&p2=v2&p3=v3&secretKey=secretKeyValue)进行md5加密后,转大写
	 * 
	 * @param params    参数集合(必须)
	 * @param secretKey 秘钥(必须)
	 * @return
	 */
	public static String signByMD5(Map<String, Object> params, String secretKey) {
		// 将参数集合按照参数名首字母先后顺序排列
		SortedMap<String, Object> sortParamMap = SignUtil.sortMap(params);
		// 把排序后的结果按照参数+参数值的方式拼接
		// 拼装好的字符串按secretKey进行md5加密后,转大写
		return SignUtil.createSign(sortParamMap, secretKey);
	}
 
	/**
	 * 把排序后的结果按照【参数+参数值 + &】的方式拼接,再加上secretKey=secretKeyValue
	 * 拼装好的字符串按MD5(p1=v1&p2=v2&p3=v3&secretKey=secretKeyValue)进行md5加密后,转大写
	 * 
	 * @param parameters
	 * @param secretKey
	 * @return
	 */
	private static String createSign(Map<String, Object> parameters, String secretKey) {
		StringBuffer sb = new StringBuffer();
		Iterator<Entry<String, Object>> it = parameters.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
			String key = (String) entry.getKey();
			Object value = entry.getValue();
			// 去掉带sign的项
			if (null != value && !"".equals(value) && !"sign".equals(key) && !"secretKey".equals(key)) {
				sb.append(key + "=" + value + "&");
			}
		}
		sb.append("secretKey=" + secretKey);
		// 注意sign转为大写
		return MD5Util.encodeByMD5(sb.toString()).toUpperCase();
	}
 
	/**
	 * 按首字母排列
	 * 
	 * @param map
	 * @return
	 */
	public static SortedMap<String, Object> sortMap(Map<String, Object> map) {
		List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(map.entrySet());
		// 排序
		Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() {
			public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
				// 按首字母比对
				return (String.valueOf(o1.getKey().charAt(0))).compareTo(String.valueOf(o2.getKey().charAt(0)));
			}
		});
		// 排序后
		SortedMap<String, Object> sortmap = new TreeMap<String, Object>();
		// 根据key进行排序ASCII顺序
		System.out.println(infoIds.toString());
		for (int i = 0; i < infoIds.size(); i++) {
			String[] split = infoIds.get(i).toString().split("=");
			if (split.length == 1) {
				sortmap.put(split[0], null);
				continue;
			}
			sortmap.put(split[0], split[1]);
		}
		return sortmap;
	}
 
	public static void main(String[] args) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("name", "minbo");
		params.put("age", 100);
		String secretKey = "996";
		String sign = SignUtil.signByMD5(params, secretKey);
		System.out.println(sign);
	}
}

4. InitRest.java文件

package com.md.demo.rest;
 
import java.util.HashMap;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
import com.md.demo.util.JsonResult;
import com.md.demo.util.ResultCode;
import com.md.demo.util.sign.NetworkUtil;
import com.md.demo.util.sign.SignUtil;
 
/**
 * 
 */
@RestController
public class InitRest {
 
	protected static Logger logger = LoggerFactory.getLogger(InitRest.class);
 
	/**
	 * http://localhost:9090/hello
	 * 
	 * @return
	 */
	@GetMapping("/hello")
	public String hello() {
		return "Hello greetings from spring-boot2-api-protect";
	}
 
	/**
	 * 利用秘钥生成签名(只有对方知,服务器知),校验请求源合法性,不同源可以设置不同的秘钥
	 */
	private static final String API_SECRET_KEY = "996";;
 
	/**
	 * http://localhost:9090/test?name=minbo&age=100&sign=495FC6F52324AB1460C95A27803E3A4A
	 * 
	 * @param name
	 * @param age
	 * @param sign 大写
	 * @return
	 */
	@GetMapping("/test")
	public JsonResult test(String name, Integer age, String sign, HttpServletRequest request) {
		// 1. 还可以在参数中增加一个动态随机字符参数,比如sId,每次请求时,对方都需要动态生成一个十位随机字符,防止sign值一直固化不变
		// 2. 同时,服务器可以校验请求是否重复,比如可以通过redis存储已请求过的rId(可设置过期时间,以免一直存储历史的rId值),防止别人利用固定请求链接刷请求
		// 3. 可以使用公网ip,限制同一个ip访问次数(也可以在nginx层做限制,这个自行网上了解了)
 
//		// 获取公网ip
//		String sIp = NetworkUtil.getIpAddress(request);
//		System.out.println("sIp=" + sIp);
		
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("name", name);
		params.put("age", age);
		String serverSign = SignUtil.signByMD5(params, API_SECRET_KEY);
		if (serverSign.equals(sign)) {
			return new JsonResult(ResultCode.SUCCESS, "签名通过");
		}
		return new JsonResult(ResultCode.SUCCESS_FAIL, "非法请求");
	}
}
package com.md.demo.util.sign;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

public class NetworkUtil {

	/**
	 * 获取用户真实IP地址
	 * 
	 * @param request
	 * @return
	 */
	public static String getIpAddress(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		// 不管转发多少次,取第一位
		return ip.split(",")[0];
	}

	/**
	 * 获取来访者的浏览器版本
	 * 
	 * @param request
	 * @return
	 */
	public static String getRequestBrowserInfo(HttpServletRequest request) {
		String browserVersion = null;
		String header = request.getHeader("user-agent");
		if (header == null || header.equals("")) {
			return "";
		}
		if (header.indexOf("MSIE") > 0) {
			browserVersion = "IE";
		} else if (header.indexOf("Firefox") > 0) {
			browserVersion = "Firefox";
		} else if (header.indexOf("Chrome") > 0) {
			browserVersion = "Chrome";
		} else if (header.indexOf("Safari") > 0) {
			browserVersion = "Safari";
		} else if (header.indexOf("Camino") > 0) {
			browserVersion = "Camino";
		} else if (header.indexOf("Konqueror") > 0) {
			browserVersion = "Konqueror";
		}
		return browserVersion;
	}

	/**
	 * 获取系统版本信息
	 * 
	 * @param request
	 * @return
	 */
	public static String getRequestSystemInfo(HttpServletRequest request) {
		String systenInfo = null;
		String header = request.getHeader("user-agent");
		if (header == null || header.equals("")) {
			return "";
		}
		// 得到用户的操作系统
		if (header.indexOf("NT 6.0") > 0) {
			systenInfo = "Windows Vista/Server 2008";
		} else if (header.indexOf("NT 5.2") > 0) {
			systenInfo = "Windows Server 2003";
		} else if (header.indexOf("NT 5.1") > 0) {
			systenInfo = "Windows XP";
		} else if (header.indexOf("NT 6.0") > 0) {
			systenInfo = "Windows Vista";
		} else if (header.indexOf("NT 6.1") > 0) {
			systenInfo = "Windows 7";
		} else if (header.indexOf("NT 6.2") > 0) {
			systenInfo = "Windows Slate";
		} else if (header.indexOf("NT 6.3") > 0) {
			systenInfo = "Windows 9";
		} else if (header.indexOf("NT 5") > 0) {
			systenInfo = "Windows 2000";
		} else if (header.indexOf("NT 4") > 0) {
			systenInfo = "Windows NT4";
		} else if (header.indexOf("Me") > 0) {
			systenInfo = "Windows Me";
		} else if (header.indexOf("98") > 0) {
			systenInfo = "Windows 98";
		} else if (header.indexOf("95") > 0) {
			systenInfo = "Windows 95";
		} else if (header.indexOf("Mac") > 0) {
			systenInfo = "Mac";
		} else if (header.indexOf("Unix") > 0) {
			systenInfo = "UNIX";
		} else if (header.indexOf("Linux") > 0) {
			systenInfo = "Linux";
		} else if (header.indexOf("SunOS") > 0) {
			systenInfo = "SunOS";
		}
		return systenInfo == null ? header : systenInfo;
	}

	/**
	 * 获取来访者的主机名称
	 * 
	 * @param ip
	 * @return
	 */
	public static String getHostName(String ip) {
		InetAddress inet;
		try {
			inet = InetAddress.getByName(ip);
			return inet.getHostName();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		return "";
	}

	/**
	 * 命令获取mac地址
	 * 
	 * @param cmd
	 * @return
	 */
	private static String callCmd(String[] cmd) {
		String result = "";
		String line = "";
		try {
			Process proc = Runtime.getRuntime().exec(cmd);
			InputStreamReader is = new InputStreamReader(proc.getInputStream());
			BufferedReader br = new BufferedReader(is);
			while ((line = br.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * @param cmd     第一个命令
	 * @param another 第二个命令
	 * @return 第二个命令的执行结果
	 */
	private static String callCmd(String[] cmd, String[] another) {
		String result = "";
		String line = "";
		try {
			Runtime rt = Runtime.getRuntime();
			Process proc = rt.exec(cmd);
			proc.waitFor(); // 已经执行完第一个命令,准备执行第二个命令
			proc = rt.exec(another);
			InputStreamReader is = new InputStreamReader(proc.getInputStream());
			BufferedReader br = new BufferedReader(is);
			while ((line = br.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * @param ip           目标ip,一般在局域网内
	 * @param sourceString 命令处理的结果字符串
	 * @param macSeparator mac分隔符号
	 * @return mac地址,用上面的分隔符号表示
	 */
	private static String filterMacAddress(final String ip, final String sourceString, final String macSeparator) {
		String result = "";
		String regExp = "((([0-9,A-F,a-f]{1,2}" + macSeparator + "){1,5})[0-9,A-F,a-f]{1,2})";
		Pattern pattern = Pattern.compile(regExp);
		Matcher matcher = pattern.matcher(sourceString);
		while (matcher.find()) {
			result = matcher.group(1);
			if (sourceString.indexOf(ip) <= sourceString.lastIndexOf(matcher.group(1))) {
				break; // 如果有多个IP,只匹配本IP对应的Mac.
			}
		}
		return result;
	}

	/**
	 * @param ip 目标ip
	 * @return Mac Address
	 */
	private static String getMacInWindows(final String ip) {
		String result = "";
		String[] cmd = { "cmd", "/c", "ping " + ip };
		String[] another = { "cmd", "/c", "arp -a" };
		String cmdResult = callCmd(cmd, another);
		result = filterMacAddress(ip, cmdResult, "-");
		return result;
	}

	/**
	 * @param ip 目标ip
	 * @return Mac Address
	 */
	private static String getMacInLinux(final String ip) {
		String result = "";
		String[] cmd = { "/bin/sh", "-c", "ping " + ip + " -c 2 && arp -a" };
		String cmdResult = callCmd(cmd);
		result = filterMacAddress(ip, cmdResult, ":");
		return result;
	}

	/**
	 * 获取MAC地址
	 * 
	 * @return 返回MAC地址
	 */
	public static String getMacAddress(String ip) {
		String macAddress = "";
		macAddress = getMacInWindows(ip).trim();
		if (macAddress == null || "".equals(macAddress)) {
			macAddress = getMacInLinux(ip).trim();
		}
		return macAddress;
	}
}

访问接口:http://localhost:9090/test?name=minbo&age=100&sign=495FC6F52324AB1460C95A27803E3A4A

b0cd7946ea649d987354fa6016ba3991.png

如果值在传输过程中有变动过,则会签名值失败:

1286b1b5da915484924f0c92825f4b7d.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值