前言
前段时间做支付的时候,要生成可供微信和支付宝扫描的支付二维码,二维码里存储的也就是一个链接,但是可能是因为链接比较长的缘故,总是被微信拦截。
然后就换成了微信官方的长链接转短链接,好不容易弄好了,发现支付宝扫的话会拦截,两家果真是水火不容。。。
有问题就解决问题呗,那我换成第三方的好了,但是网上一般的第三方短链接服务平台都不会被微信接纳,那就只能找比较知名的,比如新浪、百度、谷歌,我首先试了新浪的,完美,终于让微信和支付宝都满意了。
然后就在前几周,测试人员忽然找我说二维码出不来了。。我真是心力交瘁,去新浪官方一看你说气不气,在11号的时候这个接口下线了。。。
靠别人永远靠不住。。。那就只能靠自己了!
没有接触过这种业务的小伙伴可能会问什么是短链接?长链接好好地,干嘛要把它转成短链接?咋就能转成短链接呢?
下面我们简单介绍一下短链接的相关知识。
什么是短链接
比如一个百度图片的搜索网址是这样的:http://image.baidu.com/search/index?tn=baiduimage&ct=201326592&lm=-1&cl=2&ie=gb18030&word=%CD%BC%C6%AC&fr=ala&ala=1&alatpl=others&pos=0
以及一些第三方的:
- (https://dwz3.cn/)生成的短网址:https://0x9.me/8NhQJ
- (http://www.h5ip.cn/)生成的短网址:http://h5ip.cn/SmDX
看到没有,一个那么长的网址,确被缩短到这么短,而且每个平台生成的都不一样,缺都可以访问到我们原本那个长长的网址。以上生成的网址有些可能是永久的,有些可能一段时间就过期了。
为什么要用短链接(引用)
1、新浪微博
我们在新浪微博上发布网址的时候,微博会自动判别网址,并将其转换,例如:https://t.cn/RuPKzRW。为什么要这样做的?
这是因为微博限制字数为140字一条,那么如果我们需要发一些链接上去,但是这个链接非常的长,以至于将近要占用我们内容的一半篇幅,这肯定是不能被允许的或者说用户体验很差的,所以短网址应运而生了,短网址这种服务可以说是在微博出现之后才流行开来的!往下看:
(1)首先,我先发一条微博带有一个URL地址:
(2)然后,看他转换之后显示的效果是什么样子的哪?
(3)查看对应页面元素的HTML源码如下:
(4)可以看出:https://blog.csdn.net/xlgen157387/article/details/79863301 被转换为:http://t.cn/RuPKzRW,此时你访问http://t.cn/RuPKzRW是可以定位到https://blog.csdn.net/xlgen157387/article/details/79863301,也就是实现了转换。
2、短网址二维码
网址在转换成短网址时,也可以生成相应的短网址二维码,短网址二维码的应用,二维码核心解决的是跨平台、跨现实的数据传输问题;而且二维码跟应用场景结合之后,所能解决的问题会越来越多。
(1)短网址二维码相比短链接更方便,能少输入,尽量少输入,哪怕只是少点一下键盘,都是有意义的。
(2)二维码只是扫描一个简单的链接,打开的却是一个世界。想象一下,用手机购买售货机里商品,二维码扫描是略快于从用手机找到该售货机并找到该商品的,而且这种操作相对于搜索/查找而言不是更优雅吗?
(3)所有商超里面的商品,都是使用条码来确定商品的唯一性的,去买单的时候都是扫描条码。试想,如果里面加入了更多产品的生产日期、厂家、流转途径、原材料等等信息,是不是厉害了呢?特别是针对食品信息的可追溯上,二维码应用场景更广泛。
除了上述场景中,我们将长地址转换为短地址的使用场景的优点(压缩URL长度)之外,短地址还具有很多实际场景中的优点,例如:
(1)节省网址长度,便于社交化传播,一个是让URL更短小,传播更方便,尤其是URL中有中文和特殊字符,短网址解决很长的URL难以记忆不利于传播的问题;
(2)短网址在我们项目里可以很好的对开放以及对URL进行管理。有一部分网址可以会涵盖性、暴力、广告等信息,这样我们可以通过用户的举报,完全管理这个连接将不出现在我们的应用中,对同样的URL通过加密算法之后,得到的地址是一样的;
(3)方便后台跟踪点击量、地域分布等用户统计。我们可以对一系列的网址进行流量,点击等统计,挖掘出大多数用户的关注点,这样有利于我们对项目的后续工作更好的作出决策;
(4)规避关键词、域名屏蔽手段、隐藏真实地址,适合做付费推广链接;
(5)当你看到一个淘宝的宝贝连接后面是200个“e7x8bv7c8bisdj”这样的字符的时候,你还会觉得舒服吗。更何况微博字数只有140字,微博或短信里,字数不够,你用条短网址就能帮你腾出很多空间来;
如何生成短链接
网上比较流行的算法有两种 自增序列算法、 摘要算法
这里我也没去专门很细的研究,着急拿来用,后续有时间再去深入吧,有兴趣的小伙伴可以看一下以下博客:
面试必备:如何将一个长URL转换为一个短URL?
短网址(short URL)系统的原理及其实现
实现
好了,说了这么多,终于到实现的部分了,其实短网址生成服务基本的实现流程都是一样的,不一样的知识短网址生成的算法不同而已,下面是一个短网址系统基本的实现,我这里是存在redis中比较方便快捷,大家也可以选择存入mysql等。
代码
这里的案例场景模仿我遇到的支付问题
案例是基于springboot项目,其他框架可能要做适当修改
OrderPayController:项目内生成二维码的接口,这里简化,只是生成链接,未转变为二维码,想转为二维码的可以看:【二维码】——生成二维码并转为base64
/**
\* @author: WangZhiJun
\* @create: 2019-09-21 12:31
**/
@RestController
@RequestMapping("/api/v1/order")
public class OrderController {
private static final String SHORT_URL = "shortUrl:";
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 系统内生成支付二维码接口
*/
@GetMapping(value = "/payOrder")
public String pay(@RequestParam String orderId) {
String orderName = "XXSuperMarketPay";
BigDecimal orderMoney = new BigDecimal(100);
System.out.println("订单"+orderId+orderName+"需支付:"+orderMoney+"元");
String wxPayUrl = "http://localhost:8080/wx/pay/pay?orderId="+orderId+"&orderName="+orderName+"&orderMoney="+orderMoney;
String shortUrl = ShortUrlUtils.shortUrl(wxPayUrl);
System.out.println("生成的微信支付链接为:"+wxPayUrl);
System.out.println("生成的短链接为:"+ shortUrl);
//存入redis或者数据库,键值为六位数的短链接
redisTemplate.opsForValue().set(SHORT_URL + shortUrl, shortUrl, 1, TimeUnit.HOURS);
//这里我的业务场景中是生成二维码,这里简化为链接
return "http://localhost:8080/url/" + shortUrl;
}
}
/** 假设这是微信支付的接口
* @author: WangZhiJun
* @create: 2019-09-21 12:31
**/
@RestController
@RequestMapping("/wx/pay")
public class WxPayController {
/**
* 模仿微信支付API
*/
@GetMapping(value = "/pay")
public void paySuccess(@RequestParam String orderId,
@RequestParam String orderMoney,
@RequestParam String orderName) {
System.out.println("订单"+orderId+orderName+"支付:"+orderMoney+"元");
System.out.println("支付成功");
}
}
/**
* @description: 短网址转发
* @author: WangZhiJun
* @create: 2019-09-21 12:20
**/
@RestController
@RequestMapping("/url")
public class UrlController {
private static final String SHORT_URL = "shortUrl:";
@Resource
private RedisTemplate<String, Object> redisTemplate;
@GetMapping(value = "/{shortUrl}")
public void getInvoiceUrl(@PathVariable(required = false) String shortUrl,
HttpServletResponse response) {
String url = (String) redisTemplate.opsForValue().get(SHORT_URL + shortUrl);
try {
//将短链接映射的长链接取出并重定向到长链接
response.sendRedirect(url);
} catch (IOException e) {
e.printStackTrace();
}
}
}
长链接转短码的算法主要是使用MD5 算法对原始链接进行加密(这里使用的MD5 加密后的字符串长度为32 位),然后对加密后的字符串进行处理以得到短链接的地址。
import java.security.MessageDigest;
/** 短链接生成工具类
* @author WangZhiJun
*/
public class ShortUrlUtils {
/**
* 十六进制下数字到字符的映射数组
*/
private final static String[] HEXDIGITS = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
/**把inputString加密*/
public static String md5(String inputStr){
return encodeByMD5(inputStr);
}
/**对字符串进行MD5编码*/
private static String encodeByMD5(String originString){
if (originString!=null) {
try {
//创建具有指定算法名称的信息摘要
MessageDigest md5 = MessageDigest.getInstance("MD5");
//使用指定的字节数组对摘要进行最后更新,然后完成摘要计算
byte[] results = md5.digest(originString.getBytes());
//将得到的字节数组变成字符串返回
String result = byteArrayToHexString(results);
return result;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 轮换字节数组为十六进制字符串
* @param b 字节数组
* @return 十六进制字符串
*/
private static String byteArrayToHexString(byte[] b){
StringBuffer resultSb = new StringBuffer();
for(int i=0;i<b.length;i++){
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
/**
* @param b 字节
* @return 将一个字节转化成十六进制形式的字符串
*/
private static String byteToHexString(byte b){
int n = b;
if(n<0) {
n = 256 + n;
}
int d1 = n/16;
int d2 = n%16;
return HEXDIGITS[d1] + HEXDIGITS[d2];
}
public static String shortUrl(String url) {
return shortUrls(url)[0];
}
public static String[] shortUrls(String url) {
// 可以自定义生成 MD5 加密字符传前的混合 KEY
String key = "md5" ;
// 要使用生成 URL 的字符
String[] chars = new String[] { "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" ,
"i" , "j" , "k" , "l" , "m" , "n" , "o" , "p" , "q" , "r" , "s" , "t" ,
"u" , "v" , "w" , "x" , "y" , "z" , "0" , "1" , "2" , "3" , "4" , "5" ,
"6" , "7" , "8" , "9" , "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" ,
"I" , "J" , "K" , "L" , "M" , "N" , "O" , "P" , "Q" , "R" , "S" , "T" ,
"U" , "V" , "W" , "X" , "Y" , "Z"
};
// 对传入网址进行 MD5 加密
String hex = ShortUrlUtils.md5(key + url);
String[] resUrl = new String[4];
for ( int i = 0; i < 4; i++) {
// 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算
String sTempSubString = hex.substring(i * 8, i * 8 + 8);
// 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用 long ,则会越界
long lHexLong = 0x3FFFFFFF & Long.parseLong (sTempSubString, 16);
StringBuilder outChars = new StringBuilder();
for ( int j = 0; j < 6; j++) {
// 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引
long index = 0x0000003D & lHexLong;
// 把取得的字符相加
outChars.append(chars[(int) index]);
// 每次循环按位右移 5 位
lHexLong = lHexLong >> 5;
}
// 把字符串存入对应索引的输出数组
resUrl[i] = outChars.toString();
}
return resUrl;
}
}
演示
然后访问生成的短码网址:http://localhost:8080/url/vqQjEj,会自动重定向到原本的支付链接
可以在博主主页项目部分进行测试短网址服务
大功告成!
你好,看了之后受益匪浅
请问你有兴趣写一份关于Wireshark的报告吗?
有报酬,有意向的话请联系我哦!
额,不好意思,您说的这个不是我学的领域,目前我还没有接触过