MD5验签是一种用于验证数据完整性和防止数据篡改的安全机制。MD5(Message-Digest Algorithm 5)是一种散列算法,它可以将任意长度的消息转换为一个固定长度(通常是128位)的散列值,这个散列值通常被称为“摘要”或“哈希”。
在验签过程中,MD5通常结合私有密钥使用,流程如下:
1.生成签名
将请求头和请求体合并,并去除签名字段(如sign),因为签名本身不应该参与计算。
对合并后的字符串进行排序,通常是按照参数名的字典序排序。
将排序后的字符串与预先设定的密钥(私钥)拼接在一起。
使用MD5算法对拼接后的字符串进行散列,生成一个固定的长度的散列值,这就是签名。
2.发送签名
将生成的签名附加到请求中,通常作为一个参数(如sign)发送给服务器。
3.服务器验证签名
服务器收到请求后,重复上述签名生成的步骤,使用相同的密钥和数据重新计算签名。
比较计算出的签名与请求中携带的签名是否一致。
如果两个签名一致,说明数据在传输过程中没有被篡改;如果不一致,则可能数据已被篡改,或者请求不是来自可信的源。
MD5验签的优点在于其简单和快速,但是MD5算法已经被证明存在碰撞攻击的弱点,即不同的原始数据可能产生相同的MD5散列值。因此,在安全性要求较高的场景中,更推荐使用SHA-256等更安全的散列算法进行验签。
4.简单案列
下面通过一个简单案列来模拟验签流程,首先定义一个实体接收参数
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 这只是一个测试DTO
*/
@Data
public class TestStuDTO {
@ApiModelProperty(value = "姓名")
private String name;
@ApiModelProperty(value = "年龄")
private String age;
@ApiModelProperty(value = "性别")
private String sex;
@ApiModelProperty(value = "地址")
private String address;
@ApiModelProperty(value = "电话")
private String phone;
@ApiModelProperty(value = "key")
private String secretKey;
@ApiModelProperty(value = "MD5签名字符串(长度32)")
private String sign;
}
为了方便数据处理,Maven中用到以下两个依赖
<!-- hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.7</version>
</dependency>
<!-- fastjson序列化 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.0</version>
</dependency>
编写一个接口简单模拟接收客户端数据验签
@ApiOperation(value = "这只是一个验签测试接口")
@PostMapping("/push-test")
public boolean pushTest(@RequestBody TestStuDTO testStuDTO) {
log.info(StrUtil.format("开始调用【测试接口】,接收参数:{}", JSONUtil.toJsonStr(testStuDTO)));
/** 可根据实际规定是否需要对原始数据排序拼接或其他处理 这里只做最基本演示
---------------------
---------------------
**/
JsonMapper jsonMapper = new JsonMapper();
HashMap map = jsonMapper.convertValue(testStuDTO, HashMap.class);
map.remove("sign"); // 移除客户端签名
try {
String jsonStr = jsonMapper.writeValueAsString(map);
String sign = MD5.create().digestHex(jsonStr, StandardCharsets.UTF_8);
System.out.println("服务端产生sign:"+sign);
if (!sign.equals(testStuDTO.getSign())) { // 验签 如果服务端和客户端的签名不一致 则认为数据不正确
return false;
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return true;
}
最后只需要判断客户端传过来的Sign和服务端对数据处理后创建的Sign是否一致即可
注意:在真实环境中,需要客户端和服务端约定好签名的方式(原始数据是否排序处理、大小写处理、拼接密钥处理等)