STS临时授权访问OSS

STS临时授权访问OSS

OSS 可以通过阿里云 STS (Security Token Service) 进行临时授权访问。阿里云 STS 是为云计算用户提供临时访问令牌的Web服务。通过 STS,您可以为第三方应用或子用户(即用户身份由您自己管理的用户)颁发一个自定义时效和权限的访问凭证。

使用场景

对于您本地身份系统所管理的用户,比如您的 App 的用户、您的企业本地账号、第三方 App ,将这部分用户称为联盟用户。这些联盟用户可能需要直接访问 OSS 资源。此外,还可以是您创建的能访问您的阿里云资源应用程序的用户。

对于这部分联盟用户,通过阿里云 STS 服务为阿里云账号(或 RAM 用户)提供短期访问权限管理。您不需要透露云账号(或 RAM 用户)的长期密钥(如登录密码、AccessKey),只需要生成一个短期访问凭证给联盟用户使用即可。这个凭证的访问权限及有效期限都可以由您自定义。您不需要关心权限撤销问题,访问凭证过期后会自动失效。

通过 STS 生成的凭证包括安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)。使用AccessKey 方法与您在使用阿里云账户或 RAM 用户 AccessKey 发送请求时的方法相同。需要注意的是在每个向 OSS 发送的请求中必须携带安全令牌。

实现原理

以一个移动 App 举例。假设您是一个移动 App 开发者,打算使用阿里云 OSS 服务来保存App 的终端用户数据,并且要保证每个 App 用户之间的数据隔离,防止一个 App 用户获取到其它 App 用户的数据。你可以使用 STS 授权用户直接访问 OSS。

使用 STS 授权用户直接访问 OSS 的流程如下:


  1. App 用户登录。App 用户和云账号无关,它是 App 的终端用户,AppServer 支持 App 用户登录。对于每个有效的 App 用户来说,需要 AppServer 能定义出每个 App 用户的最小访问权限。
  2. AppServer 请求 STS 服务获取一个安全令牌(SecurityToken)。在调用 STS 之前,AppServer 需要确定 App 用户的最小访问权限(用 Policy 语法描述)以及授权的过期时间。然后通过扮演角色(AssumeRole)来获取一个代表角色身份的安全令牌。
  3. STS 返回给 AppServer 一个有效的访问凭证,包括一个安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)以及过期时间。
  4. AppServer 将访问凭证返回给 ClientApp。ClientApp 可以缓存这个凭证。当凭证失效时,ClientApp 需要向 AppServer 申请新的有效访问凭证。比如,访问凭证有效期为1小时,那么 ClientApp 可以每 30 分钟向 AppServer 请求更新访问凭证。
  5. ClientApp 使用本地缓存的访问凭证去请求 Aliyun Service API。云服务会感知 STS 访问凭证,并会依赖 STS 服务来验证访问凭证,正确响应用户请求。

STS 安全令牌、角色管理和使用相关内容详情,请参考 RAM 角色管理。调用 STS 服务接口AssumeRole来获取有效访问凭证即可。

操作步骤

假设有一个名为 ram-test 的 Bucket 用于存储用户数据,现将利用用户子账号结合 STS 实现 OSS 权限控制访问。

您可以通过 OSS SDK 与 STS SDK 的结合使用,实现使用 STS 临时授权访问 OSS 实例。

  1. 创建用户子账号。
    1. 登录 RAM 访问控制管理控制台
    2.  RAM 访问控制页面,单击用户。
    3. 用户页面,单击新建用户。
    4. 新建用户页面,用户账号信息填写登录名称、显示名称,访问方式下勾选编程访问,并单击确定。

    5. 单击权限管理 > 添加权限。

    6. 添加权限页面,为已创建子账号添加 AliyunSTSAssumeRoleAccess 权限。

      说明 尽量不要赋予子账号其他任意权限,因为在扮演角色的时候会自动获得被扮演角色的所有(部分)权限。
  2. 创建权限策略。
    1. 登录 RAM 访问控制管理控制台
    2.  RAM 访问控制页面,单击权限策略管理。
    3. 单击新建权限策略。
    4. 新建自定义权限策略页面,填写策略名称、备注,配置模式选择可视化配置或脚本配置。
      以脚本配置为例,对 ram-test 添加 GetObjectPutObject等权限,在 策略内容中配置脚本如下:
      {
          "Statement": [
              {
                  "Action": [
                      "oss:GetObject",
                      "oss:PutObject",
                      "oss:DeleteObject",
                      "oss:ListParts",
                      "oss:AbortMultipartUpload",
                      "oss:ListObjects"
                  ],
                  "Effect": "Allow",
                  "Resource": [
                      "acs:oss:*:*:ram-test/*",
                      "acs:oss:*:*:ram-test"
                  ]
              }
          ],
          "Version": "1"
      }
       


  3. 创建角色。
    1. 登录 RAM 访问控制管理控制台
    2.  RAM 访问控制页面,单击RAM 角色管理。
    3. 在 RAM 角色管理页面,单击新建 RAM 角色。
    4. 新建 RAM 角色页面,填写 RAM 角色名称,本示例 RAM 角色名称为 RamOssTest,选择可信实体类型及受信云账号 ID 保留默认选项。

    5. 单击已创建 RAM 角色 RamOssTest 右侧对应的添加权限 。
    6. 添加权限页面,选择自定义权限策略,添加步骤 2 中创建的策略 Ramtest。
      添加策略后,页面如下图所示: 

      说明 ARN 代表需要扮演角色的 ID。
  4. 通过 STS API 获取 STS AK 与 SecurityToken。

    通过调用 STS SDK 请求 STS 服务获取一个安全令牌。STS SDK 的安装及使用详见STS Java SDK安装及使用

    以 STS Java SDK 为例:
                  <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-sts</artifactId>
                <version>3.0.0</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>3.5.0</version>
            </dependency>
      @ResponseBody
        @RequestMapping("/sts")
        public AssumeRoleResponse.Credentials sts(HttpServletRequest req) {
    
            AssumeRoleResponse.Credentials credentials = null;
            String endpoint = "sts.cn-beijing.aliyuncs.com";
            String accessKeyId = "LTAIz5f---Pg8";
            String accessKeySecret = "yO7yzqxCZjgWCY----8l0fFHaPd3Gwug";
            String roleArn = "acs:ram::184810807023---6:role/ggband";
            String roleSessionName = "AliyunDMSRol--ePolicy";
            String policy = "{
    " +
                    "    "Statement": [
    " +
                    "        {
    " +
                    "            "Action": [
    " +
                    "                "oss:GetObject",
    " +
                    "                "oss:PutObject",
    " +
                    "                "oss:DeleteObject",
    " +
                    "                "oss:ListParts",
    " +
                    "                "oss:AbortMultipartUpload",
    " +
                    "                "oss:ListObjects"
    " +
                    "            ],
    " +
                    "            "Effect": "Allow",
    " +
                    "            "Resource": [
    " +
                    "                "acs:oss:*:*:ram-test/*",
    " +
                    "                "acs:oss:*:*:ram-test"
    " +
                    "            ]
    " +
                    "        }
    " +
                    "    ],
    " +
                    "    "Version": "1"
    " +
                    "}";
            try {
                // 添加endpoint(直接使用STS endpoint,前两个参数留空,无需添加region ID)
                DefaultProfile.addEndpoint("", "", "Sts", endpoint);
                // 构造default profile(参数留空,无需添加region ID)
                IClientProfile profile = DefaultProfile.getProfile("", accessKeyId, accessKeySecret);
                // 用profile构造client
                DefaultAcsClient client = new DefaultAcsClient(profile);
                final AssumeRoleRequest request = new AssumeRoleRequest();
                request.setMethod(MethodType.POST);
                request.setRoleArn(roleArn);
                request.setRoleSessionName(roleSessionName);
                request.setPolicy(policy); // Optional
                request.setProtocol(ProtocolType.HTTPS); // 必须使用HTTPS协议访问STS服务);
                final AssumeRoleResponse response = client.getAcsResponse(request);
                credentials = response.getCredentials();
                System.out.println("Expiration: " + credentials.getExpiration());
                System.out.println("Access Key Id: " + credentials.getAccessKeyId());
                System.out.println("Access Key Secret: " + credentials.getAccessKeySecret());
                System.out.println("Security Token: " + credentials.getSecurityToken());
                System.out.println("RequestId: " + response.getRequestId());
            } catch (ClientException e) {
                System.out.println("Failed:");
                System.out.println("Error code: " + e.getErrCode());
                System.out.println("Error message: " + e.getErrMsg());
                System.out.println("RequestId: " + e.getRequestId());
            }
            return credentials;
        }

    5. 前端代码如下:(以上传oss文件为例)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Oss demo</title>
    
    
        <input type="file" name="picFieldName" id="picFieldId" onchange="uploadPic(this)"/>
    
        <!-- 兼容ie -->
        <script src="https://www.promisejs.org/polyfills/promise-6.1.0.js"></script>
        <!-- oss 上传文件 JavaScript 库 -->
        <script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-4.3.0.min.js"></script>
        <script src="../../js/jquery.min.js"></script>
        <script>
    
            function uploadPic(obj) {
    
                //请求自己后台 获取sts等相关信息后,调用oss-sdk方法直传oss
                $.ajax({
                    url: "/ueditor/sts", success: function (result) {
                        var client = new OSS.Wrapper({
                            region: 'oss-cn-beijing',
                            accessKeyId: result.accessKeyId,
                            accessKeySecret: result.accessKeySecret,
                            bucket: 'rem-test',
                            stsToken: result.securityToken
    
                        });
                        var file = obj.files[0];//获取文件流
                        var val = obj.value;
                        var suffix = val.substr(val.indexOf("."));
                        var storeAs = "tally_ueditor/" + timestamp() + suffix;
                        console.log(file.name + ' => ' + storeAs);
                        client.multipartUpload(storeAs, file).then(function (result) {
                            console.log("result:" + JSON.stringify(result));
                        }).catch(function (err) {
                            console.log("err:" + JSON.stringify(err));
    
                        });
                    }
                });
    
            }
    
            /**
             * 生成文件名
             * @returns
             */
            function timestamp() {
                var time = new Date();
                var y = time.getFullYear();
                var m = time.getMonth() + 1;
                var d = time.getDate();
                var h = time.getHours();
                var mm = time.getMinutes();
                var s = time.getSeconds();
                console.log(y);
                return "" + y + add0(m) + add0(d) + add0(h) + add0(mm) + add0(s);
            }
    
            function add0(m) {
                return m < 10 ? '0' + m : m;
            }
        </script>
         
    
    </head>
    <body>
    
    </body>
    </html>

    6、前端上传时出现403错误,请进行跨域设置

ggband
原文地址:https://www.cnblogs.com/ggband/p/10218851.html