赞
踩
使用场景,在接口开发过程中,我们通常不能暴露一个接口给第三方随便调用,要对第三方发来参数进行校验,看是不是具有访问权限,在微信支付接口中也是这个道理,我们要开通微信支付,微信会提供给我们appid(公众账号ID)、mer_id(商户号),appsecret(密钥),然后通过字段拼接,获取签名,发送给微信,微信验证没有问题才会返回正确数据。
注意:MD5验签有两个作用
1. 保证数据在传输过程中不会丢失
2. 通过分配appid、appsecret保证签名只有授权用户可以访问通过
进入正题
第一步. MD5根据appid、appsecret、时间戳生成签名
首先分配参数appid、appsecret
appid自定义,appsecret通过uuid获取
appid:用户标识,每个用户有不同得appid
appsecret: 安全密钥,必须事先分配给接口提供方用于验签
第二步. 根据用户发来数据验签
直接上代码,签名验证公共类
- package com.lf.md5.util;
-
- import lombok.extern.slf4j.Slf4j;
-
- import java.security.MessageDigest;
- import java.util.*;
-
- /**
- * @Title:
- * @Package
- * @Description: 生成有序map,签名,验签
- * @author insistOn
- * @date 2020/3/422:03
- */
- @Slf4j
- public class MD5 {
-
- /**
- * 生成微信支付sign
- * @return
- */
- public static String createSign(SortedMap<String, String> params, String key){
- StringBuilder sb = new StringBuilder();
- Set<Map.Entry<String, String>> es = params.entrySet();
- Iterator<Map.Entry<String,String>> it = es.iterator();
- //生成 stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
- while (it.hasNext()){
- Map.Entry<String,String> entry = (Map.Entry<String,String>)it.next();
- String k = (String)entry.getKey();
- String v = (String)entry.getValue();
- if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){
- sb.append(k+"="+v+"&");
- }
- }
-
- sb.append("key=").append(key);
- String sign = MD5(sb.toString()).toUpperCase();
- return sign;
- }
-
-
- /**
- * 校验签名
- * @param params
- * @param key
- * @return
- */
- public static boolean isCorrectSign(SortedMap<String, String> params, String key){
- String sign = createSign(params,key);
-
- String weixinPaySign = params.get("sign").toUpperCase();
- log.info("通过用户发送数据获取新签名:{}", sign);
- return weixinPaySign.equals(sign);
- }
-
-
- /**
- * 获取有序map
- * @param map
- * @return
- */
- public static SortedMap<String,String> getSortedMap(Map<String,String> map){
-
- SortedMap<String, String> sortedMap = new TreeMap<>();
- Iterator<String> it = map.keySet().iterator();
- while (it.hasNext()){
- String key = (String)it.next();
- String value = map.get(key);
- String temp = "";
- if( null != value){
- temp = value.trim();
- }
- sortedMap.put(key,temp);
- }
- return sortedMap;
- }
-
-
- /**
- * md5常用工具类
- * @param data
- * @return
- */
- public static String MD5(String data){
- try {
-
- MessageDigest md5 = MessageDigest.getInstance("MD5");
- byte [] array = md5.digest(data.getBytes("UTF-8"));
- StringBuilder sb = new StringBuilder();
- for (byte item : array) {
- sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
- }
- return sb.toString().toUpperCase();
-
- }catch (Exception e){
- e.printStackTrace();
- }
- return null;
-
- }
- }
获取签名测试类
- package com.lf.md5.util;
-
- import lombok.extern.slf4j.Slf4j;
- import org.junit.jupiter.api.Test;
-
- import java.util.Date;
- import java.util.SortedMap;
- import java.util.TreeMap;
-
- import static org.junit.jupiter.api.Assertions.*;
-
- /**
- * @author insistOn
- * @Title: 验签测试
- * @Package
- * @Description: 验签作用:
- * 1. 防注数据传输中被串改
- * 2. 指定appid、key、appsecret的用户才能访问接口
- * @date 2020/3/422:08
- */
- @Slf4j
- class MD5Test {
-
- /**
- * 实际项目使用需根据自己需要,提前分配appid和appsecret
- */
- // 随便定义一个值
- private static final String appid = "wx123456789";
-
- // 提前生成一个appsecret用于验签,CommonUtils.generateUUID()
- private static final String appsecret = "7214fefff0cf47d7950cb2fc3b5d670a";
-
- // 定义时间戳, 时间戳作用是防止接口被重复调用,真正使用时,可以验证当时间在20秒内可以访问,其他时间超时
- private static final String time = "1583332804914";
-
- // 把数据按照 首字母排序
- public static SortedMap<String, String> getData(){
- SortedMap<String, String> sortedMap = new TreeMap<>();
- sortedMap.put("str1", "某管理系统");
- sortedMap.put("appid", appid);
- sortedMap.put("timestamp", time);
- return sortedMap;
- }
-
- @Test
- void createSign() {
-
- // 模拟发送端根据appid等数据生成签名
- log.info("用户发送签名:{}", MD5.createSign(getData(), appsecret));
-
- }
- }
执行测试类,打印出签名为:
19:52:13.330 [main] INFO com.lf.md5.util.MD5Test - 用户发送签名:6646BFE4E9DD66FCC096B115230FA577
controller代码如下
- package com.lf.md5.controller;
-
- import com.lf.md5.util.MD5;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
-
- import java.util.Date;
- import java.util.SortedMap;
- import java.util.TreeMap;
-
- /**
- * @author insistOn
- * @Title:
- * @Package
- * @Description:
- * @date 2020/3/618:54
- */
- @RestController
- public class SignController {
-
-
- private static final String appsecret = "7214fefff0cf47d7950cb2fc3b5d670a";
- /**
- * 模拟验签
- * @param appid
- * @param timestamp
- * @param str1 你要穿的参数, 还有其他参数可继续加
- * @param sign 签名
- * @return
- */
- @RequestMapping("sign")
- public String checkSign(@RequestParam String appid,
- @RequestParam String timestamp,
- @RequestParam String str1,
- @RequestParam String sign){
-
- // 1. 校验时间戳
- long t = Long.valueOf(timestamp);
- // 时间查过20秒,则认为接口为重复调用,返回错误信息
- Date date = new Date();
- long nowtime = date.getTime();
- int seconds = (int) ((nowtime - t)/1000);
- // if(seconds > 20)
- // return "接口不允许重复访问!";
-
- // 2. 组装参数
- SortedMap<String, String> sortedMap = new TreeMap<>();
- sortedMap.put("str1", str1);
- sortedMap.put("appid", appid);
- sortedMap.put("timestamp", timestamp);
- sortedMap.put("sign", sign);
-
- // 3. 校验签名
- Boolean flag = MD5.isCorrectSign(sortedMap, appsecret);
- return flag? "签名验证通过" : "签名验证未通过";
- }
- }
注释掉验证时间戳代码,调用验签接口,postman输入以下链接,查看返回信息:
开启时间戳代码,测试时间大于20秒,则不允许访问,返回值如下
结尾:有兴趣的化,可以把appid等信息放数据库,对字段进行单个校验,同时支持多个appid等配置。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。