首先,下面的方式只支持post请求,get方式可以自己扩展。每当一个不能鉴权的接口需要被其它服务调用时,如果这个接口会暴漏在公网上,那么这个不能鉴权的接口或者无token的接口就需要换一种方式进行权限验证。通常使用的方法是参数加密。调用方和被调用方参数使用同一种规则加密,匹配成功,则允许请求,匹配失败,则拒绝请求。这种根据参数加密的方式往往能防止请求被其他方拦截后篡改参数,进行非法请求。这种加密有以下几种作用:
1. 加密时可以附带接口请求时的时间戳,这样可以保证一个接口只能在有效期内被访问。
2. 参数加密时可以加密钥串一起再加密,确保参数规则被人识别后,也能篡改参数,如果别人也识别出你的加密规则,但是他没有密钥串,即使加密后也会请求失败。
3. 加密后的密码很难被逆向解密,加密后,即使被拦截,加密串和参数虽然都被非法获取,但是不能修改任意参数再请求,而且请求也会有时间限制。确保了接口的安全性。
看代码:
@Slf4j
public class ParamsEncodeUtil {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("prizeGoodsCode", "prizeGoodsCode");
map.put("prizeGoodsName", "prizeGoodsName");
map.put("prizeGoodsNum", 1);
map.put("userId", 123);
map.put("userName", "userName");
map.put("winPrizeTime", new Date());
map.put("timestamp", System.currentTimeMillis());
String secret = "z123";
System.out.println(createSign(map, secret));
}
public static String createSign(Map map, String secret) {
String secretKEY = MD5Util.md5(secret);
// 排序 true 升序
Set keySet = sortByValue(map.keySet(), true);
map(keySet,map);
// 拼接签名串
StringBuffer signStr = new StringBuffer();
signStr.append(secretKEY);
for (String str : keySet) {
signStr.append(str);
if (map.get(str) != null) {
signStr.append(map.get(str));
}
}
System.out.println("###signStr222= "+signStr.toString());
// 加密 md5(base64(signStr)+secretKEY)
String encode = Base64Utils.base64Encoder(signStr.toString());
String sign = MD5Util.md5(encode + secretKEY);
return sign;
}
public static void map(Set keySet,Map map){
Map m = new HashMap<>();
for (String key : keySet) {
m.put(key,map.get(key));
}
log.info("###keySet= {}", JSON.toJSONString(m));
}
public static Set sortByValue(Set set, final Boolean flag) {
List setList = new ArrayList(set);
Collections.sort(setList, new Comparator() {
@Override
public int compare(String o1, String o2) {
if (flag) {
return o1.toString().compareTo(o2.toString());
} else {
return o2.toString().compareTo(o1.toString());
}
}
});
set = new LinkedHashSet(setList);
return set;
}
}
/**
* MD5工具类
*/
public final class MD5Util {
public static void main(String[] args) {
long t1 = System.currentTimeMillis();
String md5 = md5("20");
System.out.println(md5);
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
}
protected final static Logger LOG = Logger.getLogger(MD5Util.class.getSimpleName());
// 用来将字节转换成 16 进制表示的字符
private static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
'f' };
private static Charset UTF8 = Charset.forName("UTF-8");
private MD5Util() {
throw new RuntimeException("can't init it");
}
public static String md5(String from) {
byte fromByte[] = null;
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
fromByte = from.getBytes(UTF8);
} catch (Throwable e) {
LOG.log(Level.SEVERE, "error while md5 for:" + from, e);
throw new RuntimeException("error while md5 for:" + from, e);
}
byte bs[] = md.digest(fromByte);
char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符,
// 所以表示成 16 进制需要 32 个字符
int k = 0; // 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) { // 从第一个字节开始,对 MD5 的每一个字节
// 转换成 16 进制字符的转换
byte byte0 = bs[i]; // 取第 i 个字节
str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
// >>> 为逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
}
return new String(str);
}
}
/**
* @Description
* @Author: jinglonglong
* @Date:2021-7-16
**/
public class Base64Utils {
public static String base64Encoder(String key) {
BASE64Encoder encoder = new BASE64Encoder();
String encode = encoder.encode(key.getBytes());
return encode;
}
public static String base64Decoder(String encode) {
BASE64Decoder decoder = new BASE64Decoder();
String str = null;
try {
byte[] bytes = decoder.decodeBuffer(encode);
str = new String(bytes);
} catch (IOException e) {
e.printStackTrace();
}
return str;
}
}