小程序禁止外部直接跳转某页面
HullQin 人气:0背景
小程序也需要注意安全性。例如某些页面是业务流程中的「第二步」、「第三步」,而非「第一步」。如果外部小程序、外部二维码、链接直接跳转到了我们小程序的「第二步」、「第三步」,可能有超出预期的事情发生。
针对外部跳转到小程序「第二步」、「第三步」页面的,我们应该拦截掉,要么直接报错:页面来源非法,要么直接跳回首页。
关键问题在于,这种逻辑该怎么实现最优雅呢?
一种不是很好的解决方案
如果你的业务流程采用了状态机模型,并在后端存储了状态,那么可以在每一个页面onLoad时,发送一个API请求,判断当前状态和当前页面是否匹配,如果匹配,则正常访问,如果不匹配,则跳回到状态对应的页面。
这依赖于后端实现,不太合适。
更好的解决方案
我们考虑纯前端的实现。
问题关键在于:我们要禁止外部直接跳转到我们小程序的部分页面。我们需要要区分:内部跳转、外部跳转。
这部分页面,只允许通过内部跳转API(wx.redirectTo
、wx.navigateTo
)来跳转,其它方式都不应跳转到。
只要我们在调用wx.redirectTo
、wx.navigateTo
时,都加上一个特殊参数
。然后在页面的onLoad里面判断,是否包含了该特殊参数
,包含该特殊参数
,表明是内部跳转,不包含该特殊参数
,表明是外部跳转。
这个特殊参数
不可以被猜到,如果被猜到了,那么外部跳转时带上特殊参数
,该方案就失效了。
所以,这个特殊参数
必须不是固定的,要是随机的。
我们可以参考WEB中针对CSRF的解决方案,如果使用随机的特殊参数
,让外部无法猜到这个特殊参数
,那么问题就解决了。
特殊参数
什么时候生成呢?可以在App onLaunch时生成,也可以在「第一步」页面onLoad时生成。不过不论怎样,这个特殊参数
都需要作为全局变量保存在内存中,方便随时引用和判断。
具体怎么做?建议你先阅读下文章:《如何全局重写小程序 Page函数 wx对象?》,学会这种方法,我们再来看下方的代码。
全局改写Page的onLoad生命周期,增加校验
const WHITE_LIST = ['pages/index']; function onLoadProxy(onLoad) { return function (query) { const app = getApp(); // 以下是token拦截逻辑: if (WHITE_LIST.includes(this.route)) { // 在允许外部跳转来的白名单页面,生成随机数validEntranceToken app.validEntranceToken = `${new Date().getTime()}${Math.random().toString(36)}`; } else if (query.validEntranceToken !== app.validEntranceToken) { // 其它页面,校验参数token是否与全局变量中token一致,若不一致,跳转到报错页面 wx.redirectTo({ url: `/pages/fail` }); return; } // 未被拦截,表明是正常来源。以下是正常流程: if (onLoad) { return onLoad.call(this, query); } }; } const PageProxy = (Page) => function (options) { const newOptions = { ...options, onLoad: onLoadProxy(options.onLoad), }; Page(newOptions); }; Page = PageProxy(Page);
全局改写wx.navigateTo方法,附带参数
function addValidEntranceToken(url) { const app = getApp(); const symbol = url.includes('?') ? '&' : '?'; return `${url}${symbol}validEntranceToken=${app.validEntranceToken}`; } export function redirectToProxy(redirectTo) { return function (object) { return redirectTo({ ...object, url: addValidEntranceToken(object.url), }); }; } function wxProxy(wx) { const newWx = { ...wx }; newWx.navigateTo = redirectToProxy(wx.navigateTo); newWx.redirectTo = redirectToProxy(wx.redirectTo); return newWx; } wx = wxProxy(wx);
解释
我们通过修改所有Page的onLoad方法,当用户访问「白名单」页面时,不会做任何拦截,而是直接生成一个随机的validEntranceToken
。当用户访问「白名单」以外的页面时,则会判断参数中是否包含正确的validEntranceToken
,若不包含,则会跳转到报错页。若包含,则继续执行该页面的其它逻辑。
此外,我们还修改了原生的wx.navigateTo
和wx.redirectTo
,当我们做内部跳转时,会自动带上刚才提到的参数validEntranceToken
。
做好这些事情后,开发时,无需关心细节(意思是这套逻辑针对业务代码是0侵入的),只需要关注「白名单」页面有哪些即可。可谓是最优雅的方案了。
总结
加载全部内容