1 背景
当我们对外提供API时,因为API接口不需要经过权限验证,此时如何实现只提供给我们指定的使用方,这就引入了签名验证。
2 思路
使用MD5加密算法来实现,包括如下两步:
1. 在XxRequest中定义一个generateSign(String seed),可以根据transId+seed生产md5的值,然后赋值给XxRequest中成员变量sign。这个seed是我们双方一个约定的字符串。
2. 在控制器XxControler中定义一个verifySign(XxRequest reqeust)函数,函数实现思路:
- 使用XxRequest#getnerateSign生产一个serverSign
- 从XxRequest中获取一个clientSign;
- 比较clientSign和serverSign来进行验证
3 实现
1. XxRequest中定义generaeSign方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import org.apache.commons.codec.digest.DigestUtils; public class TransStateQueryServiceRequest { /** * 订单id */ @NotNull(message = "transId不能为空") String transId; /** * 签名 */ @NotNull(message = "sign不能为空") String sign; /** * 根据transId和seed的值生成md5的值 * * @param seed * @return */ public String generateSign(String seed) { String key = transId + seed; // 生成md5的值 return DigestUtils.md5Hex(key); } ..... getter和setter方法 .... } |
2. 在XxController中定义verifySign(XxRequest reqeust)函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
/** * 外部系统查询订单的状态信息 */ @Controller @RequestMapping("/api") public class TransStateQuerySeriveController { public static final Logger logger = LoggerFactory.getLogger(TransStateQuerySeriveController.class); /** * 使用这个seed来生成sign,这个需要和miss那边使用相同的值 */ public static final String seed = "financeweb@miss"; /** * 根据transId来查询订单状态,这个接口是给miss调用的。 */ @RequestMapping("/queryTransStateByTransId") @ResponseBody public TransStateQueryServiceResponse queryTransStateByTransId(TransStateQueryServiceRequest request) { TransStateQueryServiceResponse response = new TransStateQueryServiceResponse(); logger.info("/service/queryTransState的参数"); try { // 1.签名验证 if (!verifySign(request)) { throw new RuntimeException("签名验证失败"); } // 2.获取订单状态的信息 .... } catch (Exception e) { ......... } return response; } /** * 进行签名校验 * * @param transStateQueryServiceRequest * @return */ private boolean verifySign(TransStateQueryServiceRequest transStateQueryServiceRequest) { // 1.传入的transId为空或者sign为空,则返回fasle if (StringUtils.isBlank(transStateQueryServiceRequest.getTransId()) || StringUtils.isBlank(transStateQueryServiceRequest.getSign())) { return false; } // 2.生成服务器端签名和获取客户端签名 String serverSign = transStateQueryServiceRequest.generateSign(seed); String clientSign = transStateQueryServiceRequest.getSign(); // 3.进行校验 if (!serverSign.equalsIgnoreCase(clientSign)) { logger.warn("查询订单状态签名验证失败,请求transId={},sigh={},生成的sign={}", transStateQueryServiceRequest.getTransId(), clientSign, serverSign); return false; } return true; } } |
4 关于Md5
4.1 比较DigestUtils中md5和md5Hex
1. 源码中函数定义
1 2 3 4 5 |
// alculates the MD5 digest and returns the value as a 16 element byte[]. md5(String data); // Calculates the MD5 digest and returns the value as a 32 character hex string. md5Hex(String data) |
2. 代码举例
(1)pom.xml
1 2 3 4 5 |
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.6</version> </dependency> |
(2)代码
1 2 3 4 5 6 7 8 9 10 11 12 |
import org.apache.commons.codec.digest.DigestUtils; /** * Created by wuzhonghu on 16/10/13. */ public class LearnDigestMd5 { public static void main(String[] args){ String key = "124344bc3323finaceweb@miss"; String md5 = new String(DigestUtils.md5(key)); String md5hex = DigestUtils.md5Hex(key); System.out.println("md5="+md5+";md5hex="+md5hex); } } |
执行结果为
由上面执行结果可以看出md5和md5hex的区别为
- md5生成是16位的byte[]
- md5hex生成是32位字符串
4.2 MD5 是否唯一
答案:不唯一。
参考维基百科:https://zh.wikipedia.org/wiki/MD5
1 |
2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证 |
4.3 MD5是否可以解密
MD5加密原理是散列算法,散列算法也称哈希算法。
比如对于一个key,通过hash算法得到value,但是对于一个value是无法推出key的,因为不同的key对应同一个value。所以md5不能解密。
那么我们怎么验证密码呢?就是因为同一密码加密后一定相同。
5 MD5其他作用
1、验证文件完整性,可以对文件进行md5编码。
其他算法
(全文完)