采集-搭建移动端日志直传服务

在移动互联时代,直接通过手机应用上传数据变得越来越普遍。针对日志场景,您也可以将移动端应用的日志直接上传到日志服务。本文介绍如何搭建移动端应用日志的直传服务。

背景信息

  • 普通模式下,写日志到日志服务需要开启阿里云账号的访问密钥AK,用于鉴权及防篡改。如果通过此模式上传日志到日志服务,需要将您阿里云账号的AK保存在移动端应用中,会存在AK泄漏风险。如果发生AK泄漏,则会威胁该账号下云资源的安全。

  • 另一种方式是通过应用服务器中转,但是使用该模式,您的应用服务器需要承载所有的移动端数据,对应用服务器的压力较大。

  • 为了避免以上问题,阿里云提供更安全、便捷的移动端应用日志采集方案。RAM角色通过STS获取可以自定义时效和访问权限的临时身份凭证。使用该临时令牌,可搭建直传服务实现移动设备将应用日志直接上传到日志库。

方案概览

image
  1. 移动端应用向应用服务器发起请求,申请获取临时安全令牌;

  2. 应用服务器检测移动端请求的合法性(可以结合业务的实际情况如:登录态、对称加密等),若通过校验,应用服务器需要使用提前创建的RAM用户生成的AK作为访问凭据,向STS服务发起AssumeRole接口调用,申请RAM用户扮演目标RAM角色;

  3. STS服务检测AK合法性,并验证RAM用户是否具备扮演目标RAM角色的权限,若通过校验,STS服务返回临时安全令牌给应用服务器;

  4. 应用服务器获取临时安全令牌后,返回临时安全令牌给移动端应用;

  5. 移动端应用获取临时安全令牌后,使用临时安全令牌访问日志服务;

  6. 日志服务通过RAM/STS服务验证临时安全令牌合法性,并验证RAM角色是否具备访问目标日志库的权限,若通过校验,日志服务处理移动端应用的上传日志请求,并返回上传结果给移动端应用。

名词解释

节点

说明

Android/iOS应用

最终用户的移动端应用,日志的来源。

SLS

阿里云日志服务,负责存储应用上传的日志。

RAM/STS

阿里云访问控制,提供用户身份管理和资源访问控制服务,负责生成临时安全凭证(STS Token)。

应用服务器

提供从STS获取临时访问凭证的服务以及用户在应用中上传数据的元数据信息。

步骤一:在日志服务中创建Project和Logstore

  1. 登录日志服务控制台

  2. 创建项目Project,输入示例Project名称:slstestproject。

  3. 创建Logstore,输入示例Logstore名称:slstestlogstore。

步骤二:创建RAM用户并完成角色扮演授权

  1. RAM用户的创建详细步骤请参考:创建RAM用户。本示例使用的RAM用户为:ramslsuser。下面步骤中会使用系统会自动生成访问密钥AccessKey,请记录并妥善保存AccessKey。

  2. 授予RAM用户调用STS服务AssumeRole接口的权限:

    a. 在用户页面,单击目标RAM用户操作列的添加权限。

    b. 在新增授权页面,选中系统策略下的AliyunSTSAssumeRoleAccess,然后单击确认新增授权image

步骤三:创建RAM角色并完成资源授权

  1. 创建RAM角色的详细步骤请参考:创建可信实体为阿里云账号的RAM角色,示例值如下所示:

    参数

    说明

    角色名称

    输入角色名称,例如ramslsrole

    备注

    输入备注信息

    选择信任的云账号

    选择当前云账号(UID)

  2. 创建完成后,在角色页面,找到ramslsrole角色,单击角色名称,查看并记录角色ARN。image

  3. 通过脚本编辑模式创建自定义策略。更多信息请参考:创建自定义权限策略,示例值如下所示:

    参数

    说明

    名称

    输入自定义策略名称,例如putlogspolicy

    备注

    输入备注信息

    策略内容

    {
      "Version": "1",
      "Statement": [
        {
          "Action": [
            "log:PostLogStoreLogs",
            "log:PutLogs"
          ],
          "Resource": "acs:log:*:*:project/slstestproject/*",
          "Effect": "Allow"
        }
      ]
    }
  4. 为RAM角色授予自定义权限策略,在角色页面,找到RAM角色ramslsrole,单击RAM角色操作列的添加权限

    image

步骤四:搭建应用服务器

本教程提供了多个语言的版本示例程序供您下载,下载地址请参见PHP、Java、Ruby、Node.js

  1. 配置文件

    示例中,每个语言包内均有一个管理配置的config.json文件,可对配置进行修改。

    {
        "AccessKeyID" : "",
        "AccessKeySecret" : "",
        "RoleArn" : "",
        "TokenExpireTime" : "900",
        "PolicyFile": "policy/write_policy.txt"
    }
                                

    参数

    说明

    AccessKeyID

    RAM用户的访问密钥ID。

    AccessKeySecret

    RAM用户的访问密钥Secret。

    RoleArn

    RAM角色的RoleArn。

    TokenExpireTime

    移动端应用获取到的Token的失效时间。

    最少是900s,可以不修改默认值。

    PolicyFile

    Token所要拥有的权限列表的文件,可以不修改默认值。

    此处提供两种最常用Token权限文件,位于policy目录下面。 您也可以根据业务需求设计policy文件。

    • write_policy.txt:指定了该Token拥有该账号下Project的写入权限。使用write_policy.txt权限文件时,请根据实际替换该文件中的Project名称。

    • readonly_policy.txt:指定了该Token拥有该账号下Project的读取权限。

    Token的最终权限为RAM角色权限与权限文件的权限交集。如果未设置权限文件,则Token的最终权限为RAM角色权限。

  2. 运行示例代码

    代码示例的运行方法:对于Java版本(依赖于Java 1.7+),将包下载解压后,新建一个Java工程,将依赖和代码以及配置拷贝到Java工程中,运行main函数即可,程序默认会监听7080端口,等待HTTP请求,其他语言类似。

步骤五:移动端直传

  1. 获取安全令牌STS Token:

    以HTTP请求形式访问服务的7080端口,获取临时安全令牌。返回结果示例如下:

    {
      "StatusCode":"200",
      "AccessKeyId":"STS.3pdgagd****",
      "AccessKeySecret":"rpnwO9wr34tGdrddgsR2Y****",
      "SecurityToken":"CAES+wMIARKAAZhjH0EUOIhJMQBMjR****tZGVtbzI=",
      "Expiration":"2021-11-20T08:23:15Z"
    }                          
  2. 移动端直传:

    示例代码演示通过移动客户端使用临时安全令牌直接将日志写入到日志库中。

    const ALY = require("aliyun-sdk");
    
    const sls = new ALY.SLS({
      accessKeyId: "Your temporary accesKeyId",
      secretAccessKey: "Your temporary accessKeySecret",
      securityToken: "Your sts token",
      endpoint: "http://cn-hangzhou.log.aliyuncs.com",
    });
    
    // -------------------------------
    // put logs
    // -------------------------------
    const projectName = "your_project_name";
    const logStoreName = "your_logstore";
    
    const logGroup = {
      logs: [
        {
          time: Math.floor(new Date().getTime() / 1000),
          contents: [
            {
              key: "a",
              value: "1",
            },
            {
              key: "a",
              value: "2",
            },
            {
              key: "a",
              value: "3",
            },
          ],
        },
      ],
      topic: "vv",
      source: "127.0.0.1",
    };
    
    sls.putLogs(
      {
        //必选字段
        projectName: projectName,
        logStoreName: logStoreName,
        logGroup: logGroup,
      },
      function (err, data) {
        if (err) {
          console.log("error:", err);
          return;
        }
    
        console.log("success:", data);
      }
    );