package com.xxx.util;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* 接口验签
* @author mafei
* @vesion 1.0
* @date 2022/6/27
*/
@Component
public class ApiSignUtil {
private static String DEFAULT_APPID = "appid";
// public static String DEFAULT_SECRET_KEY = "secretKey";
private static String TIME_STAMP_KEY = "timeStamp";
private static String SIGN_KEY = "sign";
//超时时效,超过此时间认为签名过期
private static Long EXPIRE_TIME = 5 * 60 * 1000L;
// 从开放平台获取应用id
private static String APPID = "APPID";
// 从开放平台获取应用秘钥
private static String SECRET_KEY = "SECRET_KEY";
/**
* 生成签名
* @param param 对应实体类参数
* @return
*/
public static String getSignature(Object param) {
ObjectMapper objectMapper = new ObjectMapper();
Map params;
try {
String jsonStr = objectMapper.writeValueAsString(param);
params = objectMapper.readValue(jsonStr, Map.class);
} catch (Exception e) {
throw new RuntimeException("生成签名:转换json失败");
}
// param参数要包含appid
if (params.get(DEFAULT_APPID) == null) {
params.put(DEFAULT_APPID, APPID);
}
// param参数要包含timeStamp
if (params.get(TIME_STAMP_KEY) == null) {
params.put(TIME_STAMP_KEY, System.currentTimeMillis());
}
//对map参数进行排序生成参数
Set<String> keysSet = params.keySet();
Object[] keys = keysSet.toArray();
Arrays.sort(keys);
String temp = Arrays.stream(keys)
.map(key -> key + "=" + (!params.containsKey(key) ? "" : params.get(key)))
.collect(Collectors.joining("&"));
//根据参数生成签名
String sign = DigestUtils.sha256Hex(temp + APPID + SECRET_KEY).toUpperCase();
return sign;
}
/**
* 校验签名,param参数中必须包含appid、timeStamp和sign(签名)
*/
public static boolean checkSignature(OpenProp mrsProp, HttpServletRequest request) throws IOException {
//获取request中的json参数转成map
Map<String, Object> param = getAllParams(request);
String sign = (String) param.get(SIGN_KEY);
String appid = (String) param.get(DEFAULT_APPID);
Long start = convertTimestamp(param.get(TIME_STAMP_KEY));
long now = System.currentTimeMillis();
//校验时间有效性
if (start == null || now - start > EXPIRE_TIME || start - now > 0L) {
return false;
}
//是否携带appid
if (StringUtils.isBlank(appid)) {
return false;
}
//是否携带签名
if (StringUtils.isBlank(sign)) {
return false;
}
//获取除签名外的参数
param.remove(SIGN_KEY);
//校验签名,服务端生成签名后和客户端做对比
String signature = getSignature(mrsProp, param);
if (sign.equals(signature)) {
return true;
}
return false;
}
private static Long convertTimestamp(Object timestampObj) {
return timestampObj != null ? Long.parseLong(timestampObj.toString()) : null;
}
/**
* 将URL的参数和body参数合并
* @param request
*/
public static SortedMap<String, Object> getAllParams(HttpServletRequest request) throws IOException {
SortedMap<String, Object> result = new TreeMap<>();
//获取URL上的参数,并放入结果中
result.putAll(getUrlParams(request));
// get请求和DELETE请求不需要拿body参数
if (!StringUtils.equalsAny(request.getMethod(), HttpMethod.GET.name(), HttpMethod.DELETE.name())) {
Optional.ofNullable(getBodyParams(request))
.ifPresent(result::putAll);
}
return result;
}
/**
* 获取 Body 参数
* @param request
*/
public static Map<String, Object> getBodyParams(final HttpServletRequest request) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
String str = "";
StringBuilder wholeStr = new StringBuilder();
//一行一行的读取body体里面的内容;
while ((str = reader.readLine()) != null) {
wholeStr.append(str);
}
//转化成json对象
return StringUtils.isNoneBlank(wholeStr) ? JSONObject.parseObject(wholeStr.toString(),Map.class) : new HashMap<>();
}
/**
* 将URL请求参数转换成Map
*/
public static Map<String, String> getUrlParams(HttpServletRequest request) {
return Optional.ofNullable(request.getQueryString())
.map(queryStr -> {
try {
return URLDecoder.decode(request.getQueryString(), String.valueOf(StandardCharsets.UTF_8));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
})
.map(params -> Arrays.stream(params.split("&"))
.collect(Collectors.toMap(s -> s.substring(0, s.indexOf("=")), s -> s.substring(s.indexOf("=") + 1))))
.orElse(new HashMap<>());
}
/**
* 将map转换成url参数
* @param map
* @return
*/
public static String getUrlParamsByMap(Map<String,Object> map) {
if (map == null) {
return "";
}
StringBuffer sb = new StringBuffer();
for (Map.Entry<String,Object> entry : map.entrySet()) {
sb.append(entry.getKey() + "=" + entry.getValue());
sb.append("&");
}
String s = sb.toString();
if (s.endsWith("&")) {
s = StringUtils.substringBeforeLast(s, "&");
}
return s;
}
/*public static void test1(){
AppSecretKey askid = apiSignUtil.appSecretKeyService.getByAttr("askid", 1);
System.out.println("askid = " + askid);
}*/
/**
* 生成签名
*/
/*public static Map getSignature(Object param,String appid, String secretKey) {
ObjectMapper objectMapper = new ObjectMapper();
Map params;
try {
String jsonStr = objectMapper.writeValueAsString(param);
params = objectMapper.readValue(jsonStr, Map.class);
} catch (Exception e) {
throw new RuntimeException("生成签名:转换json失败");
}
if (params.get(TIME_STAMP_KEY) == null) {
params.put(TIME_STAMP_KEY, System.currentTimeMillis());
}
//对map参数进行排序生成参数
Set<String> keysSet = params.keySet();
Object[] keys = keysSet.toArray();
Arrays.sort(keys);
String temp = Arrays.stream(keys).map(key -> key + "=" + (!params.containsKey(key) ? "" : params.get(key)))
.collect(Collectors.joining("&"));
//根据参数生成签名
String sign = DigestUtils.sha256Hex(temp + appid + secretKey).toUpperCase();
// params.put(SIGN_KEY, sign);
return params;
}*/
/**
* 校验签名
*/
/*public static boolean checkSignature(HttpServletRequest request,String appid, String secretKey) throws IOException {
//获取request中的json参数转成map
Map<String, Object> param = getAllParams(request);
String sign = (String) param.get(SIGN_KEY);
Long start = convertTimestamp(param.get(TIME_STAMP_KEY));
long now = System.currentTimeMillis();
//校验时间有效性
if (start == null || now - start > EXPIRE_TIME || start - now > 0L) {
return false;
}
//是否携带签名
if (StringUtils.isBlank(sign)) {
return false;
}
//获取除签名外的参数
param.remove(SIGN_KEY);
//校验签名
Map paramMap = getSignature(param, appid,secretKey);
String signature = (String) paramMap.get("sign");
if (sign.equals(signature)) {
return true;
}
return false;
}*/
}