zoukankan      html  css  js  c++  java
  • .NET手记-友盟消息推送服务器端加密算法的实现

    最近为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='21321321321321'
    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 = "21321321321321",
                    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加密算法和文本编码,确认了在使用同样输入字符串的情况下输出是一致的,效果与pythonmd5.hexdigest()JavaDigestUtils.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": "u4f60u7684appkey", "timestamp": "u4f60u7684timestamp", "device_tokens": "21321321321321", "type": "unicast", "payload": {"body": {"text": "u6765u81eau53cbu76dfu63a8u9001", "after_open": "go_app", "ticker": "Hello World", "title": "u4f60u597d"}, "display_type": "notification"}}你的app_master_secret
    
    
    //c#生成的拼接字符串
    
    POSThttp://msg.umeng.com/api/send{"appkey":"你的appkey","timestamp":"你的timestamp","device_tokens":"21321321321321","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 = "21321321321321",
                    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 = 0; 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一样的结果了。

    总结

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

  • 相关阅读:
    深入浅出Win32多线程程序设计(一)
    dm642的优化
    SpringBoot2
    HZERO微服务平台09: jhipster接入hzero
    如何以纯文本方式简单快速记录java代码的调用过程
    HZERO微服务平台07: 代码分析之登录日志、验证码登录、jwt token等
    HZERO微服务平台02: 认证鉴权体系介绍
    HZERO微服务平台06: 代码分析之token生成、校验、获取信息、传递
    HZERO微服务平台10: 代码分析之admin服务刷新路由、权限、swagger的过程 .md
    HZERO微服务平台11: 代码分析之数据权限、sql拦截 .md
  • 原文地址:https://www.cnblogs.com/mantgh/p/4705152.html
Copyright © 2011-2022 走看看