百度云推送javasdk诞生记之签名算法实现

        在实现之初,就遇到一个麻烦,因为每次调用服务器端都要进行验证,所以设计了一个签名算法,官方的文档如下:

      

签名算法

云推送服务API使用的签名算法如下:

  • 获取请求的http method
  • 获取请求的url,包括host和sheme,但不包括query_string的部分
  • 将所有参数(包括GET或POST的参数,但不包含签名字段)格式化为“key=value”格式,如“k1=v1”、“k2=v2”、“k3=v3”;
  • 将格式化好的参数键值对以字典序升序排列后,拼接在一起,如“k1=v1k2=v2k3=v3”,并将http method和url按顺序拼接在这个字符串前面;
  • 在拼接好的字符串末尾追加上应用的secret_key,并进行urlencode形成base_string;
  • 上述字符串的MD5值即为签名的值:
sign=MD5(urlencode($http_method$url$k1=$v1$k2=$v2$k3=$v3$secret_key)); 
说明:
$secret_key:通过“开发者中心 -> 管理中心 -> 点击某应用 -> 应用信息详情页” 获得。

举例:

url [POST]: 

http://{domain}/rest/2.0/channel/channel?method=token&timestamp=1313293563&expires=1313293565&v=1

   

 

         从官方文档来看,有几点是很重要的

  1.  http method参数是必须的,从rest api中发现,目前也只支持get和post
  2. host和scheme参数也是必须的,官方文档中说明支持https和http两种
  3. 签名字段不参加签名字段的生成
  4. 其他所有的参数按照key=value的方式拼成一个字符串
  5. 字符串末尾加上sk的值
  6. 将前面的字符串使用urlencode进行编码
  7. 将urlencode编码后的字符串生成md5值

       这个里面其他的地方都是没什么的,关键是参数的顺序,我们知道,客户端给服务器端提交参数的时候是没有顺序的,当时我在实现的时候发现,有一些方法的签名是对的,有一些被服务器端当成错误的签名了,琢磨不透参数的顺序是怎么样的,最后反复研究官方文档发现,参数的顺序是按照参数的字母顺序排列的。发现了这个,其他的都很简单了,直接上代码。

 

         

package com.baidu.push.auth;

/**
 * AK和SK是应用访问资源或服务的唯一凭证。 查看AK和SK信息
 * @see <a href="http://developer.baidu.com/wiki/index.php?title=docs/cplat/push/guide">官方文档</a>
 * 
 * @author liyazhou@baidu.com
 *
 */
public class PushCredentials {

	private String accessKey;
	private String secretKey;

	public PushCredentials(String accessKey, String secretKey) {
		this.accessKey = accessKey;
		this.secretKey = secretKey;
	}

	public String getAccessKey() {
		return this.accessKey;
	}

	public String getSecretKey() {
		return this.secretKey;
	}
}

 PushCredentials这个里面是对ak和sk的一个简单封装,没有什么。

 

package com.baidu.push.auth;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.baidu.push.http.PushHttpRequest;
import com.baidu.push.reqeust.BaiduPushRequest;
import com.baidu.push.util.Constants;
import com.baidu.push.util.PushClientException;

/**
 * 生成签名算法
 * 
 * @see <a href="http://developer.baidu.com/wiki/index.php?title=docs/cplat/push/api">官方文档</a>
 * 
 * @author liyazhou@baidu.com
 *
 */
public class PushSigner {
	private static final Log log = LogFactory.getLog(PushSigner.class);
	
	private static String urlencode(String str) throws UnsupportedEncodingException {
		String rc = URLEncoder.encode(str, "utf-8");
		return rc.replace("*", "%2A");
	}
	
	public static void sign(BaiduPushRequest pushRequest,PushHttpRequest httpRequest, PushCredentials credentials) {
		StringBuilder signContents = new StringBuilder();
		// generate sign content
		if (null == pushRequest.getHttpMethod()) {
			throw new PushClientException("Sign failed! Param: httpMethod can not be empty!");
		}
		
		signContents.append(pushRequest.getHttpMethod().toString());
		if(pushRequest.isNeedSsl()){
			signContents.append(Constants.HTTPS);
		} else {
			signContents.append(Constants.HTTP);
		}
		signContents.append(Constants.PUSH_URL);
		
		if(pushRequest.getChannelId() == null){
			signContents.append(Constants.CHANNEL);
		} else {
			signContents.append(pushRequest.getChannelId());
		}
		Map<String,String> sinParameters = httpRequest.getParameters();
		for(Map.Entry<String, String> i : sinParameters.entrySet()) {
			signContents.append(i.getKey());
			signContents.append('=');
			signContents.append(i.getValue());
		}
		signContents.append(credentials.getSecretKey());
		log.info("signContents:"+signContents);
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.reset();
			//md.update( URLEncoder.encode(sb.toString(), "utf-8").getBytes() );
			md.update(urlencode(signContents.toString()).getBytes() );
			byte[] md5  = md.digest();
			
			signContents.setLength(0);
			for(byte b : md5) {
				signContents.append( String.format("%02x", b & 0xff));
			}
			
			httpRequest.addParameter("sign", signContents.toString());
		} catch (NoSuchAlgorithmException e) {
			log.error("error:"+e);
			throw new PushClientException("Sign failed! reason:"+e.getMessage());
		} catch (UnsupportedEncodingException e) {
			log.error("error:"+e);
			throw new PushClientException("Sign failed! reason:"+e.getMessage());
		}
	}
	
}

    

      PushSigner 这个类也很好理解,如果明白了签名算法,这个就是小case了,把这个留给广大同仁自己琢磨吧。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值