Java 提供给第三方使用接口方法
Posted 小甘说码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 提供给第三方使用接口方法相关的知识,希望对你有一定的参考价值。
Java提供接口给第三方使用,校验保证接口的安全性
前言
相信有很多小伙伴,在日常的开发中都有遇到过需要调用第三方接口的需求吧,但是自己有没有写过接口提供给第三方使用呢,常规的都是我们调用别人的接口,但是自己需要开发接口提供给第三方使用的场景应该不是很多,很多小伙伴可能会想不就开发一个接口对外开放嘛岂不是很简单,但是在开发接口对外开放,我们需要考虑一个问题,没有限制条件,那岂不是太不安全了,谁都可以调我这个接口了啊。
所以接下来的就是我们需要考虑的问题了,在开发接口的时候就要考虑到安全性的问题,那么应该如何去解决这个问题呢?提供接口给第三方使用的时候需要加上校验保证接口的安全性。
下面是我写的一个例子希望对大家有帮助。
接口Controller
在写接口前一定要签名做签名校验,我的签名方式做了特殊处理,因为接口是对外开放的,这个是为了避免恶意调用接口做的处理,叫做签名的混淆值,这个签名混淆值的作用是就算别人知道了接口,并且知道签名方式也不能被攻击,是为了避免被恶意篡改数据,签名混淆值就是一组特定加密后的数据。
@PostMapping("refundDeductionPoints")
public Result<SysIntegralStatement> refundDeductionPoints (@RequestParam Map<String,String> params)
Result<SysIntegralStatement> result = new Result<SysIntegralStatement>();
try
//签名校验
String msgDigest = params.get("msgDigest");//签名
String msgData = params.get("msgData");
String timeStamp = params.get("timeStamp");
String secret = params.get("secret");// 秘钥
String sign = SignUtil.sign(msgData+"wf8la1tw7p9o2xz",timeStamp);//wf8la1tw7p9o2xz为签名混淆值
if (!msgDigest.equals(sign))
return result.setCode(1006).setReason("数字签名无效");
if (Common.isEmpty(secret)) //先签名后幂等校验
return result.setCode(1001).setReason("密钥不能为空");
/**
* 幂等校验
* 1.同一个用户操作同一个退货单一分钟内操作该单据视为重复操作(此秘钥已通过特殊处理)
*/
String value = redistempalte.opsForValue().get(secret);
if (Common.isNotEmpty(value))
logger.error("重复请求 secret=",value);
return result.setCode(1007).setReason("重复请求");
redistempalte.opsForValue().set(secret, "1",60,TimeUnit.SECONDS);//设置缓存一分钟
return service.refundDeductionPoints(params);
catch (Exception e)
logger.error("添加积分流水异常", e);
return result.setCode(ErrorCodes.BUSINESS_ERROR).setReason("生成积分流水失败");
接口幂等性校验
此接口做幂等性校验,幂等性校验常见解决方案有很多,可以自行根据实际情况选择,
说到幂等首先要先了解什么是幂等
概念:
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。
这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“getUsername()和setTrue()”函数就是一个幂等函数.
幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次,比如:
订单接口, 不能多次创建订单
支付接口, 重复支付同一笔订单只能扣一次钱
支付宝回调接口, 可能会多次回调, 必须处理重复回调
普通表单提交接口, 因为网络超时等原因多次点击提交, 只能成功一次
等等
解决方案常见的几种方式
唯一索引 – 防止新增脏数据
token机制 – 防止页面重复提交
悲观锁 – 获取数据的时候加锁(锁表或锁行)
乐观锁 – 基于版本号version实现, 在更新数据那一刻校验数据
分布式锁 – redis(jedis、redisson)或zookeeper实现
状态机 – 状态变更, 更新数据时判断状态
如果有小伙伴不理解什么是幂等可以看看官方是解释
实现类ServiceImpl
@Transactional
@Override
public Result<SysIntegralStatement> refundDeductionPoints(Map<String, String> params)
String msgData = params.get("msgData");
ParamIntegral entity = new Gson().fromJson(msgData, ParamIntegral.class);
if (Common.isNull(entity))
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("请求参数不能为空");
if (Common.isEmpty(entity.getBitems()))
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("请求参数不能为空");
int row = 0;
for (ParamIntegral bitem : entity.getBitems())
if (Common.isEmpty(bitem.getDdh()))
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("订单号为必传参数");
if (null == bitem.getJfz())
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("扣减积分不能为空");
List<MallOrderInfo> orderInfo = mallOrderInfoMapper.selectByDdh(bitem.getDdh());
if (orderInfo == null)
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR)
.setReason("订单号为" + bitem.getDdh() + "没有此订单请联系客服核对信息。");
if (orderInfo != null && orderInfo.size() > 1)
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR)
.setReason("订单号为" + bitem.getDdh() + "有多个相同订单请联系客服核对信息。");
if (!"E".equals(orderInfo.get(0).getDdzt()))
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR)
.setReason("订单号为" + bitem.getDdh() + "未确认收货还没产生积分不允许退货。");
SysIntegral integral = Common.first(integralMapper.selectByMdbm(orderInfo.get(0).getMdbm()));
if (integral == null)
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR)
.setReason("门店编码为" + orderInfo.get(0).getMdbm() + "积分汇总没有找到此门店,请联系客服核实");
BigDecimal kyjf = BigDecimal.ZERO;
if (entity.getReturnGoods() == true)
// 可用积分小于扣减积分不够扣ERP使用前抵扣
if (bitem.getJfz().compareTo(integral.getKyjf()) == 1)
kyjf = BigDecimal.ZERO;
else
// 可用积分 = 当前可用积分-扣减积分
kyjf = Common.nvl(integral.getKyjf(), BigDecimal.ZERO).subtract(bitem.getJfz());
else
// 可用积分 = 当前可用积分+退还积分
kyjf = Common.nvl(integral.getKyjf(), BigDecimal.ZERO).add(bitem.getJfz());
// 更新积分汇总
SysIntegral dataMap = new SysIntegral();
dataMap.setIntegralId(integral.getIntegralId());
dataMap.setMdbm(integral.getMdbm());
dataMap.setKyjf(kyjf);
dataMap.setUpdateTime(new Date());
dataMap.setUpdateUser(entity.getUserName());
dataMap.setUpdateUserid(entity.getUserId().intValue());
row = integralMapper.updateByPrimaryKeySelective(dataMap);
if (row == 0)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("更新积分失败");
//推送到ERP门店信息
BdMdxxH mdxx =new BdMdxxH();
mdxx.setMdbm(integral.getMdbm());
mdxx.setMdjf(kyjf);
com.lkfs.cw.common.Result<BdMdxxH> bdMdxxh = dataBaseServiceApi.updateStorePoints(mdxx);
if (!bdMdxxh.isComplete())
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return new Result<SysIntegralStatement>().setCode(bdMdxxh.getCode()).setReason(bdMdxxh.getReason());
SysIntegralStatement statement = new SysIntegralStatement();
if (entity.getReturnGoods() == true)
statement.setJfz(bitem.getJfz().negate());// 消费的积分值
if (bitem.getJfz().compareTo(integral.getKyjf()) == 1) // 可用积分小于扣减积分不够扣ERP使用前抵扣
statement.setTzhjfz(BigDecimal.ZERO);// 调整后积分值
else
statement.setTzhjfz(Common.nvl(integral.getKyjf(), BigDecimal.ZERO).subtract(bitem.getJfz()));// 调整后积分值
statement.setJfxflx("E");// 积分支出
statement.setXxsm("退货扣减积分(订单号为:" + bitem.getDdh() + "," + "退货单号为:" + entity.getDjh() + ")" + "已扣除:"
+ bitem.getJfz().negate() + ":积分");
else // 取消退货
statement.setJfxflx("I");// 积分收入
statement.setJfz(bitem.getJfz());// 取消退货把积分赠送回来
statement.setTzhjfz(Common.nvl(integral.getKyjf(), BigDecimal.ZERO).add(bitem.getJfz()));// 调整后积分值
statement.setXxsm("取消退货(订单号为:" + bitem.getDdh() + "," + "退货单号为:" + entity.getDjh() + ")" + "已退还:"
+ bitem.getJfz() + ":积分");
statement.setIntegralId(integral.getIntegralId());// 该门店积分编码
statement.setTzqjfz(integral.getKyjf());// 调整前积分值
statement.setDdh(entity.getDdh());
statement.setCreateTime(new Date());// 流水生成时间
statement.setCreateUser(entity.getUserName());
statement.setCreateUserid(entity.getUserId().intValue());
statement.setJftz("T");// 积分扣减为T
statement.setZt("Y");// 状态 Y:有效
row = mapper.insert(statement);
if (row == 0)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("插入积分流水失败");
return new Result<SysIntegralStatement>().setCode(ErrorCodes.SUCCESS).setReason("操作成功");
第三方调用接口Api实现类
模拟第三方合作方调用接口
//此方式以上已写了封装信息就不一一展示了,可以根据实际情况自行操作
private void pushIntegral(Long djlsh)
FiSjysjsH fiSjysjsh = mapper.selectByPrimaryKey(djlsh);
//订单退货调用某某退货扣减积分接口
List<FiSjysjsB> sjysjsbList = bmapper.selectByKhddh(djlsh);
if (sjysjsbList != null && sjysjsbList.size() > 0)
List<ParamIntegral> list = new ArrayList<ParamIntegral>();
for (FiSjysjsB bitem : sjysjsbList)
ParamIntegral temp = new ParamIntegral();
temp.setDdh(bitem.getKhddh());
temp.setJfz(bitem.getJfz());
list.add(temp);
ParamIntegral param = new ParamIntegral();
param.setBitems(list);
param.setDjh(fiSjysjsh.getDjh());
param.setUserId(AppRealm.getCurrentUser().getUserId());
param.setUserName(AppRealm.getCurrentUser().getUserName(常用物流快递单号查询接口种类及对接方法