最近为App开发消息推送功能,这里我们采用了友盟的消息推送服务,但其后台简陋,可定制化程度低,所以决定接入服务器端API,在自己的服务器上部署一套推送服务。

其中涉及到很多问题,首先要解决的就是与友盟服务器的加密验证问题。

官方示例

友盟官方的加密算法使用Python实现的,核心是MD5算法,如下:

import hashlib
import json
def md5(s):
print s
m = hashlib.md5(s)
return m.hexdigest() appkey = '你的appkey'
app_master_secret = '你的app_master_secret'
timestamp = '你的timestamp'
device_token=''
method = 'POST'
url = 'http://msg.umeng.com/api/send'
params = {'appkey': appkey,
'timestamp': timestamp,
'device_tokens': device_token,
'type': 'unicast',
'payload': {'body': {'ticker': 'Hello World',
'title':'你好',
'text':'来自友盟推送',
'after_open': 'go_app'},
'display_type': 'notification'
}
}
post_body = json.dumps(params)
print post_body
sign = md5('%s%s%s%s' % (method,url,post_body,app_master_secret))

Python程序输出结果为:

561128496d0f7a2acc098eb0ac263bd3

问题描述

简单一看发现很简单,于是使用C#实现了对应的一套算法,然而实际运行结果却大相径庭,让人困惑,如下:

private string GetSign()
{
var param = new
{
appkey = "你的appkey",
timestamp = "你的timestamp",
device_tokens = "",
type = "unicast",
payload = new
{
body = new
{
ticker = "Hello World",
title = "你好",
text = "来自友盟推送",
after_open = "go_app" },
display_type = "notification"
}
};
var app_master_secret = "你的app_master_secret";
var method = "POST";
var url = "http://msg.umeng.com/api/send";
var serializer = JsonSerializer.Create();
var jsonWriter = new System.IO.StringWriter();
serializer.Serialize(jsonWriter, param);
var post_body = jsonWriter.ToString();
var str = method + url + post_body + app_master_secret;
var sign = MD5(str);
return sign;
} private string MD5(string s)
{ var x = new MD5CryptoServiceProvider();
byte[] bs = System.Text.Encoding.UTF8.GetBytes(s);
bs = x.ComputeHash(bs);
var str = new System.Text.StringBuilder();
foreach (byte b in bs)
{
str.Append(b.ToString("x2").ToLower());
}
return str.ToString();
}

运行之后我们发现输出结果为:

244ba68e49004f7b4ccdaf9bae617296

显然这两个结果是不对的,是无法通过友盟服务器验证的,那到底是什么问题导致的呢?

问题分析

首先我们检查了自己的MD5加密算法和文本编码,确认了在使用同样输入字符串的情况下输出是一致的,效果与python中md5.hexdigest()、Java中DigestUtils.md5Hex(String)函数输出的结果是一致的,那么可以先确定我们的MD5算法是对的,如下:

private string MD5(string s)
{ var x = new MD5CryptoServiceProvider();
var bs = System.Text.Encoding.UTF8.GetBytes(s);
bs = x.ComputeHash(bs);
var str = new System.Text.StringBuilder();
foreach (byte b in bs)
{
str.Append(b.ToString("x2").ToLower());
}
return str.ToString();
}

那么剩余的问题就是,是否是我们的输入字符串有问题?于是我分别输出拼接出的输入字符串做了对比,如下:

//Python生成的拼接字符串

POSThttp://msg.umeng.com/api/send{"appkey": "\u4f60\u7684appkey", "timestamp": "\u4f60\u7684timestamp", "device_tokens": "", "type": "unicast", "payload": {"body": {"text": "\u6765\u81ea\u53cb\u76df\u63a8\u9001", "after_open": "go_app", "ticker": "Hello World", "title": "\u4f60\u597d"}, "display_type": "notification"}}你的app_master_secret

//c#生成的拼接字符串

POSThttp://msg.umeng.com/api/send{"appkey":"你的appkey","timestamp":"你的timestamp","device_tokens":"","type":"unicast","payload":{"body":{"ticker":"Hello World","title":"你好","text":"来自友盟推送","after_open":"go_app"},"display_type":"notification"}}你的app_master_secret

仔细对比之后,发现了3点区别:

1.Python生成的字符串post_body部分采用了Unicode编码,而c#则是默认编码

2.Python生成的字符串post_body部分“:”和“,”之后都有空格,格式与c#采用json序列化后的字符串格式不一致

3.Python生成的字符串post_body部分键值对的顺序与c#生成的不一致

问题解决

首先我们使用c#将字符串中post_body部分转成Unicode编码,再将相应键值的位置调整为和Python生成的一致,最后再将缺失的空格添加今日字符串中,完整的程序如下:

 private string GetSign()
{
var param = new
{
appkey = "你的appkey",
timestamp = "你的timestamp",
device_tokens = "",
type = "unicast",
payload = new
{
body = new
{
text = "来自友盟推送",
after_open = "go_app",
ticker = "Hello World",
title = "你好"
},
display_type = "notification"
}
};
var app_master_secret = "你的app_master_secret";
var method = "POST";
var url = "http://msg.umeng.com/api/send";
var serializer = JsonSerializer.Create();
var jsonWriter = new System.IO.StringWriter();
serializer.Serialize(jsonWriter, param);
var post_body = jsonWriter.ToString();
post_body = unicode_js_0(post_body).Replace(":",": ").Replace(",",", ");
var str = method + url + post_body + app_master_secret;
var sign = MD5(str);
return sign;
}
/// <summary>
/// MD5加密
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
private string MD5(string s)
{ var x = new MD5CryptoServiceProvider();
var bs = System.Text.Encoding.UTF8.GetBytes(s);
bs = x.ComputeHash(bs);
var str = new System.Text.StringBuilder();
foreach (byte b in bs)
{
str.Append(b.ToString("x2").ToLower());
}
return str.ToString();
}
/// <summary>
/// 中文转unicode(符合js规则的)
/// </summary>
/// <returns></returns>
public string unicode_js_0(string str)
{
var outStr = string.Empty;
if (!string.IsNullOrEmpty(str))
{
for (int i = ; i < str.Length; i++)
{
if (Regex.IsMatch(str[i].ToString(), @"[\u4e00-\u9fa5]")) { outStr += "\\u" + ((int)str[i]).ToString("x"); }
else { outStr += str[i]; }
}
}
return outStr;
}

这样就能使得c#程序输出和官方python demo一样的结果了。

总结

下面会进一步的进行与友盟消息推送服务的集成,进一步验证此方案的有效性,若有较好方案,欢迎指教,不甚感激

.NET手记-友盟消息推送服务器端加密算法的实现的更多相关文章

  1. 友盟消息推送api、python sdk问题、测试demo代码

    一,友盟消息推送python服务端sdk地址和文档地址 1.sdk地址:http://dev.umeng.com/system/resources/W1siZiIsIjIwMTYvMDgvMTkvMT ...

  2. 为友盟消息推送开发的PHP SDK(composer版):可以按省发Android push

    一直以来APP希望按省市县推送Android push,只能自己分析用户经纬度,打tag发送. 现在终于有服务商提供了. 友盟消息推送 可以“按省推送”,很方便. 我为友盟做了PHP SDK(comp ...

  3. umeng友盟消息推送功能集成

    umeng友盟消息推送功能集成(本人使用的是eclipse开发) 1.首先请自行观看友盟消息推送集成的API文档. 观看地址如下: http://dev.umeng.com/push/android/ ...

  4. 【转载自友盟消息推送iOS文档】在appDelegate中注册推送

    1.2   基本功能集成指南 提示 请先在友盟的消息推送管理后台中创建App,获得AppKey和AppSecret 导入SDK 下载 UMessage_Sdk_All_x.x.x.zip并解压缩 导入 ...

  5. 友盟消息推送UPush

    第一步:把下载的SDK里面的PushSDK当做Module导入自己的项目 第二步:在自己项目的build.gradle里面一定要配置applicationId defaultConfig { appl ...

  6. 友盟消息推送和更新XML配置

    <receiver android:name="com.umeng.message.NotificationProxyBroadcastReceiver" android:e ...

  7. 友盟iOS推送配置(从真机调试到推送)

    下面我来讲解一下友盟iOS的推送配置,其实友盟只是一个示例,换做其余的第三方推送服务也会适用,只是第三方的后面服务变了而已. iOS推送(包括真机调试)所需要的步骤和文件如下: 备注:这里我将省略掉一 ...

  8. atitit.极光消息推送服务器端开发实现推送  jpush v3. 总结o7p

    atitit.极光消息推送服务器端开发实现推送  jpush v3. 总结o7p 1. 推送所设计到底功能1 1.1. 内容压缩1 1.2. 多引擎1 2. reg  ,设置appkey and pw ...

  9. 使用PoolingHttpClientConnectionManager解决友盟(umeng)推送在多线程环境推送失败的问题

    在友盟(umeng)提供的服务端推送的sdk中,使用的是apache提供的httpclient.在单线程化境下,httpclient工作没有问题.但是由于umeng的sdk中并未考虑并发的情况,因此很 ...

随机推荐

  1. JS获取URL中文参数乱码的解决方法

    浏览器URL参数值中带有汉字字符,在接收时直接获取会出现乱码,下面是解决方法(传递前不需要encodeURI): function getUrlVars() { var vars = [], hash ...

  2. js 一些兼容检测

    1. IE5.0之前不支持 document.getElementById(),但存在 document.all[] function getElementById(id) { if(document ...

  3. pip 国内源 配置

    pip 国内源 配置 2017年12月09日 16:05:20 阅读数:183 最近使用 pip 安装包,动辄十几 k 甚至几 k 的下载速度,确实让人安装的时候心情十分不好.所以还是要给 pip 换 ...

  4. eclipse中将一个项目作为library导入另一个项目中

    1. github上搜索viewpagerIndicator: https://github.com/JakeWharton/ViewPagerIndicator2. 下载zip包,解压,eclips ...

  5. spring mvc controller中的参数验证机制(二)

    这里我们介绍以下自定义的校验器的简单的使用示例 一.包结构和主要文件 二.代码 1.自定义注解文件MyConstraint package com.knyel.validator; import ja ...

  6. java基础面试(上)

    面向对象的特征 答:抽象.继承.封装.多态 short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗? 答:对于short s1 = 1; s1 ...

  7. ping内网一台虚拟机延时很大(hyper-v虚拟机)的解决办法

    问题现象: ping 内网一台虚拟机延时很大,不稳定,造成业务系统响应慢.查看服务器上各种资源都正常. 解决办法: 在物理机上找到和hyper-v绑定的那个网卡,把“虚拟机队列”禁用掉就好了,如下图: ...

  8. [SoapUI] 如何同时调用Global Script Library(放在SoapUI安装目录)和项目特有的Script Libary(放在项目代码下)

    SoapUI 支持引入多个package: Global Script library : 在SoapUI工具File->Preference中设置Project Script Library: ...

  9. php上传多张图片

    第一种:加后缀 代码实现(就是普通的上传图片,只是在外面加个foreach循环) $allow_file_types = '|GIF|JPG|PNG|BMP|SWF|DOC|XLS|PPT|MID|W ...

  10. 利用iptables防止ssh暴力破解和控制网速

    iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --setiptables -I INPUT -p ...