我们后端使用的是 Node.js,用的框架式是阿里的 Egg.js。这是 Egg 官网 对 Egg.js 做的介绍:Egg.js 为企业级框架和应用而生,我们希望由 Egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本。

Egg.js 使用起来及其简单方便,作为一个移动端开发者,初次使用还算顺利,感兴趣的童鞋可以查看其文档:Egg 快速入门

我们的需求是前端上传日志文件到 OSS,在上传文件过程中需要签名这一步骤。在前端进行签名也可以调通阿里云的接口,但是把 accessKeyId、accessKeySecret 放在前端存在很大的安全隐患,因此开发中我们通常在后端做签名。

前端上传数据到 OSS 架构:

  • 用户发送上传 Policy 请求到应用服务器
  • 应用服务器返回上传 Policy 和签名给用户
  • 用户直接上传数据到 OSS

这里是后端签名的核心代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
'use strict';
'use aliyun-oss-sign'

const Controller = require('egg').Controller;
const crypto = require("crypto")

const config = {
  bucket: '', //oss桶名
  region: '', //oss节点名
  accessKeyId: '', //申请的osskey
  accessKeySecret: '', // 申请的osssecret
  callbackIp: "", // 回调ip,一定要能被外网访问的地址。
  callbackPort: "8080", // 回调端口
  callbackPath: "user/ossCallback", // 回调接口路径
  expAfter: 60000, // 签名失效时间
  maxSize: 1048576000 // 最大文件大小
}

class UserController extends Controller {
  // 获取token
  async getToken() {
    const { ctx } = this;
    const {
      bucket,
      region,
      expAfter,
      maxSize,
      accessKeyId,
      accessKeySecret,
      callbackIp,
      callbackPort,
      callbackPath
    } = config
    let dict = this.ctx.request.body; // oss 文件夹 不存在会自动创建
    const dirPath = dict.dirPath;
    console.log(dirPath);
    const host = `http://${bucket}.${region}.aliyuncs.com` //你的oss完整地址
    const expireTime = new Date().getTime() + expAfter
    console.log(expireTime);
    const expiration = new Date(expireTime).toISOString()
    console.log(expiration);
    const policyString = JSON.stringify({
      expiration,
      conditions: [
        ['content-length-range', 0, maxSize],
        ['starts-with', '$key', dirPath]
      ]
    })
    const policy = Buffer(policyString).toString("base64")
    const Signature = crypto.createHmac('sha1', accessKeySecret).update(policy).digest("base64")
    const callbackBody = {
      "callbackUrl": `http://${callbackIp}:${callbackPort}/${callbackPath}`,
      "callbackHost": `${callbackIp}`,
      "callbackBody": "{\"filename\": ${object},\"size\": ${size}}",
      "callbackBodyType": "application/json"
    }
    const callback = Buffer(JSON.stringify(callbackBody)).toString('base64')

    ctx.body = {
      statusCode: "200",
      message: 'oss签名成功',
      result: {
        Signature,
        policy,
        host,
        'OSSAccessKeyId': accessKeyId,
        'key': expireTime,
        'success_action_status': 200,
        dirPath,
        callback
      }
    };
    ctx.status = 200;
  }
}

module.exports = UserController;

后端在 config 里填写 OSS 相关的各种信息,前端调取 getToken 接口,会得到 Signature 等一系列返回结果,大致如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "statusCode": "200",
  "message": "oss签名成功",
  "result": {
    "Signature": "********",
    "policy": "*************",
    "host": "http://ta-app-log.oss-cn-hangzhou.aliyuncs.com",
    "OSSAccessKeyId": "************",
    "key": 1580875747468,
    "success_action_status": 200,
    "dirPath": "test",
    "callback": "********************"
  }
}

这样前端就可以安全的上传数据到 OSS 了。

结束语: 本人本职工作是 iOS,前段时间因工作需要做了一段时间前端开发。如果哪里写的有问题,欢迎各位童鞋多多指教。

相关文章: