Hash, PushState 和微信 JSSDK 授权

Stella981
• 阅读 540

最近将 riot.js 升级到了 3.0,并用上了新版本的 riot-route,原先用了一年多的 2.2.4 版本内置的 riot.route 只支持 hash 形式的 SPA 单页面应用,riot-route 则支持 pushState。

Hash 方式有个缺点,就是服务器不知道地址栏中 # 之后的内容,放在微信里,就导致了未授权用户授权后想返回原界面需要借助 JS 来实现,导致历史记录多了一条,用户按返回键无法退出(返回上一页面后又被 JS “前进”了)。

PushState 方式就没有这个问题,可以直接用 HTTP 302 重定向过来,不影响历史记录和返回按钮功能。

于是在新项目中开始采用 pushState 方式。

不过等到应用到了微信环境中,又冒出来一个问题,通过 pushState 改变地址在 iOS 中会导致 JSSDK 的授权失败。

在 config 中开启 debug,可见 invalid signature 。原因是 location.href 中的地址变化了,但是微信客户端认为地址还是打开页面时的地址,微信“复制链接”得到的地址可以作为旁证。

解决方法

先按标准方法用当前地址计算 signature,如果失败,再用打开浏览器时的地址计算 signature。

由于之前已经把加载微信 JSSDK 和相关 Api 的功能独立成函数,因此这次解决起来也比较简单。

修改前的代码
function runWx(api, fn, loadError) {
    if (typeof api === 'string') {
        api = [api];
    }

    // 用 require.js 加载 JSSDK 文件,如果你已经用 <script> 方式加载,可以省掉这一步
    require(['jweixin'], function (wx) {
        var url = location.href.split('#')[0];
        
        // 将 url 发往服务器计算相应的 signature
        $.get('/signature', { url: url }, function (ans) {
            // wx.config 执行成功时调用
            wx.ready(function () {
                wx.checkJsApi({
                    jsApiList: _.clone(api), // 坑,微信会改变此参数的内容
                    success: function success(res) {
                        if (!res || !res.checkResult || _.any(api, function (v) {
                            return !res.checkResult[v];
                        })) {
                            alert('您的微信版本过低,无法使用此功能,请升级微信');
                            return;
                        }
                        fn(wx);
                    }
                });
            });

            // wx.config 执行失败时调用
            wx.error(function () {
                alert('授权失败,您可能无法使用部分功能');
            });

            // 校验用的参数来自服务器
            var config = {
                // debug: true,
                appId: ans.data.appId,
                timestamp: ans.data.timestamp,
                nonceStr: ans.data.nonceStr,
                signature: ans.data.signature,
                jsApiList: _.union(['checkJsApi'], api)
            };

            // 进行校验
            wx.config(config);
        });
    }, loadError);
}

先简单展示一下这个 runWx 函数的用法:

// 当需要用到特定的微信接口时,运行 runWx
runWx(['uploadImage', 'chooseImage'], function (wx) {
    // 可以在这里使用 wx.uploadImage 和 wx.chooseImage 功能
    wx.uploadImage(...);
    wx.chooseImage(...);
}, function () {
    // 加载失败时要做的事
})

runWx 做了这么几件事:

  • 用 require.js 加载 weixin sdk 的 js 文件
  • 获取当前页面地址 location.href.split('#')[0]
  • 将 url 作为参数发送到服务器计算出 signature
  • 调用 wx.config
  • 成功时调用 wx.ready 并最终调用回调函数 fn
  • 失败时调用 wx.error 提示错误
修改后的代码
// 解决部分机型 pushState 不能正确改变地址导致授权失败

// 在第一次打开页面时加载此文件,记录当时的地址作为原始地址
var originUrl = location.href.split('#')[0];

// 增加 tryOrigin 参数
function runWx(api, fn, loadError, tryOrigin) {
    if (typeof api === 'string') {
        api = [api];
    }
    require(['jweixin'], function (wx) {
        // tryOrigin 为真时使用原始地址
        var url = tryOrigin ? originUrl : location.href.split('#')[0];
        
        $.get('/signature', { url: url }, function (ans) {
            wx.ready(function () {
                wx.checkJsApi({
                    jsApiList: _.clone(api), // 坑,微信会改变此参数的内容
                    success: function success(res) {
                        if (!res || !res.checkResult || _.any(api, function (v) {
                            return !res.checkResult[v];
                        })) {
                            alert('您的微信版本过低,无法使用此功能,请升级微信');
                            return;
                        }
                        fn(wx);
                    }
                });
            });

            wx.error(function () {
                // 已经试了原始地址
                if (originUrl === url) {
                    alert('授权失败,您可能无法使用部分功能');
                    return;
                }
                // 没有使用原始地址且 signature 不匹配,尝试用原始地址计算 signature
                runWx(api, fn, loadError, true);
            });

            var config = {
                debug: true,
                appId: ans.data.appId,
                timestamp: ans.data.timestamp,
                nonceStr: ans.data.nonceStr,
                signature: ans.data.signature,
                jsApiList: _.union(['checkJsApi'], api)
            };
            wx.config(config);
        });
    }, loadError);
}

和原来相比,主要变化有:

  • 记录了打开浏览器时的原始地址
  • 先尝试用标准的 location.href.split('#')[0] 计算 signature
  • 失败时用 originUrl 再试一次

这个改动的主要优点是原来用到 runWx 的地方,代码完全不需要进行变动,由 runWx 自己去尝试解决问题。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
梦
3年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
2年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这