HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

iOS平台微信SDK更新需要配置通用链接(Universal Links)

iOS 通用链接

本文档已过期,适用于:本地离线打包或者由于某种原因你需要用传统的方式:私有化部署服务器来托管apple-app-site-association文件创建通用链接。

推荐使用:一键生成iOS通用链接

从HBuilderX 2.3.4版本开始,微信SDK(登录、分享、支付)更新到openSDK1.8.6。

由于苹果iOS 13系统版本安全升级,微信SDK1.8.6版本要求支持Universal Links方式跳转,以便进行合法性校验,提升安全性。更多详情请参考微信官方说明

如果不配置通用链接,使用新版本HX提交云端打包会失败,提示以下错误信息:

Error code = -5000  
Error message:   
Error: not set parameter 'UniversalLinks' @'oauth-weixin'

第一步:配置应用支持通用链接

参考:iOS平台配置通用链接(Universal Link)

以上配置完成可确定通用链接地址,如“https://demo.dcloud.net.cn/ulink/”(要求必须使用https协议,以"/"结尾,此链接仅为文档示例,实际应用请根据自己的配置填写

第二步:在微信开放平台配置通用链接

打开微信开发平台,在“管理中心”页面的“移动应用”下找到已经申请的应用(没有申请应用请点击“创建移动应用”新建应用),点击“查看”打开应用详情页面。
在“开发信息”栏后点击修改,在“iOS应用”下的“Universal Links”项中配置应用的通用链接,如下图所示:

第三步:在HBuilderX中配置通用链接提交云端打包

将上一步在微信开放平台配置的通用链接地址配置到应用manifest.json中,注意通用链接地址必须完全一致(本地离线打包忽略此操作)
打开项目的manifest.json文件,在“(App) SDK配置”项中的微信登录(微信分享、微信支付)下的“iOS平台通用链接(Universal Links)”中配置:

如果使用了微信分享和微信支付,也需要分别配置微信分享和微信支付下的“iOS平台通用链接(Universal Links)”,并且配置的值必须相同


保存后需提交云端打包生效。

本地离线打包配置微信通用链接

参考:https://ask.dcloud.net.cn/article/309#ulink

继续阅读 »

本文档已过期,适用于:本地离线打包或者由于某种原因你需要用传统的方式:私有化部署服务器来托管apple-app-site-association文件创建通用链接。

推荐使用:一键生成iOS通用链接

从HBuilderX 2.3.4版本开始,微信SDK(登录、分享、支付)更新到openSDK1.8.6。

由于苹果iOS 13系统版本安全升级,微信SDK1.8.6版本要求支持Universal Links方式跳转,以便进行合法性校验,提升安全性。更多详情请参考微信官方说明

如果不配置通用链接,使用新版本HX提交云端打包会失败,提示以下错误信息:

Error code = -5000  
Error message:   
Error: not set parameter 'UniversalLinks' @'oauth-weixin'

第一步:配置应用支持通用链接

参考:iOS平台配置通用链接(Universal Link)

以上配置完成可确定通用链接地址,如“https://demo.dcloud.net.cn/ulink/”(要求必须使用https协议,以"/"结尾,此链接仅为文档示例,实际应用请根据自己的配置填写

第二步:在微信开放平台配置通用链接

打开微信开发平台,在“管理中心”页面的“移动应用”下找到已经申请的应用(没有申请应用请点击“创建移动应用”新建应用),点击“查看”打开应用详情页面。
在“开发信息”栏后点击修改,在“iOS应用”下的“Universal Links”项中配置应用的通用链接,如下图所示:

第三步:在HBuilderX中配置通用链接提交云端打包

将上一步在微信开放平台配置的通用链接地址配置到应用manifest.json中,注意通用链接地址必须完全一致(本地离线打包忽略此操作)
打开项目的manifest.json文件,在“(App) SDK配置”项中的微信登录(微信分享、微信支付)下的“iOS平台通用链接(Universal Links)”中配置:

如果使用了微信分享和微信支付,也需要分别配置微信分享和微信支付下的“iOS平台通用链接(Universal Links)”,并且配置的值必须相同


保存后需提交云端打包生效。

本地离线打包配置微信通用链接

参考:https://ask.dcloud.net.cn/article/309#ulink

收起阅读 »

Hello Hbuilder ,i'm coming.

Hello uni-app,i'm coming.Studing and testing……

Hello uni-app,i'm coming.Studing and testing……

直播定位,钉钉打卡,友圈改位都可用

游戏 后台定位 直播


首先要有一个硬壳芯片

插上手机后就可以随意改位置了,还可以边充电边使用的哦

操作简便谁都可以上手操作,想改哪里就改哪里。最主要的是安全,主要原理很简单,就是把手机的GPS定位给更换了,可以让手机随意定位全国的任何一个位置,这个不是挂,只是一个更换手机GPS定位的,整个手机的定位相当于都更换了,手机内的所有软件都生效,比如你朋友圈附近人都更换成定位的位置了!

功能之所以强大,就是装个X ,发个朋友圈,找个附近的人陌陌都可以的哦。身在农村的老屈,也终于可以找大城市附近的人聊聊感受了!真的是装X必备神器!更多功能还需要各位老铁去开发。

异地搞个直播,钉钉签个到打个卡都是这样的实现的,所以为什么那么多人可以在家轻松的打卡上班,正是因为他们有了一个这个!就连现在火热的一起来捉妖,靠虚拟定位才能抓妖的游戏,都能在家不出门捉妖了!真的是硬了那句话!

上有对策下有政策!好了我的世界你曾经来过,什么时候我上你的世界走走!笑看世间百态,乐观万里浮云!

继续阅读 »


首先要有一个硬壳芯片

插上手机后就可以随意改位置了,还可以边充电边使用的哦

操作简便谁都可以上手操作,想改哪里就改哪里。最主要的是安全,主要原理很简单,就是把手机的GPS定位给更换了,可以让手机随意定位全国的任何一个位置,这个不是挂,只是一个更换手机GPS定位的,整个手机的定位相当于都更换了,手机内的所有软件都生效,比如你朋友圈附近人都更换成定位的位置了!

功能之所以强大,就是装个X ,发个朋友圈,找个附近的人陌陌都可以的哦。身在农村的老屈,也终于可以找大城市附近的人聊聊感受了!真的是装X必备神器!更多功能还需要各位老铁去开发。

异地搞个直播,钉钉签个到打个卡都是这样的实现的,所以为什么那么多人可以在家轻松的打卡上班,正是因为他们有了一个这个!就连现在火热的一起来捉妖,靠虚拟定位才能抓妖的游戏,都能在家不出门捉妖了!真的是硬了那句话!

上有对策下有政策!好了我的世界你曾经来过,什么时候我上你的世界走走!笑看世间百态,乐观万里浮云!

收起阅读 »

深度解析移动应用安全的四大常见问题及解决方案

当前移动安全中恶意攻击的现状可以归结为四个主要方向:网络安全、数据安全、代码安全、设备安全。

网络安全

网络安全分为四个部分:数据防泄露、请求防重放、内容防篡改、身份防伪装。

解决数据防泄露的关键在于一定要对数据进行加密处理。请求防重放则可以通过请求时在参数中携带时间戳、随机数、流水号、“时间戳+流水号”这四种方式措施来予以防护。

内容防篡改需要我们对内容加盐哈希,再在服务端校验哈希值。身份防伪装有两种解决方案。方案一是Token身份认证:给Token一个有效期,防止Token泄露之后,攻击者其可以长期非法调用。

方案二是数字签名:通信过程中发送方对通信内容进行Hash生成摘要,然后用自己的私钥进行加密生成数字签名一起发送给接收方,接收方接收到后用自己的公钥进行解密来验证发送方的真实性,通过比对自己发送方的摘要信息与自己计算得出的摘要信息来验证内容是否被篡改。

数据安全

针对存储的安全防护,我们在数据落地的时候一定要进行加密处理,防止他人拿到重要的敏感数据。其次,我们要创建私有类型数据,尽量用private的创建方式,而不是全局的创建方式。

代码安全

常见逆向工程套路主要分为三种:反编译、脱壳、动态分析。以下方式可以防止代码反编译:代码混淆、签名验证、利用反编译工具漏洞进行防护、加固。防动态分析则可以从反调试、反Xposed、反root三个维度进行入手。

设备安全

开发者可以通过检查设备所处的环境来判断设备是否处于异常的状态。如果设备状态异常,则很可能处于黑产工具的控制下。常见的黑产工具包括“手机卡商与接码平台”、改机工具、打码平台以及群控系统。

黑产的攻击场景

黑产的攻击场景主要有以下四种:渠道推广、登录注册、营销活动、社区互动。在渠道推广环节,我们可能会遇到自动化批量刷量的黑产设备。它们会伪造虚假激活,让我们的钱白白浪费。

在登录注册之时,黑产方会对我们进行撞库攻击,并提供大量的垃圾账号进行注册。到了营销活动环节,攻击者可以针对APP优惠活动,提供大量的账号薅羊毛。社区互动过程中,直播刷榜、发送垃圾广告都是黑产攻击的常见形式。

防范黑产的措施

我们首先可以对手机上的设备信息进行全方面的检查。检查维度包括设备信息有无被篡改、手机上root环境是否处于root环境、有无安装一个Xposed的框架、系统是否被修改过、是否处于虚拟机的环境、部分地理位置异常是是否频繁。

检查后对这些风控数据予以记录,并把这些数据提交给风控引擎进行多维度的分析,比如可以对它进行IP风险评分、IP画像以及判断手机号是否在黑名单。我们通过多维度的模型标识高风险的用户、风险等级并告知客户端,以此进行一些防护。

防范黑产的案例

以渠道质量评估为例,渠道刷量分为机器刷量和人工刷量。机器刷量的特点是用群控系统配合一个改机工具,全自动化的下载和激活。其自动化程度比较高,但是我们可以通过识别设备信息、分析机器操作与人操作的差异化特征,从而标识出机器刷量的情况。

而人工刷量一般通过众包平台、IM群等下发任务的方式进行。其特点是人员分散、设备真实,但技术能力偏弱。人工刷量下设备会频繁地卸载和安装,应用的活跃程度跟以往不一样,处于异常状态。我们可以结合用户画像和这些特点标识这一类人工刷量的行为。

个验

为帮助开发者智能识别安全风险,个推在移动安全领域也推出了相应的解决方案-“个验”。个验是个推面向开发者推出的“一键认证”SDK,可以帮助APP开发者实现用户一键免密登录,简化APP登录流程,有效减少用户流失并提升转化。

作为新一代的验证解决方案,个推一键认证的功能特性更体现在能为APP开发者提供风险识别和风险防护的系列方案。

在风险识别方面,个验可以通过设备、网络、行为等多维度识别风险设备与黑产人群。准确识别出风险后,企业可提前对其加以防范,有效降低运营风险。在风险防护方面,个验在不影响正常用户使用的情况下,可以通过动画验证码阻止恶意自动化程序的进一步操作。

结语

如今,黑产攻击造成资产或声誉损失的现场此起彼伏,设备风险控制任重道远。个推将持续挖掘其丰富的数据资产,不断打磨自身技术,帮助APP开发者有效识别风险设备、保障业务安全。

继续阅读 »

当前移动安全中恶意攻击的现状可以归结为四个主要方向:网络安全、数据安全、代码安全、设备安全。

网络安全

网络安全分为四个部分:数据防泄露、请求防重放、内容防篡改、身份防伪装。

解决数据防泄露的关键在于一定要对数据进行加密处理。请求防重放则可以通过请求时在参数中携带时间戳、随机数、流水号、“时间戳+流水号”这四种方式措施来予以防护。

内容防篡改需要我们对内容加盐哈希,再在服务端校验哈希值。身份防伪装有两种解决方案。方案一是Token身份认证:给Token一个有效期,防止Token泄露之后,攻击者其可以长期非法调用。

方案二是数字签名:通信过程中发送方对通信内容进行Hash生成摘要,然后用自己的私钥进行加密生成数字签名一起发送给接收方,接收方接收到后用自己的公钥进行解密来验证发送方的真实性,通过比对自己发送方的摘要信息与自己计算得出的摘要信息来验证内容是否被篡改。

数据安全

针对存储的安全防护,我们在数据落地的时候一定要进行加密处理,防止他人拿到重要的敏感数据。其次,我们要创建私有类型数据,尽量用private的创建方式,而不是全局的创建方式。

代码安全

常见逆向工程套路主要分为三种:反编译、脱壳、动态分析。以下方式可以防止代码反编译:代码混淆、签名验证、利用反编译工具漏洞进行防护、加固。防动态分析则可以从反调试、反Xposed、反root三个维度进行入手。

设备安全

开发者可以通过检查设备所处的环境来判断设备是否处于异常的状态。如果设备状态异常,则很可能处于黑产工具的控制下。常见的黑产工具包括“手机卡商与接码平台”、改机工具、打码平台以及群控系统。

黑产的攻击场景

黑产的攻击场景主要有以下四种:渠道推广、登录注册、营销活动、社区互动。在渠道推广环节,我们可能会遇到自动化批量刷量的黑产设备。它们会伪造虚假激活,让我们的钱白白浪费。

在登录注册之时,黑产方会对我们进行撞库攻击,并提供大量的垃圾账号进行注册。到了营销活动环节,攻击者可以针对APP优惠活动,提供大量的账号薅羊毛。社区互动过程中,直播刷榜、发送垃圾广告都是黑产攻击的常见形式。

防范黑产的措施

我们首先可以对手机上的设备信息进行全方面的检查。检查维度包括设备信息有无被篡改、手机上root环境是否处于root环境、有无安装一个Xposed的框架、系统是否被修改过、是否处于虚拟机的环境、部分地理位置异常是是否频繁。

检查后对这些风控数据予以记录,并把这些数据提交给风控引擎进行多维度的分析,比如可以对它进行IP风险评分、IP画像以及判断手机号是否在黑名单。我们通过多维度的模型标识高风险的用户、风险等级并告知客户端,以此进行一些防护。

防范黑产的案例

以渠道质量评估为例,渠道刷量分为机器刷量和人工刷量。机器刷量的特点是用群控系统配合一个改机工具,全自动化的下载和激活。其自动化程度比较高,但是我们可以通过识别设备信息、分析机器操作与人操作的差异化特征,从而标识出机器刷量的情况。

而人工刷量一般通过众包平台、IM群等下发任务的方式进行。其特点是人员分散、设备真实,但技术能力偏弱。人工刷量下设备会频繁地卸载和安装,应用的活跃程度跟以往不一样,处于异常状态。我们可以结合用户画像和这些特点标识这一类人工刷量的行为。

个验

为帮助开发者智能识别安全风险,个推在移动安全领域也推出了相应的解决方案-“个验”。个验是个推面向开发者推出的“一键认证”SDK,可以帮助APP开发者实现用户一键免密登录,简化APP登录流程,有效减少用户流失并提升转化。

作为新一代的验证解决方案,个推一键认证的功能特性更体现在能为APP开发者提供风险识别和风险防护的系列方案。

在风险识别方面,个验可以通过设备、网络、行为等多维度识别风险设备与黑产人群。准确识别出风险后,企业可提前对其加以防范,有效降低运营风险。在风险防护方面,个验在不影响正常用户使用的情况下,可以通过动画验证码阻止恶意自动化程序的进一步操作。

结语

如今,黑产攻击造成资产或声誉损失的现场此起彼伏,设备风险控制任重道远。个推将持续挖掘其丰富的数据资产,不断打磨自身技术,帮助APP开发者有效识别风险设备、保障业务安全。

收起阅读 »

uni-app自定义模态对话框uniPop组件

uniapp插件 uniapp

基于UniApp自定义弹出框uniPop组件、实现了uniapp仿微信、android、ios弹窗效果

uniPop组件含有多种动画效果、皮肤类型ios/android、可以自定义弹窗样式/自定义多按钮及事件/弹窗显示位置、自动关闭秒数、遮罩层透明度及点击遮罩是否关闭

如下图:H5/小程序/App三端效果兼容性一致。(后续大图均展示App端)

uniPop组件引入方式
以下两种引入方式均可:

  • 在main.js里引入全局组件
    import uniPop from './components/uniPop/uniPop.vue'
    Vue.component('uni-pop', uniPop)
  • 在页面引入组件
<template>  
    <view class="container">  
        ...  

        <!-- 弹窗模板 -->  
        <uni-pop ref="uniPop"></uni-pop>  
    </view>  
</template>  

<script>  
    import uniPop from './components/uniPop/uniPop.vue'  
    export default {  
        data() {  
            return {  
                ...  
            }  
        },  
        components:{  
            uniPop  
        },  
        ...  
    }  
</script>

调用方式 this.$refs.uniPop.show({...})

this.$refs.uniPop.show({  
    content: 'msg消息提示框(5s后窗口关闭)',  
    shade: true,  
    shadeClose: false,  
    time: 5,  
    anim: 'fadeIn',  
})

uniPop.vue自定义弹窗模板

/**  
  * @tpl        uni-app自定义弹窗组件 - uniPop.vue  
  * @author     andy by 2019-09-20  
  * @about      Q:282310962  wx:xy190310  
  */  

<template>  
    <view v-if="opts.isVisible" class="uniPop" :class="opts.isCloseCls">  
        <view class="unipop__ui_panel">  
            <view v-if="opts.shade" class="unipop__ui_mask" @tap="shadeTaped"></view>  
            <view class="unipop__ui_main">  
                <view class="unipop__ui_child" :style="opts.style">  
                    <!-- 标题 -->  
                    <view v-if="opts.title" class="unipop__ui_tit">{{opts.title}}</view>  
                    <!-- 内容 -->  
                    <view v-if="opts.content" class="unipop__ui_cnt" :style="opts.contentStyle">  
                        {{opts.content}}  
                    </view>  
                    <view v-if="opts.btns" class="unipop__ui_btns">  
                        <text v-for="(item,index) in opts.btns" :key="index" class="btn" :style="item.style" @tap="btnTaped(item)">{{item.text}}</text>  
                    </view>  
                </view>  
                <!-- xclose -->  
                <view v-if="opts.xclose" class="unipop__xclose" @tap="close"></view>  
            </view>  
        </view>  
    </view>  
</template>
data() {  
    return {  
        defaultOptions: {  
            isVisible: false,       //是否显示弹窗  

            title: '',              //标题  
            content: '',            //内容  
            contentStyle: '',       //内容样式  
            style: null,            //自定义弹窗样式  
            skin: '',               //弹窗风格  
            icon: '',               //弹窗图标  
            xclose: false,          //自定义关闭按钮  

            shade: true,            //遮罩层  
            shadeClose: true,       //点击遮罩关闭  
            opacity: '',            //遮罩透明度  
            time: 0,                //自动关闭秒数  
            end: null,              //销毁弹窗回调函数  

            anim: 'scaleIn',        //弹窗动画  scaleIn(默认) | fadeIn | shake | top | right | bottom | left  
            position: '',           //弹窗位置  top | right | bottom | left  

            btns: null,             //弹窗按钮  
        },  
        opts: {},  
        timer: null  
    }  
},

作者:xiaoyan2015
链接: https://cloud.tencent.com/developer/column/3374
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

基于UniApp自定义弹出框uniPop组件、实现了uniapp仿微信、android、ios弹窗效果

uniPop组件含有多种动画效果、皮肤类型ios/android、可以自定义弹窗样式/自定义多按钮及事件/弹窗显示位置、自动关闭秒数、遮罩层透明度及点击遮罩是否关闭

如下图:H5/小程序/App三端效果兼容性一致。(后续大图均展示App端)

uniPop组件引入方式
以下两种引入方式均可:

  • 在main.js里引入全局组件
    import uniPop from './components/uniPop/uniPop.vue'
    Vue.component('uni-pop', uniPop)
  • 在页面引入组件
<template>  
    <view class="container">  
        ...  

        <!-- 弹窗模板 -->  
        <uni-pop ref="uniPop"></uni-pop>  
    </view>  
</template>  

<script>  
    import uniPop from './components/uniPop/uniPop.vue'  
    export default {  
        data() {  
            return {  
                ...  
            }  
        },  
        components:{  
            uniPop  
        },  
        ...  
    }  
</script>

调用方式 this.$refs.uniPop.show({...})

this.$refs.uniPop.show({  
    content: 'msg消息提示框(5s后窗口关闭)',  
    shade: true,  
    shadeClose: false,  
    time: 5,  
    anim: 'fadeIn',  
})

uniPop.vue自定义弹窗模板

/**  
  * @tpl        uni-app自定义弹窗组件 - uniPop.vue  
  * @author     andy by 2019-09-20  
  * @about      Q:282310962  wx:xy190310  
  */  

<template>  
    <view v-if="opts.isVisible" class="uniPop" :class="opts.isCloseCls">  
        <view class="unipop__ui_panel">  
            <view v-if="opts.shade" class="unipop__ui_mask" @tap="shadeTaped"></view>  
            <view class="unipop__ui_main">  
                <view class="unipop__ui_child" :style="opts.style">  
                    <!-- 标题 -->  
                    <view v-if="opts.title" class="unipop__ui_tit">{{opts.title}}</view>  
                    <!-- 内容 -->  
                    <view v-if="opts.content" class="unipop__ui_cnt" :style="opts.contentStyle">  
                        {{opts.content}}  
                    </view>  
                    <view v-if="opts.btns" class="unipop__ui_btns">  
                        <text v-for="(item,index) in opts.btns" :key="index" class="btn" :style="item.style" @tap="btnTaped(item)">{{item.text}}</text>  
                    </view>  
                </view>  
                <!-- xclose -->  
                <view v-if="opts.xclose" class="unipop__xclose" @tap="close"></view>  
            </view>  
        </view>  
    </view>  
</template>
data() {  
    return {  
        defaultOptions: {  
            isVisible: false,       //是否显示弹窗  

            title: '',              //标题  
            content: '',            //内容  
            contentStyle: '',       //内容样式  
            style: null,            //自定义弹窗样式  
            skin: '',               //弹窗风格  
            icon: '',               //弹窗图标  
            xclose: false,          //自定义关闭按钮  

            shade: true,            //遮罩层  
            shadeClose: true,       //点击遮罩关闭  
            opacity: '',            //遮罩透明度  
            time: 0,                //自动关闭秒数  
            end: null,              //销毁弹窗回调函数  

            anim: 'scaleIn',        //弹窗动画  scaleIn(默认) | fadeIn | shake | top | right | bottom | left  
            position: '',           //弹窗位置  top | right | bottom | left  

            btns: null,             //弹窗按钮  
        },  
        opts: {},  
        timer: null  
    }  
},

作者:xiaoyan2015
链接: https://cloud.tencent.com/developer/column/3374
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

真机调试无法连接到手机时解决

记录一下
根据真机运行常见问题:http://ask.dcloud.net.cn/article/97 各种排查都没有问题还是连接不上,
记录:清除之前hbuilderx的错误配置,重新来过.
执行hbuilderx安装目录下的 reset.bat命令.


后正常连接手机
注: 谨慎执行,之前的配置都会清空

继续阅读 »

记录一下
根据真机运行常见问题:http://ask.dcloud.net.cn/article/97 各种排查都没有问题还是连接不上,
记录:清除之前hbuilderx的错误配置,重新来过.
执行hbuilderx安装目录下的 reset.bat命令.


后正常连接手机
注: 谨慎执行,之前的配置都会清空

收起阅读 »

HBuilderX中配置JS/NVUE文件原生混淆加密

nvue加密 加密 原生混淆 Js原生混淆

此文档不再维护,请参考新文档地址:https://uniapp.dcloud.io/tutorial/app-sec-confusion

App的安装包都可以解压。前端资源,一般都是明文存放在安装包中,为防止解压后泄露敏感信息,需要进行安全处理。

由此DCloud提供了App端的js/nvue文件的原生混淆。5+ App/Wap2App支持对指定的js进行原生混淆。uni-app支持对指定的nvue文件原生混淆。

原生混淆后的安装包,解压后看到的都是乱码。

但需要注意:

  1. 没有绝对的安全,非常重要的信息,应该保存在服务器而不是前端
  2. 运行期对资源代码解密是影响执行性能的。不建议全包混淆,仅挑选需要保护的个别文件处理即可
  3. uni-app项目制作wgt包不支持原生混淆加密(即使配置也不会生效),HBuilderX3.1.0+版本后支持
  4. 为了保证加密数据的安全性,加密算法和key不对外公开,因此离线打包无法支持原生混淆加密,标准基座或自定义基座真机运行也不支持原生混淆加密(只有正式云打包才支持)

具体使用方式如下:

第一步、在manifest.json文件中配置要混淆的文件列表

打开manifest.json文件,切换到“源码视图”,按不同项目类型进行配置。

uni-app项目

uni-app的js运行在独立的jscore中,而不是webview中,所以不受iOS平台WKWebview不支持原生混淆的限制。
uni-app的vue页面中的js,是整体编译到一个大js文件中的,它经过编译,已经不再是vue源码了,但还不是乱码。对这个统一的大文件进行混淆会有影响性能。
所以uni-app只支持独立混淆nvue/js文件。

  • vue页面
    HBuilderX2.6.3+版本v3编译器支持对独立的js文件进行原生混淆,开发者可以将要保护的js代码写到独立的js文件中,在vue页面中使用import引用;如果此js同时被nvue页面import引用,则nvue页面也需要配置原生混淆才有效。另外main.js也可以原生混淆。
    老版本不支持vue页面的原生混淆,开发者只能将要保护的js代码写到nvue文件中进行保护。
  • nvue页面
    HBuilderX2.3.4+版本支持nvue文件的原生混淆。
    如果nvue页面引入了外部的js文件,会被一起原生混淆。但如果这个js还被其他不加密的文件引用,则该js仍然会暴露在安装包中。
  • vue页面和nvue页面同时使用加密js里的数据或方法(HBuilderX2.6.3+版本v3编译器)
    配置该js加密,并在App.vue中引用该js,把该js中的数据或方法赋值给全局对象,如globalData,vue和nvue中通过访问getApp访问共享数据或方法即可,无需配置nvue页面加密。

如果要发布多端的话,要保护的js最好写在app-plus的条件编译中,否则发布到其他端,还是无法原生混淆。

HBuilderX2.3.4版本开始,uni-app项目支持对nvue文件进行原生混淆

在"app-plus" -> "confusion" -> "resources"节点下添加要混淆的nvue文件列表:

    "app-plus": {   
        "confusion": {    
            "description": "NVUE原生混淆",    
            "resources": {    
                "pages/barcode/barcode.nvue": {     
                },     
                "pages/map/map.nvue": {     
                }     
            }     
        },    
        // ...    
    }

resource下的键名为nvue文件路径(相对于应用根目录),值为空JSON对象(大括号)。

<a id="vuejs"></a>
HBuilderX2.6.3+版本开始,uni-app项目使用v3编译器支持对vue页面中引用的js文件进行原生混淆

在manifest.json文件中添加要混淆的js文件列表:

    "app-plus": {   
        "confusion": {    
            "description": "原生混淆",    
            "resources": {    
                "common/test.js" : {}  
            }     
        },    
        // ...    
    }

在vue文件中引用混淆的js文件:

import test from '../common/test.js';  
//test.join();  //调用引用js中的方法

注意:uni-app中vue页面的webview组件支持加载使用加密混淆hybrid、static目录中的js文件,nvue页面的webview组件不支持。

5+ App/Wap2App项目

应用运行期间在页面打开时需要消耗更多时间进行混淆文件还原,为减少对运行速度的影响,5+App/wap2app仅支持对js文件进行原生混淆。
在"plus" -> "confusion" -> "resources"节点下添加要混淆的js文件列表:

    "plus": {   
        "confusion": {    
            "description": "JS原生混淆",    
            "resources": {    
                "js/common.js": {     
                },     
                "js/immersed.js": {     
                }     
            }     
        },    
        // ...    
    }

resource下的键名为js文件路径(相对于应用根目录),值为空JSON对象(大括号)。

<a id="wkwebview"></a>
HBuilderX2.6.11+版本开始,在iOS11+设备上使用WKWebview也可以支持JS原生混淆
WKWebview使用了更加严格的安全机制,使用原生混淆的js文件在html页面中必须使用自定义协议头plus-confusion://来引用:

<script type="text/javascript" src="plus-confusion://../js/common.js"></script>  
<!-- plus-confusion://后面为js文件路径,相对于当前html页面的路径 -->

在manifest.json的"plus" -> "confusion" -> "resources"节点下添加要混淆的js文件列表。
在"confusion"节点下添加 "supportWKWebview": true 支持WKWebview。
由于自定义协议仅在iOS11及以上设备才支持,建议配置应用支持的最低版本deploymentTarget为11.0:

    "plus": {   
        "confusion": {    
            "description": "JS原生混淆",   
            "supportWKWebview": true,   
            "resources": {    
                "js/common.js": {     
                }  
            }     
        },  
        "distribute": {  
            "apple": {  
                "deploymentTarget": "11.0"     //设置应用仅支持iOS11及以上设备  
                //...  
            }  
        }  
        // ...    
    }

注意:iOS平台WKWebview需iOS11+系统才支持原生混淆。5+App/wap2app项目,如果要兼容iOS11以下设备只能强制使用UIWebview内核,但苹果将要废弃UIWebview(详情)。如对原生混淆很重视,从长远考虑,建议改造升级uni-app

第二步、提交云端打包

配置好原生混淆的文件列表后,需要提交云端打包,注意在App云端打包对话框中需要勾选“对配置的js文件进行原生混淆”

再次强调:为了保证加密数据的安全性,加密算法和key不对外公开,因此离线打包无法支持原生混淆。
熟悉原生的开发者可将敏感信息存放于原生代码中,再与js进行交互。

对安全性要求较高的开发者,除了对前端js进行加密外,还应该对整个apk再进行一次加固。市面上很多加固服务可以选择,比如360加固、爱加密等。

继续阅读 »

此文档不再维护,请参考新文档地址:https://uniapp.dcloud.io/tutorial/app-sec-confusion

App的安装包都可以解压。前端资源,一般都是明文存放在安装包中,为防止解压后泄露敏感信息,需要进行安全处理。

由此DCloud提供了App端的js/nvue文件的原生混淆。5+ App/Wap2App支持对指定的js进行原生混淆。uni-app支持对指定的nvue文件原生混淆。

原生混淆后的安装包,解压后看到的都是乱码。

但需要注意:

  1. 没有绝对的安全,非常重要的信息,应该保存在服务器而不是前端
  2. 运行期对资源代码解密是影响执行性能的。不建议全包混淆,仅挑选需要保护的个别文件处理即可
  3. uni-app项目制作wgt包不支持原生混淆加密(即使配置也不会生效),HBuilderX3.1.0+版本后支持
  4. 为了保证加密数据的安全性,加密算法和key不对外公开,因此离线打包无法支持原生混淆加密,标准基座或自定义基座真机运行也不支持原生混淆加密(只有正式云打包才支持)

具体使用方式如下:

第一步、在manifest.json文件中配置要混淆的文件列表

打开manifest.json文件,切换到“源码视图”,按不同项目类型进行配置。

uni-app项目

uni-app的js运行在独立的jscore中,而不是webview中,所以不受iOS平台WKWebview不支持原生混淆的限制。
uni-app的vue页面中的js,是整体编译到一个大js文件中的,它经过编译,已经不再是vue源码了,但还不是乱码。对这个统一的大文件进行混淆会有影响性能。
所以uni-app只支持独立混淆nvue/js文件。

  • vue页面
    HBuilderX2.6.3+版本v3编译器支持对独立的js文件进行原生混淆,开发者可以将要保护的js代码写到独立的js文件中,在vue页面中使用import引用;如果此js同时被nvue页面import引用,则nvue页面也需要配置原生混淆才有效。另外main.js也可以原生混淆。
    老版本不支持vue页面的原生混淆,开发者只能将要保护的js代码写到nvue文件中进行保护。
  • nvue页面
    HBuilderX2.3.4+版本支持nvue文件的原生混淆。
    如果nvue页面引入了外部的js文件,会被一起原生混淆。但如果这个js还被其他不加密的文件引用,则该js仍然会暴露在安装包中。
  • vue页面和nvue页面同时使用加密js里的数据或方法(HBuilderX2.6.3+版本v3编译器)
    配置该js加密,并在App.vue中引用该js,把该js中的数据或方法赋值给全局对象,如globalData,vue和nvue中通过访问getApp访问共享数据或方法即可,无需配置nvue页面加密。

如果要发布多端的话,要保护的js最好写在app-plus的条件编译中,否则发布到其他端,还是无法原生混淆。

HBuilderX2.3.4版本开始,uni-app项目支持对nvue文件进行原生混淆

在"app-plus" -> "confusion" -> "resources"节点下添加要混淆的nvue文件列表:

    "app-plus": {   
        "confusion": {    
            "description": "NVUE原生混淆",    
            "resources": {    
                "pages/barcode/barcode.nvue": {     
                },     
                "pages/map/map.nvue": {     
                }     
            }     
        },    
        // ...    
    }

resource下的键名为nvue文件路径(相对于应用根目录),值为空JSON对象(大括号)。

<a id="vuejs"></a>
HBuilderX2.6.3+版本开始,uni-app项目使用v3编译器支持对vue页面中引用的js文件进行原生混淆

在manifest.json文件中添加要混淆的js文件列表:

    "app-plus": {   
        "confusion": {    
            "description": "原生混淆",    
            "resources": {    
                "common/test.js" : {}  
            }     
        },    
        // ...    
    }

在vue文件中引用混淆的js文件:

import test from '../common/test.js';  
//test.join();  //调用引用js中的方法

注意:uni-app中vue页面的webview组件支持加载使用加密混淆hybrid、static目录中的js文件,nvue页面的webview组件不支持。

5+ App/Wap2App项目

应用运行期间在页面打开时需要消耗更多时间进行混淆文件还原,为减少对运行速度的影响,5+App/wap2app仅支持对js文件进行原生混淆。
在"plus" -> "confusion" -> "resources"节点下添加要混淆的js文件列表:

    "plus": {   
        "confusion": {    
            "description": "JS原生混淆",    
            "resources": {    
                "js/common.js": {     
                },     
                "js/immersed.js": {     
                }     
            }     
        },    
        // ...    
    }

resource下的键名为js文件路径(相对于应用根目录),值为空JSON对象(大括号)。

<a id="wkwebview"></a>
HBuilderX2.6.11+版本开始,在iOS11+设备上使用WKWebview也可以支持JS原生混淆
WKWebview使用了更加严格的安全机制,使用原生混淆的js文件在html页面中必须使用自定义协议头plus-confusion://来引用:

<script type="text/javascript" src="plus-confusion://../js/common.js"></script>  
<!-- plus-confusion://后面为js文件路径,相对于当前html页面的路径 -->

在manifest.json的"plus" -> "confusion" -> "resources"节点下添加要混淆的js文件列表。
在"confusion"节点下添加 "supportWKWebview": true 支持WKWebview。
由于自定义协议仅在iOS11及以上设备才支持,建议配置应用支持的最低版本deploymentTarget为11.0:

    "plus": {   
        "confusion": {    
            "description": "JS原生混淆",   
            "supportWKWebview": true,   
            "resources": {    
                "js/common.js": {     
                }  
            }     
        },  
        "distribute": {  
            "apple": {  
                "deploymentTarget": "11.0"     //设置应用仅支持iOS11及以上设备  
                //...  
            }  
        }  
        // ...    
    }

注意:iOS平台WKWebview需iOS11+系统才支持原生混淆。5+App/wap2app项目,如果要兼容iOS11以下设备只能强制使用UIWebview内核,但苹果将要废弃UIWebview(详情)。如对原生混淆很重视,从长远考虑,建议改造升级uni-app

第二步、提交云端打包

配置好原生混淆的文件列表后,需要提交云端打包,注意在App云端打包对话框中需要勾选“对配置的js文件进行原生混淆”

再次强调:为了保证加密数据的安全性,加密算法和key不对外公开,因此离线打包无法支持原生混淆。
熟悉原生的开发者可将敏感信息存放于原生代码中,再与js进行交互。

对安全性要求较高的开发者,除了对前端js进行加密外,还应该对整个apk再进行一次加固。市面上很多加固服务可以选择,比如360加固、爱加密等。

收起阅读 »

mui上传图片案例

h5+

本案例是上传图片案例,可以进行图片选择和拍照操作。
使用时主要修改有以下几个:
1、var head_pho = document.getElementById('head_pho');这里修改成获取事件id
2、task.addData('token', token); 在此可以设置参数
3、task.addFile(filepath, { key: "personImage" }); filepath为图片路径,key是服务端接收图片的参数
4、plus.uploader.createUpload 在这里设置成服务端上传图片接口
以下为代码

var head_pho = document.getElementById('head_pho');  
mui.plusReady(function() {  
    // 文件路径  
    var filepath;  
    head_pho.addEventListener('tap', function() {  
        if (mui.os.plus) {  
            var a = [{  
                title: '拍照'  
            }, {  
                title: '从手机相册选择'  
            }];  
            plus.nativeUI.actionSheet({  
                title: '修改头像',  
                cancel: '取消',  
                buttons: a  
            }, function(b) {  
                switch (b.index) {  
                    case 0:  
                        break;  
                    case 1:  
                        //拍照  
                        getImages();  
                        break;  
                    case 2:  
                        //打开相册  
                        galleryImages();  
                        break;  
                    default:  
                        break;  
                }  
            }, false);  
        }  
    });  

    //拍照  
    function getImages() {  
        var mobileCamera = plus.camera.getCamera();  
        mobileCamera.captureImage(function(e) {  
            plus.io.resolveLocalFileSystemURL(e, function(entry) {  
                var path = entry.toLocalURL() + '?version=' + new Date().getTime();  
                var dstname = "_downloads/" + getUid() + ".jpg"; //设置压缩后图片的路径    
                compressImage(path, dstname, 0);  
            }, function(err) {  
                console.log("读取拍照文件错误");  
            });  
        }, function(e) {  
            console.log("er", err);  
        }, function() {  
            filename: '_doc/head.png';  
        });  
    }  

    //从本地相册选择  
    function galleryImages() {  
        plus.gallery.pick(function(a) {  
            plus.io.resolveLocalFileSystemURL(a, function(entry) {  
                plus.io.resolveLocalFileSystemURL('_doc/', function(root) {  
                    root.getFile('head.png', {}, function(file) {  
                        //文件已经存在  
                        file.remove(function() {  
                            console.log("文件移除成功");  
                            entry.copyTo(root, 'head.png', function(e) {  
                                var path = e.fullPath + '?version=' + new Date().getTime();  

                                var dstname = "_downloads/" + getUid() + ".jpg"; //设置压缩后图片的路径    
                                compressImage(path, dstname, 270);  
                            }, function(err) {  
                                console.log("copy image fail: ", err);  
                            });  
                        }, function(err) {  
                            console.log("删除图片失败:(" + JSON.stringify(err) + ")");  
                        });  
                    }, function(err) {  
                        //打开文件失败  
                        entry.copyTo(root, 'head.png', function(e) {  
                            var path = e.fullPath + '?version=' + new Date().getTime();  
                            uploadHeadImg(path);  
                        }, function(err) {  
                            console.log("上传图片失败:(" + JSON.stringify(err) + ")");  
                        });  
                    });  
                }, function(e) {  
                    console.log("读取文件夹失败:(" + JSON.stringify(err) + ")");  
                });  
            });  
        }, function(err) {  
            console.log("读取拍照文件失败: ", err);  
        }, {  
            filter: 'image'  
        });  
    }  

    // 产生一个随机数    
    function getUid() {  
        return Math.floor(Math.random() * 100000000 + 10000000).toString();  
    }  

    // 上传操作  
    function upload() {  
        var task = plus.uploader.createUpload(getUrl() + 'com_uploadPersonImage.action', {  
                method: "POST"  
            },  
            function(t, status) { //上传完成    
                if (status == 200) {  
                    console.log(t.responseText);  
                    // imgdiv.innerHTML = '<img id="userImg" src="' + t.responseText + '"/>';  
                } else {  
                    console.log("上传失败:" + status);  
                }  
            });  
        task.addData('token', token);  
        task.addFile(filepath, {  
            key: "personImage"  
        });  
        task.start();  
    }  

    // 进行图片压缩  
    function compressImage(src, dstname, rotate) {  
        plus.zip.compressImage({  
                src: src,  
                dst: dstname,  
                overwrite: true,  
                quality: 20,  
                rotate: rotate  
            },  
            function(event) {  
                console.log("Compress success:" + event.target);  
                filepath = event.target;  
                upload();  
            },  
            function(error) {  
                console.log(error);  
            });  
    }  
})
继续阅读 »

本案例是上传图片案例,可以进行图片选择和拍照操作。
使用时主要修改有以下几个:
1、var head_pho = document.getElementById('head_pho');这里修改成获取事件id
2、task.addData('token', token); 在此可以设置参数
3、task.addFile(filepath, { key: "personImage" }); filepath为图片路径,key是服务端接收图片的参数
4、plus.uploader.createUpload 在这里设置成服务端上传图片接口
以下为代码

var head_pho = document.getElementById('head_pho');  
mui.plusReady(function() {  
    // 文件路径  
    var filepath;  
    head_pho.addEventListener('tap', function() {  
        if (mui.os.plus) {  
            var a = [{  
                title: '拍照'  
            }, {  
                title: '从手机相册选择'  
            }];  
            plus.nativeUI.actionSheet({  
                title: '修改头像',  
                cancel: '取消',  
                buttons: a  
            }, function(b) {  
                switch (b.index) {  
                    case 0:  
                        break;  
                    case 1:  
                        //拍照  
                        getImages();  
                        break;  
                    case 2:  
                        //打开相册  
                        galleryImages();  
                        break;  
                    default:  
                        break;  
                }  
            }, false);  
        }  
    });  

    //拍照  
    function getImages() {  
        var mobileCamera = plus.camera.getCamera();  
        mobileCamera.captureImage(function(e) {  
            plus.io.resolveLocalFileSystemURL(e, function(entry) {  
                var path = entry.toLocalURL() + '?version=' + new Date().getTime();  
                var dstname = "_downloads/" + getUid() + ".jpg"; //设置压缩后图片的路径    
                compressImage(path, dstname, 0);  
            }, function(err) {  
                console.log("读取拍照文件错误");  
            });  
        }, function(e) {  
            console.log("er", err);  
        }, function() {  
            filename: '_doc/head.png';  
        });  
    }  

    //从本地相册选择  
    function galleryImages() {  
        plus.gallery.pick(function(a) {  
            plus.io.resolveLocalFileSystemURL(a, function(entry) {  
                plus.io.resolveLocalFileSystemURL('_doc/', function(root) {  
                    root.getFile('head.png', {}, function(file) {  
                        //文件已经存在  
                        file.remove(function() {  
                            console.log("文件移除成功");  
                            entry.copyTo(root, 'head.png', function(e) {  
                                var path = e.fullPath + '?version=' + new Date().getTime();  

                                var dstname = "_downloads/" + getUid() + ".jpg"; //设置压缩后图片的路径    
                                compressImage(path, dstname, 270);  
                            }, function(err) {  
                                console.log("copy image fail: ", err);  
                            });  
                        }, function(err) {  
                            console.log("删除图片失败:(" + JSON.stringify(err) + ")");  
                        });  
                    }, function(err) {  
                        //打开文件失败  
                        entry.copyTo(root, 'head.png', function(e) {  
                            var path = e.fullPath + '?version=' + new Date().getTime();  
                            uploadHeadImg(path);  
                        }, function(err) {  
                            console.log("上传图片失败:(" + JSON.stringify(err) + ")");  
                        });  
                    });  
                }, function(e) {  
                    console.log("读取文件夹失败:(" + JSON.stringify(err) + ")");  
                });  
            });  
        }, function(err) {  
            console.log("读取拍照文件失败: ", err);  
        }, {  
            filter: 'image'  
        });  
    }  

    // 产生一个随机数    
    function getUid() {  
        return Math.floor(Math.random() * 100000000 + 10000000).toString();  
    }  

    // 上传操作  
    function upload() {  
        var task = plus.uploader.createUpload(getUrl() + 'com_uploadPersonImage.action', {  
                method: "POST"  
            },  
            function(t, status) { //上传完成    
                if (status == 200) {  
                    console.log(t.responseText);  
                    // imgdiv.innerHTML = '<img id="userImg" src="' + t.responseText + '"/>';  
                } else {  
                    console.log("上传失败:" + status);  
                }  
            });  
        task.addData('token', token);  
        task.addFile(filepath, {  
            key: "personImage"  
        });  
        task.start();  
    }  

    // 进行图片压缩  
    function compressImage(src, dstname, rotate) {  
        plus.zip.compressImage({  
                src: src,  
                dst: dstname,  
                overwrite: true,  
                quality: 20,  
                rotate: rotate  
            },  
            function(event) {  
                console.log("Compress success:" + event.target);  
                filepath = event.target;  
                upload();  
            },  
            function(error) {  
                console.log(error);  
            });  
    }  
})
收起阅读 »

H5 端长按无法被页面滚动或滑动阻止

详细问题描述

目前我们要实现的需求为:
1、长按触发 showActionSheet
2、长按的元素在长按期间有页面滚动或者页面滑动时不触发长按
在小程序端没问题,在 H5 端滑动或者滚动的时候仍然会触发长按

[内容]

重现步骤

[步骤] H5 端列表元素长按时滑动

[结果] 滑动没有阻止长按事件的触发

[期望] 在长按触发前如果有页面滑动或者滚动时阻止长按触发

[如果语言难以表述清晰,拍一个视频或截图,有图有真相]

IDE运行环境说明

[HBuilder 或 HBuilderX。如果你用其他工具开发uni-app,也需要在此说明]

[IDE版本号] 2.3.3.20190923

[windows版本号]

[mac版本号]

uni-app运行环境说明

[运行端是h5或app或某个小程序?] h5
[项目是cli创建的还是HBuilderX创建的?如果是cli创建的,请更新到最新版cli再试] HBuilderX创建

[编译模式是老模板模式还是新的自定义组件模式?] 自定义组件模式

继续阅读 »

详细问题描述

目前我们要实现的需求为:
1、长按触发 showActionSheet
2、长按的元素在长按期间有页面滚动或者页面滑动时不触发长按
在小程序端没问题,在 H5 端滑动或者滚动的时候仍然会触发长按

[内容]

重现步骤

[步骤] H5 端列表元素长按时滑动

[结果] 滑动没有阻止长按事件的触发

[期望] 在长按触发前如果有页面滑动或者滚动时阻止长按触发

[如果语言难以表述清晰,拍一个视频或截图,有图有真相]

IDE运行环境说明

[HBuilder 或 HBuilderX。如果你用其他工具开发uni-app,也需要在此说明]

[IDE版本号] 2.3.3.20190923

[windows版本号]

[mac版本号]

uni-app运行环境说明

[运行端是h5或app或某个小程序?] h5
[项目是cli创建的还是HBuilderX创建的?如果是cli创建的,请更新到最新版cli再试] HBuilderX创建

[编译模式是老模板模式还是新的自定义组件模式?] 自定义组件模式

收起阅读 »

1.8升级2.3 安卓无法运行。 能解决红包奖励

1:44:58.250 44:57.623 20949 21138 E console : [ERROR] reportJSException >>>> exception function:callReportCrash, exception:weex core process crash and restart exception
11:44:58.440 Error: [JS Framework] Using invalid instance id "1" when calling destroyInstance.
11:44:59.879 App Launch at App.vue:48
11:45:00.745 45:00.127 20949 21154 E console : [ERROR] reportJSException >>>> exception function:callReportCrash, exception:weex core process crash and restart exception
11:45:00.918 Error: [JS Framework] Using invalid instance id "2" when calling destroyInstance.
11:45:02.355 App Launch at App.vue:48
11:45:03.150 45:02.533 20949 21170 E console : [ERROR] reportJSException >>>> exception function:callReportCrash, exception:weex core process crash and restart exception
11:45:03.441 Error: [JS Framework] Using invalid instance id "3" when calling destroyInstance.

继续阅读 »

1:44:58.250 44:57.623 20949 21138 E console : [ERROR] reportJSException >>>> exception function:callReportCrash, exception:weex core process crash and restart exception
11:44:58.440 Error: [JS Framework] Using invalid instance id "1" when calling destroyInstance.
11:44:59.879 App Launch at App.vue:48
11:45:00.745 45:00.127 20949 21154 E console : [ERROR] reportJSException >>>> exception function:callReportCrash, exception:weex core process crash and restart exception
11:45:00.918 Error: [JS Framework] Using invalid instance id "2" when calling destroyInstance.
11:45:02.355 App Launch at App.vue:48
11:45:03.150 45:02.533 20949 21170 E console : [ERROR] reportJSException >>>> exception function:callReportCrash, exception:weex core process crash and restart exception
11:45:03.441 Error: [JS Framework] Using invalid instance id "3" when calling destroyInstance.

收起阅读 »

Hbuilder真机运行调试之模拟器无法获得token

h5+

首先,我成功的完成HBuilder真机运行连接逍遥模拟器或mumu模拟器。

而,我的项目中大量用到了plus.storage.getItem('_token');来获取token;

但是在模拟器上成功运行项目后,测试发现所有的token获取均为null。

所以,关于在模拟器上开发应用,目前道路难行。

继续阅读 »

首先,我成功的完成HBuilder真机运行连接逍遥模拟器或mumu模拟器。

而,我的项目中大量用到了plus.storage.getItem('_token');来获取token;

但是在模拟器上成功运行项目后,测试发现所有的token获取均为null。

所以,关于在模拟器上开发应用,目前道路难行。

收起阅读 »

ios ajax的http请求无法响应

ATS

最新版本的hbuildx推送ios真机调试无法HTTP通信。没有响应。应该是又被启用了ats验证了吧。

最新版本的hbuildx推送ios真机调试无法HTTP通信。没有响应。应该是又被启用了ats验证了吧。