此文内容若含有机密信息,请不要擅自使用相关信息或转发给任何人,不然需要负法律责任,并请告知相关人及时删除并修改机密信息,谢谢。
一、简介
我们大家在开发的过程中,相信大家很多人都对接过第三方接口,但这一般都是作为用户调用第三方,那如果作为第三方开发对外接口供别人调用,那如何写呢,通常我们会约定好验签规则,有MD5、RSA和RSA2等,
这里我们用的是支付宝的RSA2验签方法,支付宝支付它是在商户和支付宝端各自都生成了私钥和公钥,也就是2对密钥,商户用商户的私钥加密,支付宝去用商户的公钥解密;支付宝用支付宝的私钥加密,商户用支付宝的公钥解密;
而这里我们针对每个用户各自分别只生成一对密钥,也就是类似上面的一对商户的密钥,如果我们需要开发回调客户的接口,我们可能就需要另外像支付宝一样另外再针对第三方生成一对密钥,当然这个看各自的需要,回调用之前的同一对密钥也可以。
二、支付宝的密钥生成工具
大家可以到支付宝支付官网下载,目前的地址如下
https://opendocs.alipay.com/open/00uk9e
说明文档
https://opensupport.alipay.com/support/helpcenter/207/201602471154?ant_source=antsupport
https://opensupport.alipay.com/support/helpcenter/207/201602487431?ant_source=antsupport

每个用户各自根据这个工具生成密钥,把公钥给我们,我们后期用这个公钥验签
三、验签
签名和验签规则和支付宝一样,就是将参数key=value形式排序拼接
通常用户调用第三方接口,接口的开发其实和我们的日常开发一样的,主要是验签,也就是在请求到接口之前,对用户的参数进行校验,这里就注解的方式,类似拦截器获取用户的参数解析进行验签,当验签通过,则才能请求Controller里面的接口
我们新建一个springboot的项目



后面下一步完成即可
新建如下目录文件,除了controller目录下是为了测试验签,其他都是验签相关的内容

pom.xml文件里面添加支付宝包引用

具体代码如下
import java.lang.annotation.*;/*** 接口签名验签注解** @author*/@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Signature {}
import com.example.signtest.utils.SignUtil;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import java.util.HashMap;import java.util.Map;/*** 数据过滤处理** @author*/@Aspect@Componentpublic class SignatureAspect {private static final String APPID = "6";@Resourceprivate HttpServletRequest request;// 配置织入点@Pointcut("@annotation(com.example.signtest.aspect.Signature)")//改成全局Api签名可以修改这里包内容public void dataScopePointCut() {}@Around("dataScopePointCut()")public Object doBefore(ProceedingJoinPoint point) throws Throwable {return handleAuth(point);}protected Object handleAuth(final ProceedingJoinPoint joinPoint) {try {// 供应商的id,验证用户的真实性String appid = request.getHeader("appid");// 请求发起的时间String timestamp = request.getHeader("timestamp");// 随机数String nonce = request.getHeader("nonce");// 签名算法生成的签名String sign = request.getHeader("sign");if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(timestamp) || StringUtils.isEmpty(nonce) || StringUtils.isEmpty(sign)) {return "请求头缺少授信参数";}// 限制为(含)60秒以内发送的请求long time = 60;long now = System.currentTimeMillis() / 1000;if (now - Long.parseLong(timestamp) > time) {return "请求发起时间超过服务器限制时间";}// 校验appidif (!APPID.equalsIgnoreCase(appid)) {return "appid参数错误";}Map<String,String> signObj = new HashMap<>(3);signObj.put("appid", appid);signObj.put("timestamp", timestamp);signObj.put("nonce", nonce);signObj.put("sign", sign);//这里验签的公钥和私钥固定值了,你们设计可以改成根据APPID从表中来查询用户的公钥if(!SignUtil.rsaCheck(signObj,"")){return "验签错误";}try {return joinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}} catch (Exception e) {e.printStackTrace();return "解析请求参数异常";}return null;}}
import com.alipay.api.AlipayApiException;import com.alipay.api.internal.util.AlipaySignature;import java.util.Map;/*** 签名工具类** @author :* @since :**/public class SignUtil {public static String getSign(Map<String, String> data, String secret,String type){if(type.equals("rsa") && type.equals("md5")){return null;}if(type.equals("rsa")){return getRSASign(data, secret);}if(type.equals("md5")){return null;}return null;}/*** 获取签名信息* @param data* @param secret* @return*/public static String getRSASign(Map<String, String> data, String secret) {String sign = null;try {String strSign=AlipaySignature.getSignContent(data);sign = AlipaySignature.rsa256Sign(strSign, "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDV/SESiXGcAHVoA1ZvrVlEe1Gi6qUTqt8bRX5FghLeI28p99bsO1kWu2IDuZZB7zuCg0r9Bk+fk9UyRiKDQ+lZUTpxDWozObCM2t0XWB8jyenPzCW+NPnqKPP/+lq4UeOyJOoyvyALv1JyfRO+xJw8JY9m7nXedVA/4DI8VvFiAS6arS77xefcNsR4Zemgo8d1XcjjTW+MiA0RmYP+yZafBjP1Ysc+nSSAJ4SQYwNB9ZACuFntCJfcTguLJ2LrqjdAhRsZyusDiAAg/adLR05diN2ImmHyKGWqe5V9ymF5Ft4qq6LSrnTU1qrXB3VkHjjoILaWEjSqA9U9Na/WLc7pAgMBAAECggEAbvJmAlFmqQaH2NzaJN7QyLPTeM+FtTPDp0UZmfo9xBTFHxJTyXlf0Gxo/htr71WVGi7uY0+xWsBwKje4zywXwWpfDZxx8os3oWAcf2z+ADVA16815m9bH/AJyi9IeAZlMsj28Eat28lfeuKkqSURCBJFTo3WaPeUGvonsac2UaPuVp8fnDkNIk3Tbw6TCy06NHQIW0HCBQ+IB/5/+8LyNamA7IkHQxQsj82ScSWQUuuaZQb3id9zxszvNLrTuBSlNbc5my75jmiUTnoVEWzJDrQgwN6/Zv00AAMmM6bgShbFGxrkCGF1dSVfoWADTZiyRvvw2Sk540PZbexQ+1gHbQKBgQD0B67I1YcaqelC1fgEEGoYqFcRr+czlj9ETEwNAurj3crelTU4yCAjeJwkwKO8aTOzxiHnYefGOIE0VEu+dWGAmvMMeEnnlyR1FD49Mra+S/lL0efk8sOuPhADjlRcXz1EafmWIVcr5NXqB2BrXhDgkBCi0394CRzl7WwmqN5p2wKBgQDgfDb8UsRDy4nVaBm5s7GTudHyzuEFt9hpuXpHDsX+iQ9YX7vame7LMPNdeHUbnNMHLwIyA/HNK5TNYJiw+/7x+3prnPyWzUiO7ABxwwYmnFB13iY7aHV8xtfJqpdvFyjuzkOumQZa7ozV8jLpWAPl+MUyh0WiAcjccrWliauPiwKBgHWOWenPy1TR81fKIb87dRDJz5/qoDMetv2cz6oY9LSuvLL7J/dhmPHdgULHNllmSIza7qw7Yt7/i2Z9ETv/6ed9PYaNT/Trv5lTFiR/EL+dc8vNo6uMMy32IdrErukMzEPv1Vc4Wnj/tPkXcqBmYTT80bbOsgJOSYiOFmKNSQ+vAoGBALjXLsNnNzoHf2/cLsqM3pwf9nKyy/BUZ/ruAXQ/AuLKYntHILwn4csnfEwromnhIJYaq/kY7GIfD4BomHGqnUsM4cl4UzeswqEETH1fs1HZhhr3l411PsBGTmo8cELF1CuGZY5/bA81u3ty2V7v3Q3gYCdx4PE7/PMf9NfgWwZvAoGBAJGHNVL5T9uoEEbWAcbuOzCv4kiJdABhcS9y4j/rchrq4IX3UL13NAc5pjfbxUfu1ku9ccJg+L8xRsH8xDOXkOMwEdIRJqSf+4Il6EgPYLX4o/9mhDaYCF24pySuRUEMNrTy60tA0y7SfAHm0eHm8B1ZUH7ihXBhcQ15QMNTYQjI", "UTF-8");// sign = AlipaySignature.rsaSign(strSign, "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDV/SESiXGcAHVoA1ZvrVlEe1Gi6qUTqt8bRX5FghLeI28p99bsO1kWu2IDuZZB7zuCg0r9Bk+fk9UyRiKDQ+lZUTpxDWozObCM2t0XWB8jyenPzCW+NPnqKPP/+lq4UeOyJOoyvyALv1JyfRO+xJw8JY9m7nXedVA/4DI8VvFiAS6arS77xefcNsR4Zemgo8d1XcjjTW+MiA0RmYP+yZafBjP1Ysc+nSSAJ4SQYwNB9ZACuFntCJfcTguLJ2LrqjdAhRsZyusDiAAg/adLR05diN2ImmHyKGWqe5V9ymF5Ft4qq6LSrnTU1qrXB3VkHjjoILaWEjSqA9U9Na/WLc7pAgMBAAECggEAbvJmAlFmqQaH2NzaJN7QyLPTeM+FtTPDp0UZmfo9xBTFHxJTyXlf0Gxo/htr71WVGi7uY0+xWsBwKje4zywXwWpfDZxx8os3oWAcf2z+ADVA16815m9bH/AJyi9IeAZlMsj28Eat28lfeuKkqSURCBJFTo3WaPeUGvonsac2UaPuVp8fnDkNIk3Tbw6TCy06NHQIW0HCBQ+IB/5/+8LyNamA7IkHQxQsj82ScSWQUuuaZQb3id9zxszvNLrTuBSlNbc5my75jmiUTnoVEWzJDrQgwN6/Zv00AAMmM6bgShbFGxrkCGF1dSVfoWADTZiyRvvw2Sk540PZbexQ+1gHbQKBgQD0B67I1YcaqelC1fgEEGoYqFcRr+czlj9ETEwNAurj3crelTU4yCAjeJwkwKO8aTOzxiHnYefGOIE0VEu+dWGAmvMMeEnnlyR1FD49Mra+S/lL0efk8sOuPhADjlRcXz1EafmWIVcr5NXqB2BrXhDgkBCi0394CRzl7WwmqN5p2wKBgQDgfDb8UsRDy4nVaBm5s7GTudHyzuEFt9hpuXpHDsX+iQ9YX7vame7LMPNdeHUbnNMHLwIyA/HNK5TNYJiw+/7x+3prnPyWzUiO7ABxwwYmnFB13iY7aHV8xtfJqpdvFyjuzkOumQZa7ozV8jLpWAPl+MUyh0WiAcjccrWliauPiwKBgHWOWenPy1TR81fKIb87dRDJz5/qoDMetv2cz6oY9LSuvLL7J/dhmPHdgULHNllmSIza7qw7Yt7/i2Z9ETv/6ed9PYaNT/Trv5lTFiR/EL+dc8vNo6uMMy32IdrErukMzEPv1Vc4Wnj/tPkXcqBmYTT80bbOsgJOSYiOFmKNSQ+vAoGBALjXLsNnNzoHf2/cLsqM3pwf9nKyy/BUZ/ruAXQ/AuLKYntHILwn4csnfEwromnhIJYaq/kY7GIfD4BomHGqnUsM4cl4UzeswqEETH1fs1HZhhr3l411PsBGTmo8cELF1CuGZY5/bA81u3ty2V7v3Q3gYCdx4PE7/PMf9NfgWwZvAoGBAJGHNVL5T9uoEEbWAcbuOzCv4kiJdABhcS9y4j/rchrq4IX3UL13NAc5pjfbxUfu1ku9ccJg+L8xRsH8xDOXkOMwEdIRJqSf+4Il6EgPYLX4o/9mhDaYCF24pySuRUEMNrTy60tA0y7SfAHm0eHm8B1ZUH7ihXBhcQ15QMNTYQjI", "utf-8");} catch (AlipayApiException e) {return null;}return sign;}/*** 获取签名信息* @param data* @param secret* @return*/public static boolean rsaCheck(Map<String, String> data, String secret) {boolean result=false;try {// String strSgin=AlipaySignature.rsaSign(data)result = AlipaySignature.rsaCheckV1(data,"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1f0hEolxnAB1aANWb61ZRHtRouqlE6rfG0V+RYIS3iNvKffW7DtZFrtiA7mWQe87goNK/QZPn5PVMkYig0PpWVE6cQ1qMzmwjNrdF1gfI8npz8wlvjT56ijz//pauFHjsiTqMr8gC79Scn0TvsScPCWPZu513nVQP+AyPFbxYgEumq0u+8Xn3DbEeGXpoKPHdV3I401vjIgNEZmD/smWnwYz9WLHPp0kgCeEkGMDQfWQArhZ7QiX3E4Liydi66o3QIUbGcrrA4gAIP2nS0dOXYjdiJph8ihlqnuVfcpheRbeKqui0q501Naq1wd1ZB446CC2lhI0qgPVPTWv1i3O6QIDAQAB", "UTF-8", "RSA2");} catch (AlipayApiException e) {return false;}return result;}}
import com.example.signtest.utils.SignUtil;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.Date;import java.util.HashMap;import java.util.Map;/*** 签名测试-客户端(用户可以模拟该代码来生成签名参数调用该项目的openapi)** @author :* @since :**/@RequestMapping("/signclient")@RestControllerpublic class TestSignClientController {@GetMapping("/sign")public Object sign() {Map<String, String> param = new HashMap<>();param.put("userId", "1");param.put("amount", "9.99");param.put("productId", "9");Map<String, String> headers = new HashMap<>(4);headers.put("appid", "6");headers.put("timestamp", String.valueOf(new Date().getTime()));headers.put("nonce", "7");String sign = SignUtil.getSign(headers,"mysecret123456","rsa");headers.put("sign", sign);return null;}}
import com.example.signtest.aspect.Signature;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.math.BigDecimal;/*** 签名测试-服务端(测试签名连通性)** @author :* @since :**/@RequestMapping("/signserver")@RestControllerpublic class TestSignServerController {@PostMapping("/checkSign")@Signaturepublic Object checkSign(){//业务处理return "请求成功";}}
配置文件配置项目端口即可
server.port=8098
启动客项目,可以访问
localhost:8098/signclient/sign来生成签名数据,
再访问 http://127.0.0.1:8098/signserver/checkSign时,把参数加上,我在注意sign和timestamp的值要实时获取放到请求的接口中,我这里用Postman请求设置如下

注意密钥这里是固定写到代码中,大家可以根据appid来查找相关信息进行验签

注意需要在需要签约的接口也要加入验签注解

代码包括引用的包都放到上面的代码中了,大家用Intellij Idea打开,鼠标放到引入的包代码上,按键盘Alt+enter进行

更多分享请关注我的公众号




