微信分享(JS-SDK权限签名算法)-Java实现

注意:免费节点订阅链接已更新至 2026-02-08点击查看详情

1、问题描述

公众号中的H5有个业务场景,要分享页面给好友,但是因为是在微信中分享,分享的链接微信是不认的,需要首先使用签名认证,认证后才能分享,按照微信官网api,首先需要获取token,然后再根据token获取jsapiticket,然后再将随机数、时间戳、url等按照keyvalue排序加密去认证,java后端实现了下,分享下代码,给需要的朋友。

2、解决方案

2.1 官方文档

官方文档才是yyds,首先查看微信开发者文档(https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html),如下:

附录1-JS-SDK使用权限签名算法 jsapi_ticket 生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。  参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html  用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi  成功返回如下JSON:  {   "errcode":0,   "errmsg":"ok",   "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8- 41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",   "expires_in":7200 } 获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。  签名算法  签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。  即signature=sha1(string1)。 示例:  noncestr=Wm3WZYTPz0wzccnW jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg timestamp=1414587457 url=http://mp.weixin.qq.com?params=value 步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:  jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value 步骤2. 对string1进行sha1签名,得到signature:  0f9de62fce790f9a083d5c99e95740ceb90c27ed 注意事项  签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。  签名用的url必须是调用JS接口页面的完整URL。  出于安全考虑,开发者必须在服务器端实现签名的逻辑。  如出现invalid signature 等错误详见附录5常见错误及解决办法。 

官网文档简要来说有几点:

(1)获取jsapi_ticket

首先拿appid与secret换取全局的access_token,然后通过access_token获取jsapi_ticket,这两个值的时效都是是7200秒(2个小时),access_token是全局的,简单说类似管理员权限,每天调用次数有限,2000次/日,这个提到全局的,就有局部的,使用过网页权限的(自定义菜单,H5跳转),也有个access_token,调用次数不限;因为access_token和jsapi_ticket每日调用次数有限,需要把这个值进行缓存;

(2)签名加密

排序、加密,需要对noncestr(随机字符串)、 有效的jsapi_ticket, timestamp(时间戳)、 url,对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1,然后sha1加密;

2.2 代码实现

一步一步来说吧,首先说下关于缓存token和jsapi_ticket的问题,很多方案都是直接放到redis中,但是目前项目比较简单,类似前置机的概念,项目周期也短,就这2个值,还的部署下redis,感觉划不来,就直接库放数据中,弄个定时的标签,整点刷一下,一天刷12次,24个值,稳定可靠,关于存储数据库和刷新就不在这里介绍了。

(1)首先获取access_token

    /**      *  获取token      * @return      */     @PostMapping("/getWXaccessToken")     public String getWXaccessToken() {         String url ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appId+"&secret=" +secret;         String resp = restTemplate.getForObject(url, String.class);         JSONObject resJson = JSONObject.parseObject(resp);         return resJson.getString("access_token");     } 

简要说明:

这里就是首先new RestTemplate(),然后拼接下appId和secret,就获取access_token,然后把这个access_token存到数据库中,然后提供查询就好了;

(2)获取jsapi_ticket

    /**      *  入参为token,返回ticket      * @param token      * @return      */     @PostMapping("/getWXJsapiTicket")     public String getWXJsapiTicket(String token) {         String ticket = null;         if (StringUtils.isBlank(ticket)) {             String url ="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + token +"&type=jsapi";             String resp = restTemplate.getForObject(url, String.class);             JSONObject resJson = JSONObject.parseObject(resp);             return resJson.getString("ticket");         }         return ticket;     } 

拼接下access_token,然后把这个access_token存到数据库中,然后提供查询就好了;

(3)随机数

    /**      *  获取随机数      * @param length      * @return      */     @PostMapping("/getRandomStr")     public  String getRandomStr(int length) {         String base ="fdajfkdajsklfjafdkjxjkljfadnfdnamn12687";         int randomNum;         char randomChar;         Random random =new Random();         StringBuffer str =new StringBuffer();         for (int i =0; i < length; i++) {             randomNum = random.nextInt(base.length());             randomChar = base.charAt(randomNum);             str.append(randomChar);         }         return str.toString();     } 

(4) 获取签名,完结

  /**      *   入参为url      * @param reqJson      * @return      */     @PostMapping("/getWXSign")     public String getWXSign(@RequestBody JSONObject reqJson) {         String url = reqJson.getString("url");         long timeStampSec = System.currentTimeMillis() /1000;         String timestamp = String.format("%010d", timeStampSec);         String nonceStr = getRandomStr(8);         String[] urls = url.split("#");         String newUrl = urls[0];         JSONObject respJson =new JSONObject();         String[] signArr =new String[]{"url=" + newUrl,"jsapi_ticket=" + getWXJsapiTicket(getWXaccessToken()),"noncestr=" + nonceStr,"timestamp=" + timestamp};         Arrays.sort(signArr);         String signStr = StringUtils.join(signArr,"&");         String resSign = DigestUtils.sha1Hex(signStr);         respJson.put("appId", appId);         respJson.put("timestamp", timestamp);         respJson.put("nonceStr", nonceStr);         respJson.put("signature", resSign);          return respJson.toJSONString();     } 

简要说明:简单来说就是获取jsapiticket,然后值排序,做下sha1加密就可以了。


完整类:

测试,使用的使用根据自己业务场景,稍作改动吧

@RestController @RequestMapping("/api/wx") @Api(value = "测试") public class WXShareController {     private static final Logger logger = LoggerFactory.getLogger(WXShareController.class);     private static final String appId ="ruanjianlaowang";     private static final String secret ="dashuaige";     RestTemplate restTemplate = new RestTemplate();      /**      *   入参为url      * @param reqJson      * @return      */     @PostMapping("/getWXSign")     public String getWXSign(@RequestBody JSONObject reqJson) {         String url = reqJson.getString("url");         long timeStampSec = System.currentTimeMillis() /1000;         String timestamp = String.format("%010d", timeStampSec);         String nonceStr = getRandomStr(8);         String[] urls = url.split("#");         String newUrl = urls[0];         JSONObject respJson =new JSONObject();         String[] signArr =new String[]{"url=" + newUrl,"jsapi_ticket=" + getWXJsapiTicket(getWXaccessToken()),"noncestr=" + nonceStr,"timestamp=" + timestamp};         Arrays.sort(signArr);         String signStr = StringUtils.join(signArr,"&");         String resSign = DigestUtils.sha1Hex(signStr);         respJson.put("appId", appId);         respJson.put("timestamp", timestamp);         respJson.put("nonceStr", nonceStr);         respJson.put("signature", resSign);          return respJson.toJSONString();     }      /**      *  入参为token,返回ticket      * @param token      * @return      */     @PostMapping("/getWXJsapiTicket")     public String getWXJsapiTicket(String token) {         String ticket = null;         if (StringUtils.isBlank(ticket)) {             String url ="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + token +"&type=jsapi";             String resp = restTemplate.getForObject(url, String.class);             JSONObject resJson = JSONObject.parseObject(resp);             return resJson.getString("ticket");         }         return ticket;     }      /**      *  获取token      * @return      */     @PostMapping("/getWXaccessToken")     public String getWXaccessToken() {         String url ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appId+"&secret=" +secret;         String resp = restTemplate.getForObject(url, String.class);         JSONObject resJson = JSONObject.parseObject(resp);         return resJson.getString("access_token");     }      /**      *  获取随机数      * @param length      * @return      */     @PostMapping("/getRandomStr")     public  String getRandomStr(int length) {         String base ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";         int randomNum;         char randomChar;         Random random =new Random();         StringBuffer str =new StringBuffer();         for (int i =0; i < length; i++) {             randomNum = random.nextInt(base.length());             randomChar = base.charAt(randomNum);             str.append(randomChar);         }         return str.toString();     } }  

更多信息请关注@软件老王,关注不迷路,软件老王和他的IT朋友们,分享一些他们的技术见解和生活故事。

科学上网软件全解析:从入门到精通的终极指南

在当今数字化时代,互联网已成为人们获取信息、娱乐和社交的重要渠道。然而,由于地区限制、网络审查和隐私泄露等问题,许多用户发现自己的网络体验受到了严重影响。科学上网软件应运而生,成为突破这些限制的利器。本文将全面解析科学上网的必要性、主流工具的选择与使用技巧,帮助你找到最适合自己的解决方案。

一、为什么我们需要科学上网?

1.1 突破地区限制,解锁全球内容

许多热门平台如Netflix、HBO Max、BBC iPlayer等会根据用户的地理位置限制内容的访问。例如,某些美剧可能只在特定国家上线,而科学上网软件可以通过切换IP地址,让你轻松绕过这些限制,享受全球化的内容资源。

1.2 保护个人隐私,防止数据泄露

谷歌、Facebook等科技巨头会收集用户的浏览习惯、位置信息甚至支付记录,这些数据可能被用于广告推送或更糟糕的商业用途。VPN等科学上网工具通过加密你的网络流量,使你的在线活动更加私密,减少被追踪的风险。

1.3 优化网络速度,提升体验

在某些地区,由于网络管制或ISP(互联网服务提供商)的限制,访问国际网站时可能会出现严重的延迟。而优质的VPN或代理服务可以通过优化路由,减少网络拥堵,从而提升访问速度。

二、科学上网软件的主要类型

2.1 VPN(虚拟私人网络)

VPN是目前最流行的科学上网方式之一,它通过加密你的网络连接并隐藏真实IP地址,确保你的数据安全和匿名性。常见的VPN服务包括:
- ExpressVPN:以高速和稳定性著称,适合流媒体解锁。
- NordVPN:提供双重加密和自动终止开关(Kill Switch),安全性极高。
- Surfshark:性价比高,支持无限设备同时连接。

2.2 Shadowsocks(SS)与ShadowsocksR(SSR)

相较于传统VPN,Shadowsocks采用更轻量级的代理技术,特别适合网络游戏和高速下载需求。SSR则在SS的基础上增加了混淆功能,使其更难被检测和封锁。

2.3 V2Ray

V2Ray是一种更高级的科学上网方案,支持多种传输协议(如WebSocket、mKCP等),灵活性极高,适合技术爱好者。它的抗封锁能力极强,但配置较为复杂,新手可能需要教程辅助。

三、如何选择适合自己的科学上网工具?

3.1 明确需求

  • 解锁流媒体:优先选择支持Netflix、Disney+等平台的VPN,如ExpressVPN。
  • 隐私保护:注重加密技术的服务,如NordVPN或ProtonVPN。
  • 高速稳定:适合游戏或下载的SSR/V2Ray方案。

3.2 预算考量

免费VPN虽然诱人,但往往存在速度慢、广告多甚至数据泄露的风险。付费服务通常提供更稳定的连接和更好的客服支持,年付套餐通常更划算。

3.3 技术支持与口碑

查看用户评价和专业评测,确保所选服务商有良好的信誉。在线客服响应速度、技术文档的完善程度也是重要参考因素。

四、热门科学上网软件深度评测

4.1 ExpressVPN

优点
- 服务器覆盖94个国家,解锁流媒体效果极佳。
- 采用TrustedServer技术,确保数据不留存日志。
- 连接速度快,适合4K视频播放。

缺点
- 价格较高,适合预算充足的用户。

4.2 NordVPN

优点
- 提供双重VPN和Onion Over VPN功能,安全性顶级。
- 庞大的服务器网络,适合多地区切换需求。

缺点
- 部分服务器在高峰时段可能出现速度波动。

4.3 V2Ray

优点
- 抗封锁能力强,适合严格审查地区。
- 高度可定制化,满足技术用户需求。

缺点
- 配置复杂,新手需学习教程。

五、科学上网软件使用指南

5.1 下载与安装

  • 从官方网站下载软件,避免第三方来源的潜在风险。
  • 安装时注意权限设置,确保防火墙不会误拦截。

5.2 配置与优化

  • 选择距离较近或负载较低的服务器以获得最佳速度。
  • 启用Kill Switch功能,防止意外断线导致IP泄露。

5.3 常见问题解决

  • 无法连接:尝试更换协议(如从TCP切换到UDP)。
  • 速度慢:关闭不必要的后台程序或更换服务器节点。

六、法律与道德考量

不同国家对科学上网的监管政策各异,某些地区可能限制VPN的使用。建议用户在合法范围内合理使用这些工具,避免用于非法活动。

结语:科学上网,让网络回归自由

科学上网不仅是技术手段,更是信息自由的重要保障。通过本文的详细解析,希望你能找到最适合自己的工具,享受更开放、更安全的互联网体验。无论是为了追剧、保护隐私还是提升网速,科学上网软件都能成为你的得力助手。

精彩点评
科学上网软件如同一把双刃剑,既能为用户打开信息世界的大门,也可能因不当使用带来风险。本文不仅提供了全面的工具评测,还强调了合法合规的重要性,体现了技术与责任并重的理性视角。在信息日益封闭的今天,掌握科学上网技能无疑是一种数字时代的生存智慧。

版权声明:

作者: freeclashnode

链接: https://www.freeclashnode.com/news/article-3743.htm

来源: FreeClashNode

文章版权归作者所有,未经允许请勿转载。

免费节点实时更新

热门文章

最新文章

归档