OBS服务应用于互联网数据上传时 使用POST 实现服务端和客户端权限控制和数据上传分离的方法

举报
shan0304 发表于 2019/06/09 15:59:55 2019/06/09
【摘要】 1 背景采用JavaScript SDK 等客户端直接签名时,AccessKeyID和AcessKeySecret会暴露在前端页面,因此存在严重的安全隐患。因此,OBS提供了服务端签名后直传的方案 解决此问题。问题代码如下所示(需要在前端使用ak sk作为初始化条件):2 原理介绍1. 客户端在登陆后,向app server请求上传对象的鉴权token;2. ...


1      背景

采用JavaScript SDK 等客户端直接签名时,AccessKeyIDAcessKeySecret会暴露在前端页面,因此存在严重的安全隐患。因此,OBS提供了服务端签名后直传的方案 解决此问题。问题代码如下所示(需要在前端使用ak sk作为初始化条件):

image.png

2      原理介绍

image.png

1.     客户端在登陆后,向app server请求上传对象的鉴权token

2.     App server 根据永久AK SK和针对上传对象和桶的policy生成一个token(具体参考后的代码示例)

3.     前端组件 收到token后使用post请求将token作为一个表单项进行对象上传。

 

同时我们也做了示例网站进行功能的展示;https://codepen.io/x00403408/pen/xQYZgE 此地址示例如何生成一个token

image.png

https://codepen.io/x00403408/pen/WYMrbY 网站示例POST请求如何使用token进行数据的上传

image.png

2.1.2        约束限制

1.     Post表单上传是单流上传,没法实现断点续传功能。因此比较适合一些小文件的上传。

2.     Post上传对于表单域我们采用强校验模式,只要携带的表单域除我们没定义的外都要包含在policy中参与签名计算。具体参考文档描述:https://support.huaweicloud.cn/api-obs/zh-cn_topic_0106557184.html

image.png

3      流程和源码解析

3.1      服务端代码

Java SDK 代码示例生成服务端token

package samples_java;
 
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
 
import com.obs.services.ObsClient;
import com.obs.services.ObsConfiguration;
import com.obs.services.exception.ObsException;
import com.obs.services.model.AuthTypeEnum;
import com.obs.services.model.PostSignatureRequest;
import com.obs.services.model.PostSignatureResponse;
 
public class TestPostObject {
    private static final String endPoint = "obs.myhwclouds.com";
 
    private static final String ak = "";
 
    private static final String sk = "";
 
    private static ObsClient obsClient;
 
    private static String bucketName = "";
 
    private static AuthTypeEnum authType = AuthTypeEnum.OBS;
 
    public static void main(String[] args) throws IOException {
        ObsConfiguration config = new ObsConfiguration();
        config.setEndPoint(endPoint);
        config.setAuthType(authType);
 
        try {
            obsClient = new ObsClient(ak, sk, config);
            //创建token
            PostSignatureRequest request = new PostSignatureRequest();
            request.setExpires(3600);
            ArrayList<String> conditions = new ArrayList<String>();
            //Condition可以根据不同业务头域的需求进行增删
            conditions.add("[\"starts-with\",\"$content-type\",\"\"]");
            conditions.add("[\"starts-with\",\"$key\",\"\"]");
            conditions.add("{\"bucket\":\""+bucket+"\"}");
            request.setConditions(conditions);
            PostSignatureResponse response = obsClient.createPostSignature(request);
            String Token = response.getToken();
           
        } catch (Exception ex) {
            if (ex instanceof ObsException) {
                ObsException e = (ObsException) ex;
                System.out.println("Message: " + e.getMessage());
            } else {
                ex.printStackTrace();
            }
        } finally {
            if (obsClient != null) {
                try {
                    obsClient.close();
                } catch (IOException e) {
                }
            }
        }
    }
 
}

3.2      客户端代码

客户端的代码在使用POST进行表单上传时候直接构造POST请求,其中传递token等表单域信息即可(JS代码);此处可以参考我们的web示例(https://codepen.io/x00403408/pen/WYMrbY)的代码(截取部分关键代码)

$(window.document).ready(function() {
 
  $('#progressBar').hide();
  $('#upload').click(function() {
    var token = $.trim($('#token').val());
    var bucket = $.trim($('#bucket').val());
    var endpoint = $.trim($('#endpoint').val());
 
    var fileList = $('#inputFile')[0].files;
    if(token === '' || bucket === '' || endpoint === '' || fileList.length <= 0){
      window.alert('输入有误!');
      return;
    }
 
    if(isIpAddress(endpoint)){
      endpoint += '/' + bucket;
    }else{
      endpoint = bucket + '.' + endpoint;
    }
    endpoint = 'https://' + endpoint;
 
 
    var xhr = new XMLHttpRequest();
    xhr.open('POST', endpoint, true);
    var formData = new FormData();
    var key = $.trim($('#key').val()) || fileList[0].name;
    formData.append('key', key);
    formData.append('token', token);
    var contentType = mimeTypes[key.substring(key.lastIndexOf('.') + 1)];
    //if(contentType){
           //formData.append('content-type', contentType);
    //}
    //var tokens = token.split(':');
    //formData.append('AccessKeyId', tokens[0]);
    //formData.append('Signature', tokens[1]);
    //formData.append('Policy', tokens[2]);
    formData.append('file', fileList[0]);
 
 
    var start = new Date().getTime();
    var cost;
 
    xhr.upload.addEventListener('progress', function(event) {
      if (event.lengthComputable) {
        if(!cost){
          $('#progressBar').show();
        }
        cost = new Date().getTime() - start;
        var uploadSpeed = (event.loaded / 1024 / cost * 1000).toFixed(2) + 'KB\/s';
        var percentage = Math.round(event.loaded * 100 / event.total) + '%';
        $('#uploadSpeed').html(uploadSpeed);
        $('#percentage').html(percentage);
        $('#progressBar').css('width', percentage);
      }
    }, false);
 
    xhr.onreadystatechange = function(response) {
      if(xhr.readyState === 4){
        if(xhr.status < 300){
          console.log('上传成功!');
        }else{
          console.log('上传失败!');
        }
       }
    };
    xhr.send(formData);

 

 

3.3      过程问题说明

3.3.1        POST 上传对应响应码200204对兼容性影响说明

POST 上传默认的响应码是204,但是在IE8/9浏览器对此响应码是不做状态上报的,导致web中的JS代码无法获取什么时候上传成功。际上是可以通过 success_action_status 重新定义响应状态码,在Post请求时候需要携带此项到表单域中。(POLICY中:["starts-with", "$success_action_status", ""],

请求示例:

image.png

  响应示例:

image.png

 

3.3.2        文件的content-type设置

因为 web/app类应用中为了防止不同用户上传的图片产生重名问题,客户在上传时候会针对原始文件名称生成一个唯一的对象名称KEY值,这样防止了存储在对象存储时候对象名称相同时候覆盖。

image.png

而这个时候导致一个问题,存储在对象存储的文件缺失了后缀名,JS-sdk不会增加content-type描述(通过后缀名增加)。OBS对于POST中携带默认的content-type描述不识别,导致设置为application/octect-stream;使得在浏览器下载时候不能在线展示;

 

image.png

那么这个时候处理需要在前端上传时候增加content-type表单域;同时在后端生成上传tokenpolicy也需要增加(POLICY中:["starts-with", "$ content-type ", ""],)来支持前端上传。


image.png

 


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。