阿里雲對象存儲 OSS 應用服務器搭建代碼

背景說明

最近作一個APP客戶端圖片直傳阿里雲OSS的服務,須要在後臺開一個阿里雲的OSSToken獲取的接口。html

阿里雲官方文檔地址:快速搭建移動應用直傳服務前端

略過移動端說明,直接看服務端的。java

不是移動端直傳嗎,爲何須要服務端呢?緣由以下:web

Android和iOS應用不能直接存儲AccessKey,這樣會存在數據泄露的風險。因此應用必須向用戶的應用服務器申請一個Token。spring

這個Token是有時效性的,若是Token的過時時間是30分鐘(由應用服務器指定),那麼在這30分鐘裏,該Android/iOS應用可使用此Token從OSS上傳和下載數據, 30分鐘後須要從新獲取Token。json

正確返回的參數 Expiration 爲該Token失效的時間。Android SDK會自動判斷Token是否失效,若是失效,則自動獲取Token。api

代碼實現

這裏有個Java代碼示例,能夠下載來參考,把功能集成到項目中,不必爲一個接口單獨部署一個項目到服務器。緩存

項目環境是 SpringBoot2.0,JDK8(雖然官方說的是適用java1.7,可是JDK8兼容可用。)服務器

添加依賴

首先在pom中添加相關依賴app

        <!--阿里雲對象存儲-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-sts</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.0.0</version>
        </dependency>    

配置屬性文件

能夠從下載的 AppTokenServerDemo 項目中找到 config.json 文件,將其中的五個參數都配置到咱們本身的屬性文件 application.properties 中。

#阿里雲對象存儲
ali.oss.AccessKeyID=xxxxxx
ali.oss.AccessKeySecret=xxxxxxxxxxxxxxx
ali.oss.RoleArn=xxxxxxxxxxxxxx
ali.oss.TokenExpireTime=900
ali.oss.PolicyFile=policy/all_policy.txt

編寫接口類

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * @date 2019/4/7 20:18
 * @auther wangbo
 */
@Api(tags = {"OSS接口類"})
@RestController
@RequestMapping("/api/oss")
public class ALiOSSController {
    //只有 RAM用戶(子帳號)才能調用 AssumeRole 接口
    //阿里雲主帳號的AccessKeys不能用於發起AssumeRole請求
    //請首先在RAM控制檯建立一個RAM用戶,併爲這個用戶建立AccessKeys
    @Value("${ali.oss.AccessKeyID}")
    private String accessKeyId;

    @Value("${ali.oss.AccessKeySecret}")
    private String accessKeySecret;

    //RoleArn 須要在 RAM 控制檯上獲取
    @Value("${ali.oss.RoleArn}")
    private String roleArn;

    //Token 過時時間,默認 900s
    @Value("${ali.oss.TokenExpireTime}")
    private Long tokenExpireTime;

    //阿里雲對象存儲例子的 token 權限文件
    //all_policy.txt:指定了該token擁有對該帳號下建立Bucket、刪除Bucket、上傳文件、下載文件、刪除文件的權限 。
    //bucket_read_policy.txt:指定了該token擁有該帳號下對指定Bucket的讀權限。
    //bucket_read_write_policy.txt:指定了該token擁有該帳號下對指定Bucket的讀寫權限。
    @Value("${ali.oss.PolicyFile}")
    private String policyFile;

    //目前只有"cn-hangzhou"這個region可用, 不要使用填寫其餘region的值
    private static final String REGION_CN_HANGZHOU = "cn-hangzhou";

    //這個是STS版本值
    private static final String STS_API_VERSION = "2015-04-01";

    @ApiOperation(value = "獲取APPToken", notes = "獲取阿里雲對象存儲的token")
    @GetMapping("/getAppToken")
    public void getAppToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //RoleSessionName 是臨時Token的會話名稱,本身指定用於標識你的用戶,主要用於審計,或者用於區分Token頒發給誰
        //可是注意RoleSessionName的長度和規則,不要有空格,只能有'-' '_' 字母和數字等字符
        //具體規則請參考API文檔中的格式要求
        String roleSessionName = "alice-001";
        //此處必須爲 HTTPS
        ProtocolType protocolType = ProtocolType.HTTPS;
        //獲取文件的字符串
        String policy = ReadJson(policyFile);
        //建立json對象
        JSONObject respMap = new JSONObject();
        try {
            final AssumeRoleResponse stsResponse = assumeRole(accessKeyId, accessKeySecret, roleArn, roleSessionName,
                    policy, protocolType, tokenExpireTime);
            //設置獲取阿里雲對象存儲成功的值
            respMap.put("StatusCode", "200");
            respMap.put("AccessKeyId", stsResponse.getCredentials().getAccessKeyId());
            respMap.put("AccessKeySecret", stsResponse.getCredentials().getAccessKeySecret());
            respMap.put("SecurityToken", stsResponse.getCredentials().getSecurityToken());
            respMap.put("Expiration", stsResponse.getCredentials().getExpiration());
        } catch (ClientException e) {
            //設置獲取阿里雲對象存儲失敗的值
            respMap.put("StatusCode", "500");
            respMap.put("ErrorCode", e.getErrCode());
            respMap.put("ErrorMessage", e.getErrMsg());
        }
        //response寫回json數據
        response(request, response,respMap.toJSONString());
    }

    protected AssumeRoleResponse assumeRole(String accessKeyId, String accessKeySecret, String roleArn,
                                                    String roleSessionName, String policy, ProtocolType protocolType, long durationSeconds) throws ClientException{
        try {
            //建立一個 Aliyun Acs Client, 用於發起 OpenAPI 請求
            IClientProfile profile = DefaultProfile.getProfile(REGION_CN_HANGZHOU, accessKeyId, accessKeySecret);
            DefaultAcsClient client = new DefaultAcsClient(profile);

            //建立一個 AssumeRoleRequest 並設置請求參數
            final AssumeRoleRequest request = new AssumeRoleRequest();
            request.setVersion(STS_API_VERSION);
            request.setMethod(MethodType.POST);
            request.setProtocol(protocolType);

            request.setRoleArn(roleArn);
            request.setRoleSessionName(roleSessionName);
            request.setPolicy(policy);
            request.setDurationSeconds(durationSeconds);

            //發起請求,並獲得response
            final AssumeRoleResponse response = client.getAcsResponse(request);

            return response;
        }catch (ClientException e){
            throw e;
        }
    }

    /**
     * 讀取配置文件
     * @param path
     * @return
     */
    public static String ReadJson(String path){
        BufferedReader reader = null;
        //返回值,使用StringBuffer
        StringBuffer data = new StringBuffer();
        try {
            //從給定位置獲取文件
            ClassPathResource resource = new ClassPathResource(path);
            InputStream inputStream = resource.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream));
            //每次讀取文件的緩存
            String temp = null;
            while((temp = reader.readLine()) != null){
                data.append(temp);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //關閉文件流
            if (reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return data.toString();
    }

    /**
     * 服務器響應結果
     * @param request
     * @param response
     * @param results
     * @throws IOException
     */
    private void response(HttpServletRequest request, HttpServletResponse response, String results) throws IOException {
        String callbackFunName = request.getParameter("callback");
        if (callbackFunName==null || callbackFunName.equalsIgnoreCase(""))
            response.getWriter().println(results);
        else
            response.getWriter().println(callbackFunName + "( "+results+" )");
        response.setStatus(HttpServletResponse.SC_OK);
        response.flushBuffer();
    }
}

此外須要把相關的資源文件(能夠在下載的demo項目中獲取)引入到resource目錄下:

踩坑記錄

代碼中的文件讀取部分在我本地 idea 中運行服務時,能夠正常執行。可是將服務打包成可執行jar的包,以jar服務運行服務時報錯。

java.io.FileNotFoundException: class path resource [policay/all_policay.txt] cannot be resolved to absolute file path because it does not reside in the file system

緣由:打包後Spring沒法使用resource.getFile()訪問JAR中的路徑的文件,必須使用resource.getInputStream()。

我嘗試了好幾種:

(1)使用demo項目中的方式,本地線上都找不到文件

File file = new File("policay/all_policay.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));

(2)使用ResouceUtils類,線上版本找不到文件

File file = ResourceUtils.getFile("classpath:policay/all_policay.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));

(3)最後改成使用InputStream才解決問題。

ClassPathResource resource = new ClassPathResource("policay/all_policay.txt");
InputStream inputStream
= resource.getInputStream();
BufferedReader reader
= new BufferedReader(new InputStreamReader(inputStream));

另外注意,該接口須要定義爲GET接口,而且響應值不能自定義的,須要按照demo中的形式進行響應,由於該接口是給阿里雲OSS調用的,前端會在相應位置配置該接口地址。

參考博客:

快速上手阿里雲對象存儲(一)

快速上手阿里雲對象存儲(二)

快速上手阿里雲對象存儲(三)

相關文章
相關標籤/搜索