为了深入洞察用户行为,企业通常会从小程序中收集小程序日志,分析用户的设备信息、浏览历史等关键数据,进而改善产品功能并优化用户体验。通过启用阿里云日志服务(SLS)的Web Tracking功能,企业能自动将终端用户的小程序日志收集到SLS,既提高了数据处理效率,又减轻了业务服务器的负担。尽管便捷,但由于允许Logstore支持匿名写入,如小程序中的配置信息泄露可能引致数据污染。为了降低该风险,推荐使用阿里云安全令牌服务(STS),为小程序端提供时间和权限受限的访问令牌,从而增强数据上传过程的安全性。本文将以用户浏览行为日志收集为例,介绍如何利用SLS和STS为小程序构建安全、高效的小程序日志直传服务。
背景信息
企业A开发的这款小程序为用户提供了一个功能丰富、界面友好的在线平台,旨在吸引用户前来浏览、购买商品或服务。为了更好地理解用户的需求、优化用户体验,并进一步提升小程序的转化率,企业A认识到了对用户行为进行深入分析的重要性。这种分析需要基于用户在小程序上的实际操作行为,比如点击、滑动、停留、搜索习惯以及购买行为等。因此,企业A的技术团队决定采集小程序端日志,并将这些日志上传到SLS进行更高效的数据分析和处理。
安全风险
为了减轻业务服务器的负担并提高处理效率,该小程序被设计为允许用户的日志数据直接上传到SLS,避免了日志采集与上传过程中经过业务服务器的中转。
企业A计划采取以下方案搭建小程序日志直传服务:
然而,在以上方案中,允许小程序直接上传日志到SLS的同时,由于SLS的Logstore开启了支持匿名写入的Web Tracking功能,企业将面临以下风险:
数据污染风险:恶意用户可能会上传虚假或蓄意破坏的数据,影响数据质量和分析结果的准确性。
服务滥用风险:未受限的写入访问可能会被利用进行拒绝服务(DoS)攻击,通过大量写入请求耗尽资源。
解决方案
为了应对上述风险,企业在原有方案的基础上增加临时授权。通过这种方式,企业A能够在确保数据直传效率的同时,实现以下效果:
增强的身份验证和授权:通过STS生成的具有时间限制的令牌,即便在短时间内泄露,也极大降低了安全风险。因为这些凭证很快就会失效,降低了被不当利用的可能性。
精细化权限控制:STS允许根据最小权限原则配置权限,仅授权小程序必需的访问权限。这种精细化的权限控制方法限制了潜在泄露的影响范围,防止了过度权限的风险。
企业A最终采取以下方案搭建小程序日志直传服务:
方案部署
下面将以一个简单的用户浏览行为日志收集场景为例,引导您一步步使用SLS和STS为小程序部署小程序日志直传服务。本方案部署的示例工程:sls-mini-tracking-sts.zip。
准备工作
在开始部署前,您需要先创建云资源。
创建一个日志服务Project和Logstore,用于存储和分析从浏览器采集的日志。具体操作,请参见创建项目Project和创建Logstore。
参数
示例值
所属地域
华东2(上海)
Project名称
sls-webtracking-mini
Logstore名称
web-tracking-logstore
创建一台ECS实例作为业务服务器,用于生成临时身份凭证。具体步骤,请参见创建ECS实例。
说明在实际部署时,您可以将调用STS服务的接口集成到自己的业务服务器的接口中,而无需创建该ECS实例。
参数
示例值
付费类型
按量付费
地域
华东2(上海)
公网 IP
分配公网 IPv4 地址
安全组
开放HTTP (TCP:80)端口
步骤一:创建RAM用户
步骤二:授权RAM用户授予调用AssumeRole接口
为RAM用户添加AliyunSTSAssumeRoleAccess权限,使其可以通过扮演RAM角色来获取临时身份凭证。具体操作,请参见为RAM用户授权。
步骤三:创建RAM角色
创建RAM用户要扮演的RAM角色,例如命名为:sls-web-tracking
。具体操作,请参见创建可信实体为阿里云账号的RAM角色。
步骤四:创建自定义权限策略
创建一个自定义权限策略,例如命名为:post-logs-policy
。其中在脚本编辑页签,请使用以下脚本替换配置框中的原有内容。具体操作,请参见通过脚本编辑模式创建自定义权限策略。
将以下脚本中的<Project名称>
和<Logstore名称>
替换为准备工作中创建的Project名称和Logstore名称。
该策略只能向指定的SLS Logstore上传日志。您可根据实际需求配置更细粒度的授权策略,防止出现权限过大的风险。更多信息,请参见RAM自定义授权示例。
{
"Version":"1",
"Statement":[
{
"Effect":"Allow",
"Action":[
"log:PostLogStoreLogs"
],
"Resource":[
"acs:log:*:*:project/<Project名称>/logstore/<Logstore名称>"
]
}
]
}
步骤五:为RAM角色授予权限
为RAM角色sls-web-tracking
授予创建的自定义权限post-logs-policy
,以便被RAM用户扮演时能获取所需的权限。具体操作,请参见为RAM角色授权。
步骤六:在业务服务器获取临时身份凭证
在小程序中,通过在业务服务器集成STS SDK,实现一个获取临时STS身份凭证的接口。当这个接口(/get_sts_token
)通过HTTP GET方法被访问时,它会生成一个临时身份凭证,并将其返回给请求者。
在ECS实例上,使用Flask框架快速搭建小程序,实现一个获取临时STS身份凭证的接口的操作示例如下:
登录ECS实例。并在ECS实例中安装Python3。
配置RAM用户的
ALIBABA_CLOUD_ACCESS_KEY_ID
和ALIBABA_CLOUD_ACCESS_KEY_SECRET
,操作步骤请参见在Linux、macOS和Windows系统配置环境变量。创建项目目录,并切换到项目目录。
mkdir my_web_sample cd my_web_sample
安装依赖。
pip3 install Flask pip3 install attr pip3 install yarl pip3 install async_timeout pip3 install idna_ssl pip3 install attrs pip3 install aiosignal pip3 install charset_normalizer pip3 install alibabacloud_tea_openapi pip3 install alibabacloud_sts20150401 pip3 install alibabacloud_credentials
编写后端代码。
创建一个
main.py
文件。在这个文件中,添加以下Python代码。
重要将代码中的
<YOUR_ROLE_ARN>
替换为步骤三中创建的RAM角色sls-web-tracking
的ARN。将代码中的
<YOUR_ROLE_SESSION_NAME>
设置为自定义的会话名称,例如role_session_test
。import json from flask import Flask, render_template from alibabacloud_tea_openapi.models import Config from alibabacloud_sts20150401.client import Client as Sts20150401Client from alibabacloud_sts20150401 import models as sts_20150401_models from alibabacloud_credentials.client import Client as CredentialClient app = Flask(__name__) # 将<YOUR_ROLE_ARN>替换为RAM角色的ARN。 role_arn_for_oss_upload = '<YOUR_ROLE_ARN>' # 设置为STS服务的地域,例如cn-shanghai。 region_id = 'cn-shanghai' @app.route('/get_sts_token', methods=['GET']) def get_sts_token(): # 初始化 CredentialClient 时不指定参数,代表使用默认凭据链。 # 在本地运行程序时,可以通过环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID、ALIBABA_CLOUD_ACCESS_KEY_SECRET 指定 AK; # 在 ECS\ECI\容器服务上运行时,可以通过环境变量 ALIBABA_CLOUD_ECS_METADATA 来指定绑定的实例节点角色,SDK 会自动换取 STS 临时凭证。 config = Config(region_id=region_id, credential=CredentialClient()) sts_client = Sts20150401Client(config=config) assume_role_request = sts_20150401_models.AssumeRoleRequest( role_arn=role_arn_for_oss_upload, # 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称。 role_session_name='<YOUR_ROLE_SESSION_NAME>' ) response = sts_client.assume_role(assume_role_request) token = json.dumps(response.body.credentials.to_map()) return token app.run(host="0.0.0.0", port=80)
启动应用程序。
python3 main.py
在浏览器中访问
http://<ECS实例公网IP地址>/get_sts_token
。成功返回示例如下:
步骤七:在小程序使用临时身份凭证上传日志到日志服务Logstore
小程序的开发流程和代码构成,请参见支付宝文档中心。本文档的小程序客户端代码,基于支付宝小程序的Todo示例和日志服务的使用WebTracking JavaScript SDK的STS插件上传日志,示例代码的下载地址sls-mini-tracking-sts.zip。
在业务服务器配置了获取STS临时身份凭证的接口后,在小程序的app.js
文件中集成SLS的埋点SDK(@aliyun-sls/web-track-mini)和STS插件(@aliyun-sls/web-sts-plugin),实现实时监控用户行为并上传相关日志。
当创建埋点跟踪器(tracker)实例时,它将自动请求之前部署的/get_sts_token
接口,以便获取必要的临时STS凭证。这种机制保证了用户在小程序交互过程中,如登录、浏览商品或提交订单等活动的数据会被安全地传输至SLS。
下载小程序开发者工具。
创建和体验Todo App 小程序。
安装npm。
yum install npm
安装项目依赖。
npm install --save @aliyun-sls/web-track-mini npm install --save @aliyun-sls/web-sts-plugin
修改Todo示例代码,将用户数据通过Webtracking上传到Logstore。修改后的示例代码sls-mini-tracking-sts.zip,修改的文件如下。
文件
修改代码的说明
app.js
在
onLaunch
方法中调用了initSlsTracker
方法,当小程序启动时会初始化SlsTracker。在
initSlsTracker
方法中定义日志服务的Project、Logstore等参数,参数说明参见webtracking参数名称。stsOpt
配置和refreshSTSToken
函数用于动态获取和更新阿里云的安全令牌服务(Security Token Service,简称 STS)的临时访问凭证。STS相关参数说明参见STS参数名称。
mini.project.json
STS插件(@aliyun-sls/web-sts-plugin)包含ES6特性,在
mini.project.json
文件中设置编译选项compileOptions.transpile
为true
,让小程序的开发工具将 ES6 代码转换为ES5代码,确保兼容性。todo.xml
<view class="todo-footer"> <add-button text="Add Todo" onClickMe="handleAddTodo"></add-button> </view>
定义了用户界面底部的一个按钮,界面词为
Add Todo
,add-button
是自定义按钮。onClickMe="handleAddTodo"
代表按钮被单击时调用todo.js
中的方法handleAddTodo
。todo.js
handleAddTodo()
是事件处理函数。当用户在页面上添加新的待办事项时,这个函数被触发。它依次执行两个操作:调用
this.addTodo()
方法添加一个新的待办事项。调用
this.onAddTodoButtonClick()
发送关于用户点击添加按钮的日志消息。
addTodo()
调用小程序的my.navigateTo
函数,实现从当前页面跳转到应用内/pages/add-todo/add-todo
路径对应的页面。onAddTodoButtonClick()
调用app.js
中定义的SlsTracker
的send
方法发送一条日志事件,这条日志的内容为eventType: 'addtodo_click'
。
add-todo.js
在
add
方法中创建一个新对象
newTodo
,代表一个待办事项,newTodo
包括两个属性:text
:待办事项的内容,这里从this.data.inputValue
获取,意味着它可能是来自用户输入。completed
:表示这个待办事项是否已完成,初始化为false
,意味着新建的待办事项默认状态是未完成。
调用
app.js
中定义的SlsTracker
的send
方法发送一条日志事件,这条日志的内容为eventType: 'add_todo' todoText: newTodo.text
add-button.js
自定义按钮组件。
onClickMe
方法首先检查this.props.onClickMe
是否为一个函数类型。只有当确定this.props.onClickMe
是函数时,才执行调用。配置小程序
app.js
文件的参数。// app.js import SlsTracker from '@aliyun-sls/web-track-mini'; import createStsPlugin from '@aliyun-sls/web-sts-plugin'; App({ todos: [ { text: 'Learning Javascript', completed: true }, { text: 'Learning ES2016', completed: true }, { text: 'Learning 支付宝小程序', completed: false }, ], userInfo: null, tracker: null, onLaunch: function() { // 在应用启动时进行初始化 this.initSlsTracker(); }, initSlsTracker: function() { const opts = { host: 'cn-shanghai.log.aliyuncs.com', // 替换为你的服务入口 project: '${project}', // 替换为你的 Project 名称 logstore: '${Logstore}', // 替换为你的 Logstore 名称 time: 10, count: 10, topic: 'topic', source: 'source', tags: { tags: 'tags', }, }; const stsOpt = { accessKeyId: '', accessKeySecret: '', securityToken: '', refreshSTSToken: () => new Promise((resolve, reject) => { my.request({ url: 'http://${ECS实例的公网IP}}/get_sts_token', // 替换为后端 STS Token 提供服务的真实地址 method: 'GET', success: (res) => { if (res.statusCode === 200) { let credential; // 检查返回的数据类型确保是字符串,若不是字符串则可能已是对象 if (typeof res.data === "string") { //解析为JSON对象 credential = JSON.parse(res.data); } else { // 直接使用对象 credential = res.data; } stsOpt.accessKeyId = credential.AccessKeyId; stsOpt.accessKeySecret = credential.AccessKeySecret; stsOpt.securityToken = credential.SecurityToken; resolve(credential); } else { reject(new Error('Failed to refresh STS token with status code: ' + res.statusCode)); } }, fail: (err) => { reject(new Error('Failed to refresh STS token', err)); }, }); }), }; const tracker = new SlsTracker(opts); const stsPlugin = createStsPlugin(stsOpt); tracker.useStsPlugin(stsPlugin); // 以单条日志上传为例,只要启动小程序,就会发送一条日志。 // tracker.send({ // eventType:'view_product', // productName: 'Tablet', // price: 500 // }); this.tracker = tracker; }, });
关于
opts
和stsOpt
的参数说明,请参见使用WebTracking JavaScript SDK的STS插件上传日志。将代码中的
${project}
和${logstore}
替换为准备工作中创建的Project名称和Logstore名称。将代码中的
<ECS实例公网IP地址>
替换为准备工作中创建的ECS实例的公网IP地址。如何查看公网IP地址,请参见查看IP地址。
运行客户端示例代码。在开发者工具,单击页面右上角的编译。在右侧小程序预览页面,单击小程序的Todo页面的Add Todo按钮或输入Todo项时,小程序会直接向日志服务的Logstore上传日志,无需经过服务器。
完成及清理
方案验证
完成以上操作后,您可以通过预览方式查看数据是否已上传到SLS。
在消费预览面板,查看成功上传的日志。
清理资源
在本方案中,您创建了1台ECS实例、1个SLS Project和Logstore、1个RAM用户和1个RAM角色。测试完方案后,您可以参考以下规则处理对应产品的资源,避免继续产生费用或产生安全风险。
释放ECS实例。具体操作,请参见释放实例。
先删除Logstore,再删除Project。具体操作,请参见管理Logstore和管理Project。
后续操作
本文在开发者工具中对示例程序进行本地调试,如果需要正式上线小程序,必须完成以下步骤:
创建开发者账号,创建小程序,将小程序提交审核等,具体参见开发者账号注册。
在开发者工具中忽略域名合法性,检查便于调试,步骤请参见接入准备。
在支付宝控制台中配置以下的域名白名单。
添加日志服务的服务器域名(request合法域名)。格式为
https://${project}.${host}
,project
是日志服务的Project名称,host
是Project域名,本文示例为sls-webtracking-mini.cn-shanghai.log.aliyuncs.com
,操作步骤参见服务器域名白名单。添加小程序后端服务器的域名,本文示例只有小程序客户端代码,正式上线小程序还需要开发服务端代码。服务器域名白名单只支持HTTPS协议,配置HTTPS协议需要在服务器部署SSL证书,步骤请参见SSL证书安装说明。本文示例在开发者工具中忽略域名合法性,小程序正式上线前必须配置域名检查。
将终端用户的小程序日志收集到SLS后: