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 的流程如下:
- App 用戶登錄。App 用戶和雲賬號無關,它是 App 的終端用戶,AppServer 支持 App 用戶登錄。對於每個有效的 App 用戶來說,需要 AppServer 能定義出每個 App 用戶的最小訪問權限。
- AppServer 請求 STS 服務獲取一個安全令牌(SecurityToken)。在調用 STS 之前,AppServer 需要確定 App 用戶的最小訪問權限(用 Policy 語法描述)以及授權的過期時間。然后通過扮演角色(AssumeRole)來獲取一個代表角色身份的安全令牌。
- STS 返回給 AppServer 一個有效的訪問憑證,包括一個安全令牌(SecurityToken)、臨時訪問密鑰(AccessKeyId, AccessKeySecret)以及過期時間。
- AppServer 將訪問憑證返回給 ClientApp。ClientApp 可以緩存這個憑證。當憑證失效時,ClientApp 需要向 AppServer 申請新的有效訪問憑證。比如,訪問憑證有效期為1小時,那么 ClientApp 可以每 30 分鍾向 AppServer 請求更新訪問憑證。
- ClientApp 使用本地緩存的訪問憑證去請求 Aliyun Service API。雲服務會感知 STS 訪問憑證,並會依賴 STS 服務來驗證訪問憑證,正確響應用戶請求。
STS 安全令牌、角色管理和使用相關內容詳情,請參考 RAM 角色管理。調用 STS 服務接口AssumeRole來獲取有效訪問憑證即可。
操作步驟
假設有一個名為 ram-test 的 Bucket 用於存儲用戶數據,現將利用用戶子賬號結合 STS 實現 OSS 權限控制訪問。
您可以通過 OSS SDK 與 STS SDK 的結合使用,實現使用 STS 臨時授權訪問 OSS 實例。
- 創建用戶子賬號。
- 登錄 RAM 訪問控制管理控制台。
- 在 RAM 訪問控制頁面,單擊用戶。
- 在用戶頁面,單擊新建用戶。
- 在新建用戶頁面,用戶賬號信息填寫登錄名稱、顯示名稱,訪問方式下勾選編程訪問,並單擊確定。
- 單擊
- 在添加權限頁面,為已創建子賬號添加 AliyunSTSAssumeRoleAccess 權限。
說明 盡量不要賦予子賬號其他任意權限,因為在扮演角色的時候會自動獲得被扮演角色的所有(部分)權限。
- 創建權限策略。
- 登錄 RAM 訪問控制管理控制台。
- 在 RAM 訪問控制頁面,單擊權限策略管理。
- 單擊新建權限策略。
- 在新建自定義權限策略頁面,填寫策略名稱、備注,配置模式選擇可視化配置或腳本配置。
以腳本配置為例,對 ram-test 添加 GetObject與 PutObject等權限,在 策略內容中配置腳本如下:
{ "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" }
- 創建角色。
- 登錄 RAM 訪問控制管理控制台。
- 在 RAM 訪問控制頁面,單擊RAM 角色管理。
- 在 RAM 角色管理頁面,單擊新建 RAM 角色。
- 在新建 RAM 角色頁面,填寫 RAM 角色名稱,本示例 RAM 角色名稱為 RamOssTest,選擇可信實體類型及受信雲賬號 ID 保留默認選項。
- 單擊已創建 RAM 角色 RamOssTest 右側對應的添加權限 。
- 在添加權限頁面,選擇自定義權限策略,添加步驟 2 中創建的策略 Ramtest。
- 通過 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 = "{\n" + " \"Statement\": [\n" + " {\n" + " \"Action\": [\n" + " \"oss:GetObject\",\n" + " \"oss:PutObject\",\n" + " \"oss:DeleteObject\",\n" + " \"oss:ListParts\",\n" + " \"oss:AbortMultipartUpload\",\n" + " \"oss:ListObjects\"\n" + " ],\n" + " \"Effect\": \"Allow\",\n" + " \"Resource\": [\n" + " \"acs:oss:*:*:ram-test/*\",\n" + " \"acs:oss:*:*:ram-test\"\n" + " ]\n" + " }\n" + " ],\n" + " \"Version\": \"1\"\n" + "}"; 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錯誤,請進行跨域設置