Java如何提供给第三方使用接口方法详解

目录
  • 前言
  • 接口Controller
  • 接口幂等性校验
  • 实现类ServiceImpl
  • 第三方调用接口Api实现类
  • 生成签名工具类
  • HttpCilent工具类
  • 附:分享一个获取IP工具类
  • 总结

前言

相信有很多小伙伴,在日常的开发中都有遇到过需要调用第三方接口的需求吧,但是自己有没有写过接口提供给第三方使用呢,常规的都是我们调用别人的接口,但是自己需要开发接口提供给第三方使用的场景应该不是很多,很多小伙伴可能会想不就开发一个接口对外开放嘛岂不是很简单,但是在开发接口对外开放,我们需要考虑一个问题,没有限制条件,那岂不是太不安全了,谁都可以调我这个接口了啊。

所以接下来的就是我们需要考虑的问题了,在开发接口的时候就要考虑到安全性的问题,那么应该如何去解决这个问题呢?提供接口给第三方使用的时候需要加上校验保证接口的安全性。

下面是我写的一个例子希望对大家有帮助。

接口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());
			if (new Short("1").equals(fiSjysjsh.getLocked())) {
				param.setReturnGoods(true);
			}else {
				param.setReturnGoods(false);
			}
			String msgData = new Gson().toJson(param).toString();
			Map<String, String> params = new HashMap<String, String>();
			String timeStamp = String.valueOf(System.currentTimeMillis());//时间戳
			params.put("timeStamp", timeStamp);
			params.put("msgData", msgData);
			params.put("msgDigest", SignUtil.sign(msgData+"wf8la1tw7p9o2xz", timeStamp));//生成签名第二个值暂定(wf8la1tw7p9o2xz签名混淆值)
			params.put("secret",IDEMPOTENT_SECRET_PREFIX + fiSjysjsh.getDjh() + AppRealm.getCurrentUser().getUserId()+param.getReturnGoods() );//自定义密钥 做幂等校验
			String result = HttpCilent.post(B2B_URL, params); //发送http post请求
			B2bIntegralResponse res = new Gson().fromJson(result, B2bIntegralResponse.class);
			if (null == res) {
				throw new RuntimeException("调用积分接口系统异常");
			}
			if (res.getCode() != 0) {//接口返回失败异常代码提示
				throw new RuntimeException("调用积分接口发生异常,异常代码为:"+res.getCode()+"异常信息为:"+res.getReason());
			}
		}
	}

生成签名工具类

package com.cy.xgsm.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

/**
 *
 * @author Dylan
 *
 */
public class SignUtil {

private static final Logger log = LoggerFactory.getLogger(SignUtil.class);

	/**
	 *
	 */
	public static String sign(String str, String secret) {
		StringBuilder enValue = new StringBuilder();
		enValue.append(secret);
		enValue.append(str);
		enValue.append(secret);
		return encryptByMD5(enValue.toString());
	}

	private static String encryptByMD5(String data) {
		String re_md5 = new String();
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(data.getBytes());
			byte b[] = md.digest();
			int i;
			StringBuffer buf = new StringBuffer();
			for (int offset = 0; offset < b.length; offset++) {
				i = b[offset];
				if (i < 0)
					i += 256;
				if (i < 16)
					buf.append("0");
				buf.append(Integer.toHexString(i));
			}
			re_md5 = buf.toString();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return re_md5.toUpperCase();
	}

	public static String compare(String jsonStr,String secret ){

		JsonParser jsonParser = new JsonParser();
		JsonObject jsonObject = jsonParser.parse(jsonStr).getAsJsonObject();
		String sign1 = "null";
		JsonElement signElement = jsonObject.remove("sign");
		if( signElement != null ){
			sign1 = signElement.getAsString();
		}
		log.info("sign1: " + sign1);
		StringBuilder enValue = new StringBuilder();
		enValue.append(secret);
		enValue.append(jsonObject.toString());
		enValue.append(secret);
		String sign2 = encryptByMD5(enValue.toString());
		jsonObject.addProperty("sign", sign2);
		return jsonObject.toString();
	}
}

HttpCilent工具类

这个工具类在我之前的文章也有但是没有把这个方式的放上去,如果有需要用到可直接把一下代码复制到这个Http工具类(见文末) 最后即可直接使用。

   /**
     * 发送post请求
     * @param url 目的url
     * @param parameters 参数
     * @return
     */
    public static String post(String url, Map<String, String> parameters) {
    	String result = "";// 返回的结果
    	BufferedReader in = null;// 读取响应输入流
    	PrintWriter out = null;
    	StringBuffer sb = new StringBuffer();// 处理请求参数
    	String params = "";// 编码之后的参数
    	try {
    	// 编码请求参数
    	if (parameters.size() == 1) {
    	for (String name : parameters.keySet()) {
    	sb.append(name)
    	.append("=")
    	.append(java.net.URLEncoder.encode(
    	parameters.get(name), "UTF-8"));
    	}
    	params = sb.toString();
    	} else {
    	for (String name : parameters.keySet()) {
    	sb.append(name)
    	.append("=")
    	.append(java.net.URLEncoder.encode(
    	parameters.get(name), "UTF-8")).append("&");
    	}
    	String temp_params = sb.toString();
    	params = temp_params.substring(0, temp_params.length() - 1);
    	}
    	// 创建URL对象
    	java.net.URL connURL = new java.net.URL(url);
    	// 打开URL连接
    	java.net.HttpURLConnection httpConn = (java.net.HttpURLConnection) connURL
    	.openConnection();
    	// 设置通用属性
    	httpConn.setRequestProperty("Accept", "*/*");
    	httpConn.setRequestProperty("Connection", "Keep-Alive");
    	httpConn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
    	httpConn.setRequestProperty("User-Agent",
    	"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
    	// 设置POST方式
    	httpConn.setDoInput(true);
    	httpConn.setDoOutput(true);
    	// 获取HttpURLConnection对象对应的输出流
    	out = new PrintWriter(httpConn.getOutputStream());
    	// 发送请求参数
    	out.write(params);
    	// flush输出流的缓冲
    	out.flush();
    	// 定义BufferedReader输入流来读取URL的响应,设置编码方式
    	in = new BufferedReader(new InputStreamReader(
    	httpConn.getInputStream(), "UTF-8"));
    	String line;
    	// 读取返回的内容
    	while ((line = in.readLine()) != null) {
    	result += line;
    	}
    	} catch (Exception e) {
    	e.printStackTrace();

    	} finally {
    	try {
    	if (out != null) {
    	out.close();
    	}
    	if (in != null) {
    	in.close();
    	}
    	} catch (IOException ex) {
    	ex.printStackTrace();
    	}
    	}
    	return result;
    	}

附:分享一个获取IP工具类

在日常开发中,经常会遇到需要记录IP的需求,接下来分享一个获取IP工具类,希望对小伙伴有帮助

package com.cy.xgsm.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;

/**
 * 获取IP工具类
 * @author Dylan
 *
 */
public class IpUtils {

	private static Logger logger = LoggerFactory.getLogger(IpUtils.class);

	/**
	 * 获取IP地址
	 * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
	 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
	 */
    public static String getIpAddr(HttpServletRequest request)
    {
        if (request == null)
        {
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("X-Real-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getRemoteAddr();
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    public static boolean internalIp(String ip)
    {
        byte[] addr = textToNumericFormatV4(ip);
        return internalIp(addr) || "127.0.0.1".equals(ip);
    }

    private static boolean internalIp(byte[] addr)
    {
        if (StringUtils.isNull(addr) || addr.length < 2)
        {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        final byte section1 = 0x0A;
        final byte section2 = (byte) 0xAC;
        final byte section3 = (byte) 0x10;
        final byte section4 = (byte) 0x1F;
        final byte section5 = (byte) 0xC0;
        final byte section6 = (byte) 0xA8;
        switch (b0)
        {
            case section1:
                return true;
            case section2:
                if (b1 >= section3 && b1 <= section4)
                {
                    return true;
                }
            case section5:
                switch (b1)
                {
                    case section6:
                        return true;
                    default:
                        return false;
                }
            default:
                return false;
        }
    }

    /**
     * 将IPv4地址转换成字节
     *
     * @param text IPv4地址
     * @return byte 字节
     */
    public static byte[] textToNumericFormatV4(String text)
    {
        if (text.length() == 0)
        {
            return null;
        }

        byte[] bytes = new byte[4];
        String[] elements = text.split("\\.", -1);
        try
        {
            long l;
            int i;
            switch (elements.length)
            {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > 4294967295L)){
                        return null;
                    }
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > 255L)){
                        return null;
                    }
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L)){
                        return null;
                    }
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    for (i = 0; i < 2; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)){
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L)){
                        return null;
                    }
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < 4; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)){
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null;
            }
        }
        catch (NumberFormatException e)
        {
            return null;
        }
        return bytes;
    }

    public static String getHostIp()
    {
        try
        {
            return InetAddress.getLocalHost().getHostAddress();
        }
        catch (UnknownHostException e)
        {
        }
        return "127.0.0.1";
    }

    public static String getHostName()
    {
        try
        {
            return InetAddress.getLocalHost().getHostName();
        }
        catch (UnknownHostException e)
        {
        }
        return "未知";
    }
}

总结

到此这篇关于Java如何提供给第三方使用接口方法的文章就介绍到这了,更多相关Java提供接口给第三方使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java调用第三方http接口的常用方式总结

    目录 1.概述 在Java项目中调用第三方接口的常用方式有 2.Java调用第三方http接口的方式 2.1 通过JDK网络类Java.net.HttpURLConnection 2.2 通过apache common封装好的HttpClient 2.3 通过Apache封装好的CloseableHttpClient 2.4 通过OkHttp 2.5 通过Spring的RestTemplate 2.6通过hutool的HttpUtil 3.总结 1.概述 在实际开发过程中,我们经常需要调用对方提

  • Java调用第三方接口示范的实现

    在项目开发中经常会遇到调用第三方接口的情况,比如说调用第三方的天气预报接口. 使用流程 [1]准备工作:在项目的工具包下导入HttpClientUtil这个工具类,或者也可以使用Spring框架的restTemplate来调用,上面有调用接口的方法[分为Get和Post方式的有参和无参调用]: package com.njsc.credit.util; import java.io.IOException; import java.net.URI; import java.util.ArrayL

  • 实例详解Java调用第三方接口方法

    目录 一. 通过JDK网络类Java.net.HttpURLConnection 1.java.net包下的原生java api提供的http请求 2.HttpClientUtil工具类 3.第三方api接口 4.测试类 二.通过Apache common封装好的HttpClient 1.引入依赖 2.httpClientUtil 3.第三方api接口 4.测试类 三.通过Spring的RestTemplate 1.引入依赖 2.RestTemplate配置类 3.RestTemplate实现类

  • Java如何提供给第三方使用接口方法详解

    目录 前言 接口Controller 接口幂等性校验 实现类ServiceImpl 第三方调用接口Api实现类 生成签名工具类 HttpCilent工具类 附:分享一个获取IP工具类 总结 前言 相信有很多小伙伴,在日常的开发中都有遇到过需要调用第三方接口的需求吧,但是自己有没有写过接口提供给第三方使用呢,常规的都是我们调用别人的接口,但是自己需要开发接口提供给第三方使用的场景应该不是很多,很多小伙伴可能会想不就开发一个接口对外开放嘛岂不是很简单,但是在开发接口对外开放,我们需要考虑一个问题,没

  • Java实现API sign签名校验的方法详解

    目录 1. 前言 2. 签名生成策略 3. API 签名算法 Java 实现 4. 测试一下 1. 前言 目的:为防止中间人攻击. 场景: 项目内部前后端调用,这种场景只需要做普通参数的签名校验和过期请求校验,目的是为了防止攻击者劫持请求 url 后非法请求接口. 开放平台向第三方应用提供能力,这种场景除了普通参数校验和请求过期校验外,还要考虑 3d 应用的授权机制,不被授权的应用就算传入了合法的参数也不能被允许请求成功. 2. 签名生成策略 接下来详述场景 2,其实场景 1 也包含在场景 2

  • Java使用POI实现导出Excel的方法详解

    目录 一.前景 二.概念 2.1. 简介 2.2.Excel版本和相关对象 2.3.WorkBook 2.4.POI依赖 三.POI - 写 3.1.代码示例 3.2. 性能对比 3.3. 测试rowAccessWindowSize 3.4. 导出Excel样式设置 四.POI - 读 4.1.代码示例 4.2.读取不同的数据类型 4.3.读取公式 五.POI - 遇到的坑 一.前景 在项目开发中往往需要使用到Excel的导入和导出,导入就是从Excel中导入到DB中,而导出就是从DB中查询数据

  • Java操作Excel文件解析与读写方法详解

    目录 一.概述 二.Apache POI 三.XSSF解析Excel文件 1.Workbook(Excel文件) 2.Sheet(工作簿) 3.Row(数据行) 4.Cell(单元格) 四.超大Excel文件读写 1.使用POI写入 2.使用EasyExcel 一.概述 在应用程序的开发过程中,经常需要使用 Excel 文件来进行数据的导入或导出.所以,在通过Java语言实现此 类需求的时候,往往会面临着Excel文件的解析(导入)或生成(导出). 在Java技术生态圈中,可以进行Excel文件

  • Java Fluent Mybatis 聚合查询与apply方法详解流程篇

    前言 接着上一篇文章:Java Fluent Mybatis 分页查询与sql日志输出详解流程篇 我把分页已经调整好了,现在实验一下官方给出的聚合查询方法. GitHub代码仓库:GitHub仓库 数据准备 为了聚合查询的条件,添加了几条数据. MIN 我们试着获取最小的年龄. 方法实现 @Override public Integer getAgeMin() { Map<String, Object> result = testFluentMybatisMapper .findOneMap(

  • Java利用StampedLock实现读写锁的方法详解

    目录 概述 StampedLock介绍 演示例子 性能对比 总结 概述 想到读写锁,大家第一时间想到的可能是ReentrantReadWriteLock.实际上,在jdk8以后,java提供了一个性能更优越的读写锁并发类StampedLock,该类的设计初衷是作为一个内部工具类,用于辅助开发其它线程安全组件,用得好,该类可以提升系统性能,用不好,容易产生死锁和其它莫名其妙的问题.本文主要和大家一起学习下StampedLock的功能和使用. StampedLock介绍 StampedLock的状态

  • Java 添加超链接到 Word 文档方法详解

    在Word文档中,超链接是指在特定文本或者图片中插入的能跳转到其他位置或网页的链接,它也是我们在编辑制作Word文档时广泛使用到的功能之一.今天这篇文章就将为大家演示如何使用Free Spire.Doc for Java在Word文档中添加文本超链接和图片超链接. Jar包导入 方法一:下载Free Spire.Doc for Java包并解压缩,然后将lib文件夹下的Spire.Doc.jar包作为依赖项导入到Java应用程序中. 方法二:通过Maven仓库安装JAR包,配置pom.xml文件

  • Java JWT实现跨域身份验证方法详解

    目录 1.JWT简介 2.JWT的结构 2.1 头部(header) 2.2 载荷(payload) 2.3 签证(signature) 3.JWT的原则 4.JWT的用法 5.JWT的问题和趋势 6.整合JWT令牌 6.1 在模块中添加jwt工具依赖 6.2 创建JWT工具类 1.JWT简介 JWT(JSON Web Token)是目前流行的跨域认证解决方案,是一个开放标准(RFC 7519),它定义了一种紧凑的.自包含的方式,用于作为JSON对象在各方之间安全地传输信息.该信息可以被验证和信

  • Java实现字符串转为驼峰格式的方法详解

    字符串转为驼峰格式 构建工具类 package com.yt.common.util; import com.yt.common.dto.NameCode; import com.yt.exam.enums.ZyEnum; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 字符串转为驼峰格式 构建工具类 * @author LYY * @date 2022/07

  • Java唤醒本地应用的两种方法详解

    目录 引言 1. Runtime使用方式 2. ProcessBuilder使用方式 3. 小结 引言 作为一个后端同学,经常被安全的小伙伴盯上,找一找安全漏洞:除了常说的注入之外,还有比较吓人的执行远程命令,唤醒本地应用程序等:然后有意思的问题就来了,写了这么多年的代码,好像还真没有尝试过用java来唤醒本地应用程序的 比如说一个最简单的,打开本地的计算器,应该怎么搞? 接下来本文将介绍一下如何使用java打开本地应用,以及打开mac系统中特殊一点的处理方式(直白来说就是不同操作系统,使用姿势

随机推荐