苏铭客

导航

Ali OSS 服务端签名并设置回调,客户端上传文件

一.最近做阿里云oss文件上传开发,一点收获分享给大家,帮助大家绕过一些坑。关于阿里云oss服务的介绍,我这里不做赘述了,可以查看阿里云OSS开发api文档。

     在这里我主要介绍下,文件上传流程比较复杂的服务器设置回调功能。整个流程图是这样的:

     

 

        大家可以参考阿里api文档介绍的工作流程说明:https://help.aliyun.com/document_detail/31927.html?spm=5176.doc31988.6.633.OTY557

二. 客户端请求policy,签名,key以及回调地址等

     1.首先定义我们的业务类型biz_type,主要是将文件按业务类型目录存储。这里我要着重强调一下,阿里oss回调请求的自定义参数:

 ${x:biz_type},不要使用驼峰法命名,在开发的工程中,发现驼峰法命名bizType 取不到值。建议需要命名时 用"_"取代。
2.accessId ,accesskey 定义在properties文件中,这些定值从文件中读取,这里我要说明下:定义的end 和 endPoint

     endpoint = http://oss-cn-shanghai.aliyuncs.com
     end = oss-cn-shanghai.aliyuncs.com

     bucket 是定义的桶名称就类似于文件目录名称。

     2. callbackurl 定义自己的回调方法

     3.签名,失效时间,policy,这些是根据阿里云官网提供的案例代码写的。

     4.在定义callback字符串时,遇到一个大坑,这也是我写整篇文章的原因。

       定义callback 时,阿里支持两种方式,一个是callbackBody字符串直接拼接 用&符连接。

       这里我使用的是第二种方式,callbackBody 采用json字符串。但是这跟以往的字符串不一样,中间有阿里自带的字符bucket,object,size等以及自定义的字符,ali oss 需要解析自定义字符,这里可以理解成

      变量的一个占位符,那么json串是如何拼接一个占位符变量呢???带着这个问题,我就一遍一遍的拼串,尝试了大概有两小时,看了官网的一篇错误排查文章:https://yq.aliyun.com/articles/68863/

      定义的回调字符串是这样的:

  {
    "callbackUrl" : "http://abc.com/test.php",
    "callbackHost" : "oss-cn-hangzhou.aliyuncs.com",
    "callbackBody" : "{\"bucket\":${mimeType}, \"object\":${object},\"size\":${size},\"mimeType\":${mimeType},\"my_var\":${x:my_var}}",
    "callbackBodyType" : "application/json"
  } 
顿时眼睛亮了,callbackBody 定义的原来是一个字符串的jsonobject,一直以为需要定义一个占位符类型的jsonobject对象。
  然后继续拼串,定义的callbackBody就是这样: "{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid},\'biz_type\':${x:biz_type}}"
最终将 resMap 转成json 字符返回。
 1 public String reqOssServer() throws ServletException, IOException {
 2           
 3         String[] bizArray = new String[]{"","user-dir/","advise-dir/"};
 4           
 5         String biz_type = getRequest().getParameter("biz_type");
 6           
 7         String sysCurrtime = System.currentTimeMillis()+"";
 8         
 9           //15位随机码
10         String randStr = sysCurrtime.substring(5,sysCurrtime.length())+CommonUtil.getRamdon(15);
11           //目录
12         String dir = bizArray[Integer.valueOf(biz_type)];
13         
14         PropertiesUtil pro = new PropertiesUtil("oss.properties");
15         String end = pro.readValue("end");
16         String endpoint = pro.readValue("endpoint");
17         String accessId = pro.readValue("accessKeyId");
18         String accessKey = pro.readValue("accessKeySecret");
19         String bucket = pro.readValue("bucketName");
20             
21         String host = "https://" + bucket + "." + end;
23         
24         //oss 回调地址
25         String callbackurl = Cont.TEST_DOMAIN_NAME+"/osscallback";
26         
27         OSSClient client = new OSSClient(endpoint, accessId, accessKey);
28         try {     
29             long expireTime = 3000;
30             long expireEndTime = System.currentTimeMillis() + expireTime * 100;
31             Date expiration = new Date(expireEndTime);
32             PolicyConditions policyConds = new PolicyConditions();
33             policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
34             policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
35 
36             String postPolicy = client.generatePostPolicy(expiration, policyConds);
37             byte[] binaryData = postPolicy.getBytes("utf-8");
38             String encodedPolicy = BinaryUtil.toBase64String(binaryData);
39             String postSignature = client.calculatePostSignature(postPolicy);
40             
41             
42             Map<String, String> respMap = new LinkedHashMap<String, String>();
43             URL url = client.generatePresignedUrl(bucket, accessKey, expiration);
44             respMap.put("accessid", accessId);
45             respMap.put("policy", encodedPolicy);
46             respMap.put("signature", postSignature);
47             //respMap.put("expire", formatISO8601Date(expiration));
48             respMap.put("dir", dir);
49             respMap.put("host", host);
50             respMap.put("expire", String.valueOf(expireEndTime / 1000));
51             respMap.put("url", url.toString());
52             respMap.put("key", randStr);
53             //bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&my_var=${x:my_var}
54            /* String callback = "{\"callbackUrl\":\"" + callbackurl + "\",\"callbackBody\":\"filename=${object}&size=${size}&mimeType=${mimeType}&"
55                     + "orderid=" + orderid + "&calbackright=" + calbackright + "\",\"callbackBodyType\":\"application/x-www-form-urlencoded\"}";*/
56             //先传入一个固定值测试
57            // String callback = "{\"callbackUrl\":\"" + callbackurl + "\",\"callbackBody\":\"filename=${object}&size=${size}&mimeType=${mimeType}&orderid=123\",\"callbackBodyType\":\"application/x-www-form-urlencoded\"}";
58             
59             //String callback = "{\"callbackUrl\":\""+callbackurl+"\",\"callbackBody\":\"{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid}}\",\"callbackBodyType\":\"application/json\"}";
60             
61             String callbackbody = "{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid},\'biz_type\':${x:biz_type}}";
62             if("1".equals(biz_type)){//用户头像,banner修改
63                 callbackbody = "{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid},\'biz_type\':${x:biz_type},\'portrait\':${x:portrait},\'banner\':${x:banner}}";
64             }else if("2".equals(biz_type)){//投诉建议
65                 callbackbody = "{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid},\'biz_type\':${x:biz_type},\'path\':${x:path},\'guideid\':${x:guideid}}";
66             }
67             
68             String callback = "{\"callbackUrl\":\""+callbackurl+"\",\"callbackBody\":\""+callbackbody+"\",\"callbackBodyType\":\"application/json\"}";
69             
70             byte[] bytes = Base64.encodeBase64(callback.getBytes("UTF-8"));
71             respMap.put("callback", new String(bytes));
72             //respMap.put("callback_str", callback);
73             JSONObject ja1 = JSONObject.fromObject(respMap);
74             System.out.println("=====respMap:===== "+ja1.toString());
75             getResponse().setHeader("Access-Control-Allow-Origin", "*");
76             getResponse().setHeader("Access-Control-Allow-Methods", "GET, POST");
77             
78             setJsonString(AppJSON.succReq("请求成功", ja1.toString()));
79         } catch (Exception e) {
80             Assert.fail(e.getMessage());
81         }
82           
83         return "ajax";
84       }

 三. 回调方法处理业务逻辑

      该回调方法是一个servlet,需要在web.xml中配置 访问的路径地址。在servlet中,主要进行业务处理,这里就不在贴出我的业务处理了,大家根据自己的实际需要进行编码。

  1 package com.zd.servlet;
  2 import java.io.BufferedReader;
  3 import java.io.IOException;
  4 import java.io.InputStream;
  5 import java.io.InputStreamReader;
  6 import java.net.URI;
  7 import java.net.URL;
  8 import java.security.KeyFactory;
  9 import java.security.PublicKey;
 10 import java.security.spec.X509EncodedKeySpec;
 11 import java.text.ParseException;
 12 import java.util.Date;
 13 import javax.servlet.ServletException;
 14 import javax.servlet.annotation.WebServlet;
 15 import javax.servlet.http.HttpServlet;
 16 import javax.servlet.http.HttpServletRequest;
 17 import javax.servlet.http.HttpServletResponse;
 18 import net.sf.json.JSONObject;
 19 import org.apache.commons.lang.StringUtils;
 20 import org.apache.http.HttpResponse;
 21 import org.apache.http.client.methods.HttpGet;
 22 import org.apache.http.impl.client.DefaultHttpClient;
 23 import com.aliyun.oss.common.utils.BinaryUtil;
 24 import com.zd.aliyun.oss.HttpMethod;
 25 import com.zd.aliyun.oss.OSSClient;
 26 import com.zd.aliyun.oss.common.utils.DateUtil;
 27 import com.zd.aliyun.oss.model.GeneratePresignedUrlRequest;
 28 import com.zd.util.Cont;
 29 import com.zd.util.FileUtil;
 30 import com.zd.util.StringUtil;
 31 import com.zd.util.codeBuild.PropertiesUtil;
 32 
 33 
 34 @SuppressWarnings("deprecation")
 35 @WebServlet(asyncSupported = true)
 36 public class OssCallBackService extends HttpServlet {
 37     protected void doGet(HttpServletRequest req, HttpServletResponse resp)
 38             throws ServletException, IOException {
 39         this.doPost(req, resp);
 40         response(req, resp, "input get ", 200);
 41     }
 44     
 45     @SuppressWarnings({ "finally" })
 46     public String executeGet(String url) {
 47         BufferedReader in = null;
 49         String content = null;
 50         try {
 51             // 定义HttpClient
 52             @SuppressWarnings("resource")
 53             DefaultHttpClient client = new DefaultHttpClient();
 54             // 实例化HTTP方法
 55             HttpGet request = new HttpGet();
 56             request.setURI(new URI(url));
 57             HttpResponse response = client.execute(request);
 58 
 59             in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
 60             StringBuffer sb = new StringBuffer("");
 61             String line = "";
 62             String NL = System.getProperty("line.separator");
 63             while ((line = in.readLine()) != null) {
 64                 sb.append(line + NL);
 65             }
 66             in.close();
 67             content = sb.toString();
 68         } catch (Exception e) {
 69         } finally {
 70             if (in != null) {
 71                 try {
 72                     in.close();// 最后要关闭BufferedReader
 73                 } catch (Exception e) {
 74                     e.printStackTrace();
 75                 }
 76             }
 77             return content;
 78         }
 79     }
 80 
 81     public String GetPostBody(InputStream is, int contentLen) {
 82         if (contentLen > 0) {
 83             int readLen = 0;
 84             int readLengthThisTime = 0;
 85             byte[] message = new byte[contentLen];
 86             try {
 87                 while (readLen != contentLen) {
 88                     readLengthThisTime = is.read(message, readLen, contentLen - readLen);
 89                     if (readLengthThisTime == -1) {// Should not happen.
 90                         break;
 91                     }
 92                     readLen += readLengthThisTime;
 93                 }
 94                 return new String(message);
 95             } catch (IOException e) {
 96             }
 97         }
 98         return "";
 99     }
100 
101    //oss回调请求
102     protected boolean VerifyOSSCallbackRequest(HttpServletRequest request, String ossCallbackBody) throws NumberFormatException, IOException
103     {
104         boolean ret = false;    
105         String autorizationInput = new String(request.getHeader("Authorization"));
106         String pubKeyInput = request.getHeader("x-oss-pub-key-url");
107         byte[] authorization = BinaryUtil.fromBase64String(autorizationInput);
108         byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
109         String pubKeyAddr = new String(pubKey);
110         if (!pubKeyAddr.startsWith("http://gosspublic.alicdn.com/") && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/"))
111         {
112             System.out.println("pub key addr must be oss addrss");
113             return false;
114         }
115         String retString = executeGet(pubKeyAddr);
116         retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
117         retString = retString.replace("-----END PUBLIC KEY-----", "");
118         String queryString = request.getQueryString();
119         String uri = request.getRequestURI();
120         String decodeUri = java.net.URLDecoder.decode(uri, "UTF-8");
121         String authStr = decodeUri;
122         if (queryString != null && !queryString.equals("")) {
123             authStr += "?" + queryString;
124         }
125         authStr += "\n" + ossCallbackBody;
126         ret = doCheck(authStr, authorization, retString);
127         return ret;
128     }
129 
130     
131     protected void doPost(HttpServletRequest request, HttpServletResponse response)
132             throws ServletException, IOException {
133         //HttpServletRequest
134         String ossCallbackBody = GetPostBody(request.getInputStream(), Integer.parseInt((request).getHeader("content-length")));
135         System.out.println("ossCallbackBody == "+ossCallbackBody);
136         
137         boolean ret = VerifyOSSCallbackRequest(request, ossCallbackBody);
138         System.out.println("verify result:" + ret);
139         
140         // String aaa  ="{\"bucket\":\"picture-zoomdu\",\"object\":\"2017072131713.jpg\",\"size\":\"12345\","gid":"86","uid":"121","type":"1"}
141         JSONObject obj = JSONObject.fromObject(ossCallbackBody);
142         PropertiesUtil pro = new PropertiesUtil("oss.properties");
143         String endpoint = pro.readValue("endpoint");
144         String accessKeyId = pro.readValue("accessKeyId");
145         String accessKeySecret = pro.readValue("accessKeySecret");
146         
147         String bucketName = obj.get("bucket").toString();
148         String bucketkey = obj.get("object").toString();
149         
150         OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
151         Date expirations = null;
152         try {
153             expirations = DateUtil.parseRfc822Date("Wed, 18 Mar 2019 14:20:00 GMT");
154         } catch (ParseException e) {
155             e.printStackTrace();
156         }
157         GeneratePresignedUrlRequest requestGenerate = new GeneratePresignedUrlRequest(bucketName, bucketkey, HttpMethod.GET);
158         requestGenerate.setExpiration(expirations);
159         
160         URL signedUrl = ossClient.generatePresignedUrl(requestGenerate);
161         String path =signedUrl.toString();
162         if(signedUrl.toString().indexOf("?")>-1){
163             path = path.substring(0,signedUrl.toString().indexOf("?"));
164         }
165         // 图片压缩配置的字符串
166         path += "?x-oss-process=style/zd_list_image";
167         String ossurl = "{\"ossurl\":\""+signedUrl.toString()+"\"}";
168         if (ret){//签名验证通过,业务处理
169             //客户端 在回调中需要 传入的字段
171             
172             response(request, response, "{\"Status\":\"OK\"}", HttpServletResponse.SC_OK);
173             //业务处理..... 此处省略
174             if(obj.containsKey("uid") && StringUtils.isNotEmpty(obj.get("uid")+"")){
175                 String uid  = obj.get("uid").toString();
176                 //biz_type : 1:用户头像、banner ,2:投诉建议图片上传220             }
221         }
222         else{
223             response(request, response, "{\"Status\":\"verdify not ok\"}", HttpServletResponse.SC_BAD_REQUEST);
224         }
226     }
227     
229     public static boolean doCheck(String content, byte[] sign, String publicKey) {
230         try {
231             KeyFactory keyFactory = KeyFactory.getInstance("RSA");
232             byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
233             PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
234             java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
235             signature.initVerify(pubKey);
236             signature.update(content.getBytes());
237             boolean bverify = signature.verify(sign);
238             return bverify;
239 
240         } catch (Exception e) {
241             e.printStackTrace();
242         }
243 
244         return false;
245     }
246 
247     private void response(HttpServletRequest request, HttpServletResponse response, String results, int status) throws IOException {
248         String callbackFunName = request.getParameter("callback");
249         System.out.println("callbackFunName:"+callbackFunName);
250         response.addHeader("Content-Length", String.valueOf(results.length()));
251         if (callbackFunName == null || callbackFunName.equalsIgnoreCase(""))
252             response.getWriter().println(results);
253         else
254             response.getWriter().println(callbackFunName + "( " + results + " )");
255         response.setStatus(status);
256         response.flushBuffer();
257     }
258     
259 }

 四.关于oss 上传,并设置回调就介绍到这里,本篇文章没有介绍,oss服务配置,主要是讲技术坑。如有什么问题,请留言联系我,很乐意解决大家的问题。

       另外郑重说明,本篇文章是本人原创,如需转载 请标注转载原文地址。

   

posted on 2017-12-03 14:06  苏铭客  阅读(9498)  评论(0编辑  收藏  举报