最近作一個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調用的,前端會在相應位置配置該接口地址。
參考博客: