金山快盘API二次开发 - OAUTH协议

OAuth

(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。

使用OAuth进行认证和授权的过程如下所示:

  1. 用户访问客户端的网站,想操作自己存放在服务提供方的资源。
  2. 客户端服务提供方请求一个临时令牌。
  3. 服务提供方验证客户端的身份后,授予一个临时令牌。
  4. 客户端获得临时令牌后,将用户引导至服务提供方的授权页面请求用户授权。在这个过程中将临时令牌和客户端的回调连接发送给服务提供方
  5. 用户服务提供方的网页上输入用户名和密码,然后授权该客户端访问所请求的资源。
  6. 授权成功后,服务提供方引导用户返回客户端的网页。
  7. 客户端根据临时令牌从服务提供方那里获取访问令牌 。
  8. 服务提供方根据临时令牌和用户的授权情况授予客户端访问令牌。
  9. 客户端使用获取的访问令牌访问存放在服务提供方上的受保护的资源。


测试页面main

<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="./purl.js"></script>
<script src="./sha1.js"></script>
<script src="./kuaipan.js"></script>

<script>
function doRequest(method, uri)
{
	if (method == "GET"){ // 如果是GET,直接在浏览器里面点击链接完成。
			document.write( method + "<hr>" 
				+ "<a href='" + uri + "'>" + uri + "</a>"  + "<hr>");
	}
				
	if (method == "POST"){// 如果是POST,生成一个表单,让用户提交。
		document.write("<form method='POST' enctype='multipart/form-data' action=" + uri + ">"
		+ "<input type='file' name='file_content1'/>" 
		+ "<input type='file' name='file_content2'/>"  // 从测试情况看:有多个文件,金山选择了最后一个part作为文件内容。
		+ "<input type='submit' value='post' />"
		+ "</form>"
		);
	}
	// TODO: 更酷的方式,是实现JS跨域请求。
}

// 申请应用时候的key和secret
var oauth_consumer_key = 'XXXXXXXXXXXXXXX';
var consumer_secret = '???????????????';

// 临时token和secret
var temp_oauth_token = 'XXXXXXXXXXXXXXXXXXXXXX';
var temp_oauth_token_secret = '??????????????????????????';
// 以及,校验码
var oauth_verifier = 'YYYYYYYYYYYY';

// 认证交互后得到的token和secret
var oauth_token = 'XXXXXXXXXXXXXXXXXXX';
var oauth_token_secret = '?????????????????????????????????';

</script>

<body>
<a href="javascript:requestToken(oauth_consumer_key, consumer_secret, 'http://www.a.com/kuaipan/api.html');">requestToken</a><br>
<a href="javascript:authorise(temp_oauth_token);">authorise</a><br>
<a href="javascript:accessToken(oauth_consumer_key, consumer_secret, temp_oauth_token, temp_oauth_token_secret, oauth_verifier);">accessToken</a><br>
<a href="javascript:account_info(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret);">account_info</a><br>
<a href="javascript:metadata(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, false, 'images/Desert.jpg');">metadata</a><br>
<a href="javascript:shares(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, false, 'images/Desert.jpg', 'ShareName', '123456');">shares</a><br>
<a href="javascript:create_folder(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, false, 'images/beaty2');">create_folder</a><br>
<a href="javascript:Delete(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, false, 'images/beaty/sample.jpg', false);">Delete</a><br>
<a href="javascript:move(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, false, 'images/Desert.jpg', 'images/Desert2.jpg');">move</a><br>
<a href="javascript:copy(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, false, 'images/Desert.jpg', 'images/beaty/sample.jpg');">copy</a><br>
<a href="javascript:upload_locate(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret);">upload_locate</a><br>
<a href="javascript:upload_file(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, 'http://p3.dfs.kuaipan.cn/cdlnode', false, true, '/hello.txt');">upload_file</a><br>
<a href="javascript:download_file(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, false, 'images/Desert2.jpg');">download_file</a><br>
<a href="javascript:thumbnail(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, false, 'images/Desert2.jpg', 40, 30);">thumbnail</a><br>
<a href="javascript:documentView(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, false, 'images/test.txt', 'txt', 'normal', 1);">documentView</a><br>
</body>
</html>

核心web接口

//成生签名
function createSign(consumer_secret, token_secret, url, method, params){
	var key=encode(consumer_secret||"")+'&'+encode(token_secret||"");
	var turl=[];
	turl.push(method.toUpperCase());
	turl.push(encode(url));
	turl.push(encode($.param(sortByKey(params))));
	/*Fix: 
	 * 1, Use '=' to pad base64
	 * 2, Use Upper Hex Format
	 */
	return b64_hmac_sha1(key, turl.join("&"));
}

//合并参数
function createParam(params){
	var timestamp = (new Date()).getTime();
	var tmps={
	    oauth_signature_method: "HMAC-SHA1",
	    oauth_version: "1.0",
	    oauth_timestamp: Math.round(timestamp/1000),
	    oauth_nonce: timestamp
	};
	for(var k in params){
		tmps[k]=params[k];
	}
	return tmps;
}

//参数排序
function sortByKey(paramArr){
	var keys=[];
	for(var k in paramArr){
		keys.push(k);
	}
	keys=keys.sort();
	var params={};
	for(var k in keys){
		params[keys[k]]=paramArr[keys[k]];
	}
	return params;
}

//url编码
function encode(s){
	s = encodeURIComponent(s);
    s = s.replace(/\!/g, "%21");
    s = s.replace(/\*/g, "%2A");
    s = s.replace(/\'/g, "%27");
    s = s.replace(/\(/g, "%28");
    s = s.replace(/\)/g, "%29");
    return s;
}

///
/**
 * 通用api
 * @param string apiurl  api地址
 * @param object param   其他接口特定的参数
 * @param string method  调用方式GET/POST
 */
function apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, apiurl, param, method){
	// 生成基本oauth参数表, 附加接口参数
	var params=createParam(param);
	// 添加oauth_consumer_key到oauth参数表中
	if (oauth_consumer_key!="") params["oauth_consumer_key"] = oauth_consumer_key;
	// 添加oauth_token到oauth参数表中
	if (oauth_token != "") params["oauth_token"] = oauth_token;
	
	// 生成签名
	params["oauth_signature"]=createSign(consumer_secret
			, oauth_token_secret
			, apiurl
			, method
			, params
			);
	
	// http请求
	doRequest(method, apiurl+"?"+$.param(params));
}

/**
 * 生成临时token
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string callbackurl          回调url
 * @return JSON 临时oauth_token和临时oauth_secret
 */
function requestToken(oauth_consumer_key, consumer_secret, callbackurl){
	apis(oauth_consumer_key, consumer_secret
			, "", ""
			, "https://openapi.kuaipan.cn/open/requestToken"
			, { oauth_callback : callbackurl }
			, 'GET');
}

/**
* 用户登录授权
* @param string oauth_token          临时token
* @return string 校验码
*/
function authorise(oauth_token){
	doRequest( 'GET', 'https://www.kuaipan.cn/api.php?ac=open&op=authorise&oauth_token=' + oauth_token);
}

/**
 * 生成正式token
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          临时oauth_token
 * @param string oauth_token_secret   临时oauth_secret
 * @param string oauth_verifier       校验码
 * @return JSON 正式oauth_token和正式oauth_secret
 */
function accessToken(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, oauth_verifier){
	apis(oauth_consumer_key, consumer_secret
			, oauth_token, oauth_token_secret
			, "https://openapi.kuaipan.cn/open/accessToken"
			, {	oauth_verifier: oauth_verifier}
			, 'GET');
}


/**
 * 获取用户信息
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @return JSON  用户信息
 */
function account_info(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret){
	apis(oauth_consumer_key, consumer_secret
			, oauth_token, oauth_token_secret
			,"http://openapi.kuaipan.cn/1/account_info"
			,{}
			,"GET");
}

/**
 * 获取文件信息
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param bool   isroot               根目录类型
 * @param string path                 查询目标
 * @return JSON 文件(夹)信息
 */
function metadata(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, isroot, path){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			, "http://openapi.kuaipan.cn/1/metadata/"+(isroot?"kuaipan":"app_folder") + "/" + path
			, {}
			, "GET");
}

/**
 * 获取分享链接
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param bool   isroot               根目录类型
 * @param string path                 文件路径
 * @param string name                 设定一个共享名字
 * @param string access_code          设定一个访问密码
 * @return string 共享地址
 */
function shares(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, isroot, path, name/*共享名不能有非法字符*/, access_code){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			, "http://openapi.kuaipan.cn/1/shares/" + (isroot?"kuaipan":"app_folder") + "/" + path
			, {name: name, access_code: access_code}
			, "GET");
}

/**
 * 新建文件
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param bool   isroot               根目录类型
 * @param string folder_name          文件夹名称
 */
function create_folder(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, isroot, folder_name){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			, "http://openapi.kuaipan.cn/1/fileops/create_folder"
			, {root: (isroot? "kuaipan" : "app_folder"), path: folder_name}
			, "GET");
}

/**
 * 删除文件(夹)
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param bool   isroot               根目录类型 
 * @param string path                 删除的文件(夹)名称
  */
function Delete(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, isroot, path, to_recycle){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			, "http://openapi.kuaipan.cn/1/fileops/delete"
			, {root: (isroot?"kuaipan":"app_folder"), path: path, to_recycle: to_recycle}
			,"GET");
}

/**
 * 移动文件(夹)
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param bool   isroot               根目录类型 
 * @param string src                  移动的源文件(夹)名称
 * @param string dest                 移动的目标文件(夹)名称
 */
function move(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, isroot, src, dest){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			,"http://openapi.kuaipan.cn/1/fileops/move"
			,{root: (isroot?"kuaipan":"app_folder"), from_path: src, to_path: dest}
			,"GET");
}

/**
 * 复制文件(夹)
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param bool   isroot               根目录类型 
 * @param string src                  复制的源文件(夹)名称
 * @param string dest                 复制的目标文件(夹)名称
 */
function copy(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, isroot, src, dest){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			,"http://openapi.kuaipan.cn/1/fileops/copy"
			,{root: (isroot?"kuaipan":"app_folder"), from_path: src, to_path: dest}
			,"GET");
}

/**
 * 获取上传服务器地址
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @return string 上传服务器地址url
 */
function upload_locate(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			, "http://api-content.dfs.kuaipan.cn/1/fileops/upload_locate"
			, {}
			, "GET");
}

/**
 * 生成文件上传时候的post的地址
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param string upload_url           上传服务器地址url
 * @param string isroot               根目录类型
 * @param [True/False] overwrite      是否覆盖以及存在的同名文件
 * @param string path                 上传的位置(包括文件名和扩展)
 */
function upload_file(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, upload_url, isroot, overwrite, path){
	//[Sample] http://p3.dfs.kuaipan.cn/cdlnode/
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			, upload_url + "/1/fileops/upload_file"
			, {
				root : (isroot? "kuaipan": "app_folder"),
				overwrite: (overwrite ? "True": "False"), 
				path: path,
				}
			, "POST");// 上传的数据本体,由用户通过表单提交
}

/**
 * 下载文件
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param string isroot               根目录类型
 * @param string path                 下载的位置(包括文件名和扩展)
 */
function download_file(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, isroot, path){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			, "http://api-content.dfs.kuaipan.cn/1/fileops/download_file"
			, {
				root: (isroot?"kuaipan":"app_folder"), 
				path: path
			  }
			,"GET");
}

/**
 * 获取缩略图
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param string isroot               根目录类型
 * @param string path                 图片文件的位置(包括文件名和扩展)
 * @param int    w                    缩略图的宽度
 * @param int    h                    缩略图的高度
  */
function thumbnail(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, isroot, path, w, h){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			, "http://conv.kuaipan.cn/1/fileops/thumbnail"
			, {
				root: (isroot?"kuaipan":"app_folder"), 
				path: path, 
				width: w, height: h
			  }
			,"GET");
}

/**
 * 文档转换
 * @param string oauth_consumer_key   开发者key
 * @param string consumer_secret      开发者密钥
 * @param string oauth_token          正式oauth_token
 * @param string oauth_token_secret   正式oauth_secret
 * @param string isroot               根目录类型
 * @param string path                 转档文件的位置(包括文件名和扩展)
 * @param string type                 转档文件的类型
 * @param string view                 显示设备的类型
 * @param string zip                  是否生成zip压缩的html
 */
function documentView(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret, isroot, path, type, view, zip){
	apis(oauth_consumer_key, consumer_secret, oauth_token, oauth_token_secret
			, "http://conv.kuaipan.cn/1/fileops/documentView"
			,{
				root: (isroot?"kuaipan":"app_folder"), 
				path: path, 
				type: "txt", 
				view: "normal",
				zip: 1
			}
			,"GET");
}


hash函数

/*
 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
 * in FIPS 180-1
 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for details.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 1;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = "="; /* base-64 pad character. "=" for strict RFC compliance   */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_sha1(s)    { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
function b64_sha1(s)    { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
function hex_hmac_sha1(k, d)
  { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_sha1(k, d)
  { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_sha1(k, d, e)
  { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function sha1_vm_test()
{
  return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
}

/*
 * Calculate the SHA1 of a raw string
 */
function rstr_sha1(s)
{
  return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
}

/*
 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
 */
function rstr_hmac_sha1(key, data)
{
  var bkey = rstr2binb(key);
  if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
  return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
}

/*
 * Convert a raw string to a hex string
 */
function rstr2hex(input)
{
  try { hexcase } catch(e) { hexcase=0; }
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var output = "";
  var x;
  for(var i = 0; i < input.length; i++)
  {
    x = input.charCodeAt(i);
    output += hex_tab.charAt((x >>> 4) & 0x0F)
           +  hex_tab.charAt( x        & 0x0F);
  }
  return output;
}

/*
 * Convert a raw string to a base-64 string
 */
function rstr2b64(input)
{
  try { b64pad } catch(e) { b64pad=''; }
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var output = "";
  var len = input.length;
  for(var i = 0; i < len; i += 3)
  {
    var triplet = (input.charCodeAt(i) << 16)
                | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
                | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > input.length * 8) output += b64pad;
      else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
    }
  }
  return output;
}

/*
 * Convert a raw string to an arbitrary string encoding
 */
function rstr2any(input, encoding)
{
  var divisor = encoding.length;
  var remainders = Array();
  var i, q, x, quotient;

  /* Convert to an array of 16-bit big-endian values, forming the dividend */
  var dividend = Array(Math.ceil(input.length / 2));
  for(i = 0; i < dividend.length; i++)
  {
    dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
  }

  /*
   * Repeatedly perform a long division. The binary array forms the dividend,
   * the length of the encoding is the divisor. Once computed, the quotient
   * forms the dividend for the next step. We stop when the dividend is zero.
   * All remainders are stored for later use.
   */
  while(dividend.length > 0)
  {
    quotient = Array();
    x = 0;
    for(i = 0; i < dividend.length; i++)
    {
      x = (x << 16) + dividend[i];
      q = Math.floor(x / divisor);
      x -= q * divisor;
      if(quotient.length > 0 || q > 0)
        quotient[quotient.length] = q;
    }
    remainders[remainders.length] = x;
    dividend = quotient;
  }

  /* Convert the remainders to the output string */
  var output = "";
  for(i = remainders.length - 1; i >= 0; i--)
    output += encoding.charAt(remainders[i]);

  /* Append leading zero equivalents */
  var full_length = Math.ceil(input.length * 8 /
                                    (Math.log(encoding.length) / Math.log(2)))
  for(i = output.length; i < full_length; i++)
    output = encoding[0] + output;

  return output;
}

/*
 * Encode a string as utf-8.
 * For efficiency, this assumes the input is valid utf-16.
 */
function str2rstr_utf8(input)
{
  var output = "";
  var i = -1;
  var x, y;

  while(++i < input.length)
  {
    /* Decode utf-16 surrogate pairs */
    x = input.charCodeAt(i);
    y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
    if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
    {
      x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
      i++;
    }

    /* Encode output as utf-8 */
    if(x <= 0x7F)
      output += String.fromCharCode(x);
    else if(x <= 0x7FF)
      output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0xFFFF)
      output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0x1FFFFF)
      output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
                                    0x80 | ((x >>> 12) & 0x3F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
  }
  return output;
}

/*
 * Encode a string as utf-16
 */
function str2rstr_utf16le(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode( input.charCodeAt(i)        & 0xFF,
                                  (input.charCodeAt(i) >>> 8) & 0xFF);
  return output;
}

function str2rstr_utf16be(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
                                   input.charCodeAt(i)        & 0xFF);
  return output;
}

/*
 * Convert a raw string to an array of big-endian words
 * Characters >255 have their high-byte silently ignored.
 */
function rstr2binb(input)
{
  var output = Array(input.length >> 2);
  for(var i = 0; i < output.length; i++)
    output[i] = 0;
  for(var i = 0; i < input.length * 8; i += 8)
    output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
  return output;
}

/*
 * Convert an array of big-endian words to a string
 */
function binb2rstr(input)
{
  var output = "";
  for(var i = 0; i < input.length * 32; i += 8)
    output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
  return output;
}

/*
 * Calculate the SHA-1 of an array of big-endian words, and a bit length
 */
function binb_sha1(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << (24 - len % 32);
  x[((len + 64 >> 9) << 4) + 15] = len;

  var w = Array(80);
  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;
  var e = -1009589776;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;
    var olde = e;

    for(var j = 0; j < 80; j++)
    {
      if(j < 16) w[j] = x[i + j];
      else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
      var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
      e = d;
      d = c;
      c = bit_rol(b, 30);
      b = a;
      a = t;
    }

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
    e = safe_add(e, olde);
  }
  return Array(a, b, c, d, e);

}

/*
 * Perform the appropriate triplet combination function for the current
 * iteration
 */
function sha1_ft(t, b, c, d)
{
  if(t < 20) return (b & c) | ((~b) & d);
  if(t < 40) return b ^ c ^ d;
  if(t < 60) return (b & c) | (b & d) | (c & d);
  return b ^ c ^ d;
}

/*
 * Determine the appropriate additive constant for the current iteration
 */
function sha1_kt(t)
{
  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
         (t < 60) ? -1894007588 : -899497514;
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}


url解析函数

/*
 * JQuery URL Parser plugin, v2.2.1
 * Developed and maintanined by Mark Perkins, mark@allmarkedup.com
 * Source repository: https://github.com/allmarkedup/jQuery-URL-Parser
 * Licensed under an MIT-style license. See https://github.com/allmarkedup/jQuery-URL-Parser/blob/master/LICENSE for details.
 */ 

;(function(factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD available; use anonymous module
		if ( typeof jQuery !== 'undefined' ) {
			define(['jquery'], factory);	
		} else {
			define([], factory);
		}
	} else {
		// No AMD available; mutate global vars
		if ( typeof jQuery !== 'undefined' ) {
			factory(jQuery);
		} else {
			factory();
		}
	}
})(function($, undefined) {
	
	var tag2attr = {
			a       : 'href',
			img     : 'src',
			form    : 'action',
			base    : 'href',
			script  : 'src',
			iframe  : 'src',
			link    : 'href'
		},
		
		key = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment'], // keys available to query
		
		aliases = { 'anchor' : 'fragment' }, // aliases for backwards compatability
		
		parser = {
			strict : /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,  //less intuitive, more accurate to the specs
			loose :  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
		},
		
		toString = Object.prototype.toString,
		
		isint = /^[0-9]+$/;
	
	function parseUri( url, strictMode ) {
		var str = decodeURI( url ),
		res   = parser[ strictMode || false ? 'strict' : 'loose' ].exec( str ),
		uri = { attr : {}, param : {}, seg : {} },
		i   = 14;
		
		while ( i-- ) {
			uri.attr[ key[i] ] = res[i] || '';
		}
		
		// build query and fragment parameters		
		uri.param['query'] = parseString(uri.attr['query']);
		uri.param['fragment'] = parseString(uri.attr['fragment']);
		
		// split path and fragement into segments		
		uri.seg['path'] = uri.attr.path.replace(/^\/+|\/+$/g,'').split('/');     
		uri.seg['fragment'] = uri.attr.fragment.replace(/^\/+|\/+$/g,'').split('/');
		
		// compile a 'base' domain attribute        
		uri.attr['base'] = uri.attr.host ? (uri.attr.protocol ?  uri.attr.protocol+'://'+uri.attr.host : uri.attr.host) + (uri.attr.port ? ':'+uri.attr.port : '') : '';      
		  
		return uri;
	};
	
	function getAttrName( elm ) {
		var tn = elm.tagName;
		if ( typeof tn !== 'undefined' ) return tag2attr[tn.toLowerCase()];
		return tn;
	}
	
	function promote(parent, key) {
		if (parent[key].length == 0) return parent[key] = {};
		var t = {};
		for (var i in parent[key]) t[i] = parent[key][i];
		parent[key] = t;
		return t;
	}

	function parse(parts, parent, key, val) {
		var part = parts.shift();
		if (!part) {
			if (isArray(parent[key])) {
				parent[key].push(val);
			} else if ('object' == typeof parent[key]) {
				parent[key] = val;
			} else if ('undefined' == typeof parent[key]) {
				parent[key] = val;
			} else {
				parent[key] = [parent[key], val];
			}
		} else {
			var obj = parent[key] = parent[key] || [];
			if (']' == part) {
				if (isArray(obj)) {
					if ('' != val) obj.push(val);
				} else if ('object' == typeof obj) {
					obj[keys(obj).length] = val;
				} else {
					obj = parent[key] = [parent[key], val];
				}
			} else if (~part.indexOf(']')) {
				part = part.substr(0, part.length - 1);
				if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
				parse(parts, obj, part, val);
				// key
			} else {
				if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
				parse(parts, obj, part, val);
			}
		}
	}

	function merge(parent, key, val) {
		if (~key.indexOf(']')) {
			var parts = key.split('['),
			len = parts.length,
			last = len - 1;
			parse(parts, parent, 'base', val);
		} else {
			if (!isint.test(key) && isArray(parent.base)) {
				var t = {};
				for (var k in parent.base) t[k] = parent.base[k];
				parent.base = t;
			}
			set(parent.base, key, val);
		}
		return parent;
	}

	function parseString(str) {
		return reduce(String(str).split(/&|;/), function(ret, pair) {
			try {
				pair = decodeURIComponent(pair.replace(/\+/g, ' '));
			} catch(e) {
				// ignore
			}
			var eql = pair.indexOf('='),
				brace = lastBraceInKey(pair),
				key = pair.substr(0, brace || eql),
				val = pair.substr(brace || eql, pair.length),
				val = val.substr(val.indexOf('=') + 1, val.length);

			if ('' == key) key = pair, val = '';

			return merge(ret, key, val);
		}, { base: {} }).base;
	}
	
	function set(obj, key, val) {
		var v = obj[key];
		if (undefined === v) {
			obj[key] = val;
		} else if (isArray(v)) {
			v.push(val);
		} else {
			obj[key] = [v, val];
		}
	}
	
	function lastBraceInKey(str) {
		var len = str.length,
			 brace, c;
		for (var i = 0; i < len; ++i) {
			c = str[i];
			if (']' == c) brace = false;
			if ('[' == c) brace = true;
			if ('=' == c && !brace) return i;
		}
	}
	
	function reduce(obj, accumulator){
		var i = 0,
			l = obj.length >> 0,
			curr = arguments[2];
		while (i < l) {
			if (i in obj) curr = accumulator.call(undefined, curr, obj[i], i, obj);
			++i;
		}
		return curr;
	}
	
	function isArray(vArg) {
		return Object.prototype.toString.call(vArg) === "[object Array]";
	}
	
	function keys(obj) {
		var keys = [];
		for ( prop in obj ) {
			if ( obj.hasOwnProperty(prop) ) keys.push(prop);
		}
		return keys;
	}
		
	function purl( url, strictMode ) {
		if ( arguments.length === 1 && url === true ) {
			strictMode = true;
			url = undefined;
		}
		strictMode = strictMode || false;
		url = url || window.location.toString();
	
		return {
			
			data : parseUri(url, strictMode),
			
			// get various attributes from the URI
			attr : function( attr ) {
				attr = aliases[attr] || attr;
				return typeof attr !== 'undefined' ? this.data.attr[attr] : this.data.attr;
			},
			
			// return query string parameters
			param : function( param ) {
				return typeof param !== 'undefined' ? this.data.param.query[param] : this.data.param.query;
			},
			
			// return fragment parameters
			fparam : function( param ) {
				return typeof param !== 'undefined' ? this.data.param.fragment[param] : this.data.param.fragment;
			},
			
			// return path segments
			segment : function( seg ) {
				if ( typeof seg === 'undefined' ) {
					return this.data.seg.path;
				} else {
					seg = seg < 0 ? this.data.seg.path.length + seg : seg - 1; // negative segments count from the end
					return this.data.seg.path[seg];                    
				}
			},
			
			// return fragment segments
			fsegment : function( seg ) {
				if ( typeof seg === 'undefined' ) {
					return this.data.seg.fragment;                    
				} else {
					seg = seg < 0 ? this.data.seg.fragment.length + seg : seg - 1; // negative segments count from the end
					return this.data.seg.fragment[seg];                    
				}
			}
	    	
		};
	
	};
	
	if ( typeof $ !== 'undefined' ) {
		
		$.fn.url = function( strictMode ) {
			var url = '';
			if ( this.length ) {
				url = $(this).attr( getAttrName(this[0]) ) || '';
			}    
			return purl( url, strictMode );
		};
		
		$.url = purl;
		
	} else {
		window.purl = purl;
	}

});




HTTP表单上传文件过程

html的写法一般如下

<form action="target.html" method="post"  enctype="multipart/form-data" >
  <input type="text" name="key1" value="hello"/>
  <input type="file" name="img"/>
  <input type="submit"/>
</form>

当用户点击submit的时候,浏览器开始

1,读取文件的二进制数据,添加文本描述信息在二进制数据的头部,把整个这样的数据块叫做一个Encapsulated multipart part,

这个数据块既有文本的描述信息:

Content-Disposition: form-data; name="img"; filename="Hydrangeas.jpg"\r\n
Content-Type: image/jpeg\r\n\r\n

也有二进制的文件数据:

——————————ff d8 ff e0 00 10 4a 46 49 46 00 01 02 01 00  .......JFIF.....
0100   60 00 60 00 00 ff e1 10 36 45 78 69 66 00 00 4d  `.`.....6Exif..M


2, 读取表单的其他字段,也封装成Encapsulated multipart part,

比如: key1=hello

Content-Disposition: form-data; name="key1"\r\n\r\n
0050   3d 22 6b 65 79 31 22 0d 0a 0d 0a 68 65 6c 6c 6f  ="key1"....hello

3, 把这些part用http头部提到的boundary分割开,组成一个大的

Type: multipart/form-data


分割符号是:

Boundary: "---------------------------7dd320321022c"


4, 计算整个multipart/form-data的大小(包括各个part和boundary字符串)得到content-length设置给http头部

Content-Length: 595573\r\n


5,设置http头部的content-type字段

Content-Type: multipart/form-data; boundary=---------------------------7dd320321022c\r\n

 

6,然后把http头部和multipart一起通过socket的send操作来post给server。


 

HTTP HEAD抓包

0000   50 4f 53 54 20 2f 6b 75 61 69 70 61 6e 2f 74 61  POST /kuaipan/ta
0010   72 67 65 74 2e 68 74 6d 6c 20 48 54 54 50 2f 31  rget.html HTTP/1
0020   2e 31 0d 0a 41 63 63 65 70 74 3a 20 74 65 78 74  .1..Accept: text
0030   2f 68 74 6d 6c 2c 20 61 70 70 6c 69 63 61 74 69  /html, applicati
0040   6f 6e 2f 78 68 74 6d 6c 2b 78 6d 6c 2c 20 2a 2f  on/xhtml+xml, */
0050   2a 0d 0a 52 65 66 65 72 65 72 3a 20 68 74 74 70  *..Referer: http
0060   3a 2f 2f 31 30 2e 31 32 2e 32 33 2e 34 37 2f 6b  ://10.12.23.47/k
0070   75 61 69 70 61 6e 2f 70 6f 73 74 2e 68 74 6d 6c  uaipan/post.html
0080   0d 0a 41 63 63 65 70 74 2d 4c 61 6e 67 75 61 67  ..Accept-Languag
0090   65 3a 20 7a 68 2d 43 4e 0d 0a 55 73 65 72 2d 41  e: zh-CN..User-A
00a0   67 65 6e 74 3a 20 4d 6f 7a 69 6c 6c 61 2f 35 2e  gent: Mozilla/5.
00b0   30 20 28 63 6f 6d 70 61 74 69 62 6c 65 3b 20 4d  0 (compatible; M
00c0   53 49 45 20 31 30 2e 30 3b 20 57 69 6e 64 6f 77  SIE 10.0; Window
00d0   73 20 4e 54 20 36 2e 31 3b 20 57 4f 57 36 34 3b  s NT 6.1; WOW64;
00e0   20 54 72 69 64 65 6e 74 2f 36 2e 30 29 0d 0a 43   Trident/6.0)..C
00f0   6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 6d 75 6c  ontent-Type: mul
0100   74 69 70 61 72 74 2f 66 6f 72 6d 2d 64 61 74 61  tipart/form-data
0110   3b 20 62 6f 75 6e 64 61 72 79 3d 2d 2d 2d 2d 2d  ; boundary=-----
0120   2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d  ----------------
0130   2d 2d 2d 2d 2d 2d 37 64 64 33 32 30 33 32 31 30  ------7dd3203210
0140   32 32 63 0d 0a 41 63 63 65 70 74 2d 45 6e 63 6f  22c..Accept-Enco
0150   64 69 6e 67 3a 20 67 7a 69 70 2c 20 64 65 66 6c  ding: gzip, defl
0160   61 74 65 0d 0a 48 6f 73 74 3a 20 31 30 2e 31 32  ate..Host: 10.12
0170   2e 32 33 2e 34 37 0d 0a 43 6f 6e 74 65 6e 74 2d  .23.47..Content-
0180   4c 65 6e 67 74 68 3a 20 35 39 35 35 37 33 0d 0a  Length: 595573..
0190   44 4e 54 3a 20 31 0d 0a 43 6f 6e 6e 65 63 74 69  DNT: 1..Connecti
01a0   6f 6e 3a 20 4b 65 65 70 2d 41 6c 69 76 65 0d 0a  on: Keep-Alive..
01b0   43 61 63 68 65 2d 43 6f 6e 74 72 6f 6c 3a 20 6e  Cache-Control: n
01c0   6f 2d 63 61 63 68 65 0d 0a 0d 0a                 o-cache....


表单内容部分(multipart/form-data)

0000   2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d  ----------------
0010   2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 37 64 64  -------------7dd
0020   33 32 30 33 32 31 30 32 32 63 0d 0a 43 6f 6e 74  320321022c..Cont
0030   65 6e 74 2d 44 69 73 70 6f 73 69 74 69 6f 6e 3a  ent-Disposition:
0040   20 66 6f 72 6d 2d 64 61 74 61 3b 20 6e 61 6d 65   form-data; name
0050   3d 22 6b 65 79 31 22 0d 0a 0d 0a 68 65 6c 6c 6f  ="key1"....hello
0060   0d 0a 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d  ..--------------
0070   2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 37  ---------------7
0080   64 64 33 32 30 33 32 31 30 32 32 63 0d 0a 43 6f  dd320321022c..Co
0090   6e 74 65 6e 74 2d 44 69 73 70 6f 73 69 74 69 6f  ntent-Dispositio
00a0   6e 3a 20 66 6f 72 6d 2d 64 61 74 61 3b 20 6e 61  n: form-data; na
00b0   6d 65 3d 22 69 6d 67 22 3b 20 66 69 6c 65 6e 61  me="img"; filena
00c0   6d 65 3d 22 48 79 64 72 61 6e 67 65 61 73 2e 6a  me="Hydrangeas.j
00d0   70 67 22 0d 0a 43 6f 6e 74 65 6e 74 2d 54 79 70  pg"..Content-Typ
00e0   65 3a 20 69 6d 61 67 65 2f 6a 70 65 67 0d 0a 0d  e: image/jpeg...
00f0   0a ff d8 ff e0 00 10 4a 46 49 46 00 01 02 01 00  .......JFIF.....
0100   60 00 60 00 00 ff e1 10 36 45 78 69 66 00 00 4d  `.`.....6Exif..M
此处省略很多二进制数据
91620   c7 69 b6 b0 be 8d e2 5e e2 b9 ce 38 0f d9 4a f5  .i.....^...8..J.
91630   b9 a3 b7 7b 5f ab 54 d2 6b 42 46 09 fb 7e cf f2  ...{_.T.kBF..~..
91640   fd bd 7f ff d9 0d 0a 2d 2d 2d 2d 2d 2d 2d 2d 2d  .......---------
91650   2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d  ----------------
91660   2d 2d 2d 2d 37 64 64 33 32 30 33 32 31 30 32 32  ----7dd320321022
91670   63 2d 2d 0d 0a                                   c--..

 

如果没指定enctype="multipart/form-data

截图像这样(错误:文件的二进制内容并没有被附加):


 

7, 服务器处理表单数据

 

7.1 首先http协议处理器的接收器部分(socket)读取http的head,查询content-length,然后读取到头部最后的\r\n\r\n,从这个位置开始再读取content-length指定的长度,就是post过来的数据,在这个例子中,post过来的数据是用multipart&boundary封装的表单form数据。

 

7.2 接下来http协议处理器会解析http头部的其他参数,更重要的是解析表单数据部分(根据boundary,\r\n等切分提取),然后对每一个part进行简单的封装(字典,内存对象等等)。

假如,http协议处理器(apache等)处理以后得到一下的对象:

key1=hello

img={"Hydrangeas.jpg", "image/jpeg", imagesize=595284, data={内存指针,或者一个二进制数据的内存块}}

 

7.3 最后,http协议处理器会把这些内存对象,交给上层的动态脚本引擎(asp script engin, jsp script engine, cgi, php等)。

这些上层的引擎在拿到这些对象以后,会再次进行封装,封装成web后台程序员熟悉的request对象(jsp),这个时候,jsp程序员要获取key1的值,只需要在jsp里面调用request.getParameter("key1"); 就可以到的值hello。

如果要得到上传文件,可以通过request.getParameter("img")得到一个文件对象。这个文件对象是根据这些属性创建的:

img={"Hydrangeas.jpg", "image/jpeg", imagesize=595284, data={内存指针,或者一个二进制数据的内存块}}

有了这些文件属性和内容,jsp程序员想把文件保存在tomcat服务器的某个位置应该是很容易的事情吧。


 

参考:

http://www.kuaipan.cn/developers/document_oauth.htm

http://zh.wikipedia.org/wiki/OAuth

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值