暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

对外接口签名验签(验签方法借用支付宝)

静思笃行的蜗牛 2021-11-08
2665

此文内容若含有机密信息,请不要擅自使用相关信息或转发给任何人,不然需要负法律责任,并请告知相关人及时删除并修改机密信息,谢谢。

  、简介


       我们大家在开发的过程中,相信大家很多人都对接过第三方接口,但这一般都是作为用户调用第三方,那如果作为第三方开发对外接口供别人调用,那如何写呢,通常我们会约定好验签规则,有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)
@Documented
public @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
@Component
public class SignatureAspect {




private static final String APPID = "6";


@Resource
private 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 "请求发起时间超过服务器限制时间";
}
// 校验appid
if (!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")
@RestController
public 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")
@RestController
public class TestSignServerController {


@PostMapping("/checkSign")
@Signature
public 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进行



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



文章转载自静思笃行的蜗牛,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论