全部產品
Search
文件中心

Object Storage Service:Android授權訪問

更新時間:Feb 28, 2024

Android SDK提供了STS鑒權模式、自簽名模式以及簽名URL的方式,用於保障移動終端的安全性。

背景資訊

無論是STS鑒權模式還是自簽名模式,您實現的回呼函數都需要保證調用時Token、Signature的返回結果。如果您需要實現向業務Server擷取Token、Signature的網路請求,建議調用網路程式庫的同步介面。回調都是在SDK發起具體請求時,在請求的子線程中執行,所以不會阻塞主線程。

STS鑒權模式

說明

使用STS模式授權需要先開通阿里雲存取控制RAM服務。

OSS可以通過阿里雲STS(Security Token Service)進行臨時授權訪問。阿里雲STS是為雲端運算使用者提供臨時存取權杖的Web服務。通過STS,您可以為第三方應用或子使用者(即使用者身份由您自己管理的使用者)頒發一個自訂時效和許可權的訪問憑證。關於STS的更多資訊,請參見STS介紹

STS的優勢如下:

  • 您無需透露您的長期密鑰(AccessKey)給第三方應用,只需產生一個存取權杖並將令牌交給第三方應用。您可以自訂這個令牌的存取權限及有效期間限。

  • 您無需關心許可權撤銷問題,存取權杖到期後自動失效。

通過STS臨時授權訪問OSS的步驟如下:

  1. 擷取臨時訪問憑證

    臨時訪問憑證包括臨時存取金鑰(AccessKey ID和AccessKey Secret)和安全性權杖(SecurityToken)。臨時訪問憑證有效時間單位為秒,最小值為900,最大值以當前角色設定的最大會話時間為準。更多資訊,請參見設定RAM角色最大會話時間

    您可以通過以下兩種方式擷取臨時訪問憑證。

    • 方式一

      通過調用STS服務的AssumeRole介面擷取臨時訪問憑證。

    • 方式二

      通過各語言STS SDK擷取臨時訪問憑證。

  2. 使用臨時訪問憑證初始化SDK

    String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
    
    OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider("StsToken.AccessKeyId", "StsToken.SecretKeyId", "StsToken.SecurityToken");
    
    OSS oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider);                  

    通過臨時訪問憑證初始化SDK時,需要注意StsToken的有效時間。

    以下代碼用於在判斷StsToken的有效時間小於5分鐘時對StsToken進行更新:

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date date = sdf.parse("<StsToken.Expiration>");
    long expiration = date.getTime() / 1000;
    // 如果StsToken即將到期,有效時間小於5分鐘,則更新StsToken。
    if (DateUtil.getFixedSkewedTimeMillis() / 1000 > expiration - 5 * 60) {
        oss.updateCredentialProvider(new OSSStsTokenCredentialProvider("StsToken.AccessKeyId", "StsToken.SecretKeyId", "StsToken.SecurityToken"));
    }
    • 手動更新StsToken

      當判斷StsToken即將到期時,您可以重新構造OSSClient,也可以通過如下方式更新CredentialProvider:

      oss.updateCredentialProvider(new OSSStsTokenCredentialProvider("StsToken.AccessKeyId", "StsToken.SecretKeyId", "StsToken.SecurityToken"));                   
    • 自動更新StsToken

      如果您期望SDK自動更新StsToken,那麼您需要在SDK的應用中實現回調。通過您實現回調的方式去擷取Federation Token(即StsToken),SDK會使用此StsToken來進行加簽處理,並在需要更新時主動調用此回調來擷取StsToken。

      String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
      
      OSSCredentialProvider credentialProvider = new OSSFederationCredentialProvider() {
      
          @Override
          public OSSFederationToken getFederationToken() {
          // 擷取FederationToken,並將其構造為OSSFederationToken對象返回。如果因某種原因FederationToken擷取失敗,伺服器則直接返回null。
      
              OSSFederationToken token;
              // 從您的伺服器中擷取Token。
              return token;
          }
      };
      
      OSS oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider);                    
      說明

      如果您已經通過其他方式擷取了StsToken所需的各個欄位,也可以在回調中直接返回StsToken。但您需要手動處理StsToken的更新,且更新後重新設定該OSSClient執行個體的OSSCredentialProvider。

      假設您訪問的Server地址為http://localhost:8080/distribute-token.json,則返回的資料如下:

      {
          "StatusCode": 200,
          "AccessKeyId":"STS.iA645eTOXEqP3cg3****",
          "AccessKeySecret":"rV3VQrpFQ4BsyHSAvi5NVLpPIVffDJv4LojU****",
          "Expiration":"2015-11-03T09:52:59Z",
          "SecurityToken":"CAES7QIIARKAAZPlqaN9ILiQZPS+JDkS/GSZN45RLx4YS/p3OgaUC+oJl3XSlbJ7StKpQ****"}                    

      實現OSSFederationCredentialProvider的樣本如下:

      OSSCredentialProvider credetialProvider = new OSSFederationCredentialProvider() {
          @Override
          public OSSFederationToken getFederationToken() {
              try {
                  URL stsUrl = new URL("http://localhost:8080/distribute-token.json");
                  HttpURLConnection conn = (HttpURLConnection) stsUrl.openConnection();
                  InputStream input = conn.getInputStream();
                  String jsonText = IOUtils.readStreamAsString(input, OSSConstants.DEFAULT_CHARSET_NAME);
                  JSONObject jsonObjs = new JSONObject(jsonText);
                  String ak = jsonObjs.getString("AccessKeyId");
                  String sk = jsonObjs.getString("AccessKeySecret");
                  String token = jsonObjs.getString("SecurityToken");
                  String expiration = jsonObjs.getString("Expiration");
                  return new OSSFederationToken(ak, sk, token, expiration);
              } catch (Exception e) {
                  e.printStackTrace();
              }
              return null;
          }
      };                    

自簽名模式

您可以把AccessKey ID和AccessKey Secret儲存在自有伺服器中後,通過自有伺服器對用戶端資訊進行簽名。具體步驟如下:

  1. 在用戶端擷取並發送待簽名的字串到自有伺服器。

    1. 構造請求時通過SDK中OSSCustomSignerCredentialProvider的signContent方法擷取待簽名的字串。

    2. 發送待簽名的字串到自有伺服器。

  2. 在自有伺服器進行簽名並返回簽名後的字串到用戶端。

    1. 按照OSS規定的簽名演算法對待簽名字串進行簽名。關於簽名演算法的更多資訊,請參見簽名版本1

      簽名演算法的格式為signature = "OSS " + AccessKeyId + ":" + base64(hmac-sha1(AccessKeySecret, content)),其中content是已根據請求參數拼接後的字串。

    2. 返回簽名後的字串給用戶端。

      假設服務端地址為http://localhost:8080/sign,將content傳給服務端進行簽名,擷取簽名後的signature返回給用戶端。範例程式碼如下:

      String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
      
      OSSCredentialProvider credentialProvider = new OSSCustomSignerCredentialProvider() {
          @Override
          public String signContent(String content) {
              URL stsUrl = new URL("http://localhost:8080/sign?content=" + content);
              HttpURLConnection conn = (HttpURLConnection) stsUrl.openConnection();
              InputStream input = conn.getInputStream();
              String jsonText = IOUtils.readStreamAsString(input, OSSConstants.DEFAULT_CHARSET_NAME);
              JSONObject jsonObjs = new JSONObject(jsonText);
              String signature = jsonObjs.getString("signature");
              return signature;
          }
      };
      OSS oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider);
  3. 在用戶端發送簽名後的字串到OSS伺服器進行鑒權。

簽名URL

您可以將產生的簽名URL提供給訪客進行臨時訪問。產生簽名URL時,您可以自訂URL的到期時間來限制訪客的訪問時間長度。

重要

通過以下樣本產生的簽名URL中如果包含特殊符號+,可能出現無法正常訪問該簽名URL的現象。如需正常訪問該簽名URL,請將簽名URL中的+替換為%2B

以下代碼用於產生簽名URL,並通過簽名URL上傳和下載檔案。

產生簽名URL並通過簽名URL上傳檔案

  1. 產生用於上傳的簽名URL

    // 填寫Bucket名稱,例如examplebucket。
    String bucketName = "examplebucket";
    // 填寫不包含Bucket名稱在內源Object的完整路徑,例如exampleobject.txt。
    String objectKey = "exampleobject.txt";
    // 設定content-type。
    String contentType = "application/octet-stream";
    String url = null;
    try {
        // 產生用於上傳檔案的簽名URL。
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey);
        // 設定簽名URL的到期時間為30分鐘。
        request.setExpiration(30*60);
        request.setContentType(contentType);    
        request.setMethod(HttpMethod.PUT);
        url = mOss.presignConstrainedObjectURL(request);
        Log.d("url", url);
    } catch (ClientException e) {
        e.printStackTrace();
    }
  2. 通過簽名URL上傳檔案

    // 填寫產生的簽名URL。
    String url = "";
    // 指定本地檔案的完整路徑。
    String localFile = "/storage/emulated/0/oss/examplefile";
    // 設定content-type。
    String contentType = "application/octet-stream";
    // 通過簽名URL上傳檔案。
    OkHttpClient client = new OkHttpClient();
    Request putRequest = new Request.Builder()
            .url(url)
            .put(RequestBody.create(MediaType.parse(contentType), new File(localFile)))
            .build();
    client.newCall(putRequest).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
        }
    
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            Log.d("response", response.body().string());
        }
    });

產生簽名URL並通過簽名URL下載檔案

  1. 產生用於下載的簽名URL

    // 填寫Bucket名稱,例如examplebucket。
    String bucketName = "examplebucket";
    // 填寫不包含Bucket名稱在內源Object的完整路徑,例如exampleobject.txt。
    String objectKey = "exampleobject.txt";
    String url = null;
    try {
        // 產生用於下載檔案的簽名URL。
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey);
        // 設定簽名URL的到期時間為30分鐘。
        request.setExpiration(30*60);
        request.setMethod(HttpMethod.GET);
        url = mOss.presignConstrainedObjectURL(request);
        Log.d("url", url);
    } catch (ClientException e) {
        e.printStackTrace();
    }
  2. 通過簽名URL下載檔案

    // 填寫產生的簽名URL。
    String url = "";
    OkHttpClient client = new OkHttpClient();
    // 通過簽名URL下載檔案。
    Request getRequest = new Request.Builder()
            .url(url)
            .get()
            .build();
    client.newCall(getRequest).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
        }
    
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (response.code() == 203 || response.code() >= 300) {
                Log.d("download", "fail");
                Log.d("download", response.body().string());
                return;
            }
            // 請求成功。
            InputStream inputStream = response.body().byteStream();
    
            byte[] buffer = new byte[2048];
            int len;
    
            while ((len = inputStream.read(buffer)) != -1) {
                // 處理下載的資料,例如圖片展示或者寫入檔案等。
            }
        }
    });