zoukankan      html  css  js  c++  java
  • 微信开发 企业号(二)-- 回调模式之Tooken验证 .net/python

    在企业号开发者中心中,有加密解密源代码,供给开发者使用。(加解密库下载

    由于官方只提供了python2.*的类库,使用python3.*的朋友可以再最后下载我修改后的py文件(仅修改验证Tooken代码)。

    加解密库分析

    一、需要用到的几个数据

    在企业号中配置/获取到的数据

    string sToken = "QDG6eK";
    string sCorpID = "wx5823bf96d3bd56c7";
    string sEncodingAESKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C";

    通过URL中获取到的参数

     // string sVerifyMsgSig = Request("msg_signature");
    string sVerifyMsgSig = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";
    // string sVerifyTimeStamp = Request("timestamp");
    string sVerifyTimeStamp = "1409659589";
    // string sVerifyNonce = Request("nonce");
    string sVerifyNonce = "263014780";
    // string sVerifyEchoStr = Request("echostr");
    string sVerifyEchoStr = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";

    例如:

    http://127.0.0.1/?msg_signature=5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3&timestamp=1409659589&nonce=263014780&echostr=P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==

    二、环境搭建

    1、C#

      只需要在官网下载C#库后将CS文件复制到工程,或编译后引用DLL文件即可。

    2、Python

      需要pycrypto第三方库。

    I、linux

      只需要运行:pip install pycrypto安装即可

    II、windows

      安装比较麻烦,网上有说pip install pycrypto、easy_install pycrypto。

      由于我电脑没安装VS2008,安装的是VS2013。按照网上各种方法都无法对下载的包进行编译。

      最后找到了编译好的exe文件,直接安装即可http://www.voidspace.org.uk/python/modules.shtml#pycrypto

    三、修改地方

    1、C#

      无修改

    2、Python3.*

      删除

    reload(sys)
    
    sys.setdefaultencoding('utf-8') 
    将所有
    tryexcept Exception,e:
        print e
    
    修改为
    tryexcept Exception as e:
        print(e)
    第51行
    sha.update("".join(sortlist))
    改为
    sha.update("".join(sortlist).encode("ascii"))
    第174行
    pad = ord(plain_text[-1]) 
    改为
    pad = plain_text[-1]
    第182行
    from_corpid = content[xml_len+4:]
    改为
    from_corpid = content[xml_len+4:].decode("utf8") 

    四、分析

    1、实例化

    //C#
    Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(sToken, sEncodingAESKey, sCorpID);
    #Python
    wxcpt=WXBizMsgCrypt(sToken,sEncodingAESKey,sCorpID)

    2、调用URL验证接口

    //c#
    int ret = 0;
    string sEchoStr = "";
    ret = wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sVerifyEchoStr, ref sEchoStr);
    #Python
    ret,sEchoStr=wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp,sVerifyNonce,sVerifyEchoStr)

    3、signature验证

    I、将token, timestamp, nonce, encrypt的内容按照大小字母顺序排列

    II、按顺序将列表中排序号的内容拼接成一个字符串,并对其进行ASCII转码

    III、对ASCII转码后的数组做SHA1加密生成 signature

    IV、生成的signature和URL中获取到的sMsgSignature进行比对,如果一致则继续,否则返回错误。

    //C#
    public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt ,ref string sMsgSignature) { ArrayList AL
    = new ArrayList(); AL.Add(sToken); AL.Add(sTimeStamp); AL.Add(sNonce); AL.Add(sMsgEncrypt); AL.Sort(new DictionarySort()); string raw = ""; for (int i = 0; i < AL.Count; ++i) { raw += AL[i]; } SHA1 sha; ASCIIEncoding enc; string hash = ""; try { sha = new SHA1CryptoServiceProvider(); enc = new ASCIIEncoding(); byte[] dataToHash = enc.GetBytes(raw); byte[] dataHashed = sha.ComputeHash(dataToHash); hash = BitConverter.ToString(dataHashed).Replace("-", ""); hash = hash.ToLower(); } catch (Exception) { return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error; } sMsgSignature = hash; return 0; } public class DictionarySort : System.Collections.IComparer { public int Compare(object oLeft, object oRight) { string sLeft = oLeft as string; string sRight = oRight as string; int iLeftLength = sLeft.Length; int iRightLength = sRight.Length; int index = 0; while (index < iLeftLength && index < iRightLength) { if (sLeft[index] < sRight[index]) return -1; else if (sLeft[index] > sRight[index]) return 1; else index++; } return iLeftLength - iRightLength; } } //调用 string hash = ""; int ret = 0; ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash); if (ret != 0) return ret; if (hash == sSigture) return 0;
    #Python
      def getSHA1(self, token, timestamp, nonce, encrypt):
            """用SHA1算法生成安全签名
            @param token:  票据
            @param timestamp: 时间戳
            @param encrypt: 密文
            @param nonce: 随机字符串
            @return: 安全签名
            """
            try:
                sortlist = [token, timestamp, nonce, encrypt]
                sortlist.sort()
                sha = hashlib.sha1()
                sha.update("".join(sortlist).encode("ascii"))
                return  ierror.WXBizMsgCrypt_OK, sha.hexdigest()
            except Exception as e:
                print(e)
                return  ierror.WXBizMsgCrypt_ComputeSignature_Error, None

    4、解密

    I、在sEncodingAESKey接入加入等号(“=”)

     sEncodingAESKey = sEncodingAESKey+"="

    II、再用Base64对其进行编码

    byte[] Key;
    Key = Convert.FromBase64String(EncodingAESKey + "=");
    Key = base64.b64decode(sEncodingAESKey+"=")

    III、根据Key生成AES加密所需要的偏移量IV

    //C#
    byte
    [] Iv = new byte[16]; Array.Copy(Key, Iv, 16);
    #python
    Iv = Key[:16]

    IV、解密方法

    private static byte[] AES_decrypt(String Input, byte[] Iv, byte[] Key)
            {
                RijndaelManaged aes = new RijndaelManaged();
                aes.KeySize = 256;
                aes.BlockSize = 128;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.None;
                aes.Key = Key;
                aes.IV = Iv;
                var decrypt = aes.CreateDecryptor(aes.Key, aes.IV);
                byte[] xBuff = null;
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
                    {
                        byte[] xXml = Convert.FromBase64String(Input);
                        byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32];
                        Array.Copy(xXml, msg, xXml.Length);
                        cs.Write(xXml, 0, xXml.Length);
                    }
                    xBuff = decode2(ms.ToArray());
                }
                return xBuff;
            }
    # -*- coding: utf-8 -*-
    #
    #  Cipher/blockalgo.py 
    #
    # ===================================================================
    # The contents of this file are dedicated to the public domain.  To
    # the extent that dedication to the public domain is not available,
    # everyone is granted a worldwide, perpetual, royalty-free,
    # non-exclusive license to exercise all rights associated with the
    # contents of this file for any purpose whatsoever.
    # No rights are reserved.
    #
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
    # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
    # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    # SOFTWARE.
    # ===================================================================
    """Module with definitions common to all block ciphers."""
    
    import sys
    if sys.version_info[0] == 2 and sys.version_info[1] == 1:
        from Crypto.Util.py21compat import *
    from Crypto.Util.py3compat import *
    
    #: *Electronic Code Book (ECB)*.
    #: This is the simplest encryption mode. Each of the plaintext blocks
    #: is directly encrypted into a ciphertext block, independently of
    #: any other block. This mode exposes frequency of symbols
    #: in your plaintext. Other modes (e.g. *CBC*) should be used instead.
    #:
    #: See `NIST SP800-38A`_ , Section 6.1 .
    #:
    #: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
    MODE_ECB = 1
    
    #: *Cipher-Block Chaining (CBC)*. Each of the ciphertext blocks depends
    #: on the current and all previous plaintext blocks. An Initialization Vector
    #: (*IV*) is required.
    #:
    #: The *IV* is a data block to be transmitted to the receiver.
    #: The *IV* can be made public, but it must be authenticated by the receiver and
    #: it should be picked randomly.
    #:
    #: See `NIST SP800-38A`_ , Section 6.2 .
    #:
    #: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
    MODE_CBC = 2
    
    #: *Cipher FeedBack (CFB)*. This mode is similar to CBC, but it transforms
    #: the underlying block cipher into a stream cipher. Plaintext and ciphertext
    #: are processed in *segments* of **s** bits. The mode is therefore sometimes
    #: labelled **s**-bit CFB. An Initialization Vector (*IV*) is required.
    #:
    #: When encrypting, each ciphertext segment contributes to the encryption of
    #: the next plaintext segment.
    #:
    #: This *IV* is a data block to be transmitted to the receiver.
    #: The *IV* can be made public, but it should be picked randomly.
    #: Reusing the same *IV* for encryptions done with the same key lead to
    #: catastrophic cryptographic failures.
    #:
    #: See `NIST SP800-38A`_ , Section 6.3 .
    #:
    #: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
    MODE_CFB = 3
    
    #: This mode should not be used.
    MODE_PGP = 4
    
    #: *Output FeedBack (OFB)*. This mode is very similar to CBC, but it
    #: transforms the underlying block cipher into a stream cipher.
    #: The keystream is the iterated block encryption of an Initialization Vector (*IV*).
    #:
    #: The *IV* is a data block to be transmitted to the receiver.
    #: The *IV* can be made public, but it should be picked randomly.
    #:
    #: Reusing the same *IV* for encryptions done with the same key lead to
    #: catastrophic cryptograhic failures.
    #:
    #: See `NIST SP800-38A`_ , Section 6.4 .
    #:
    #: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
    MODE_OFB = 5
    
    #: *CounTeR (CTR)*. This mode is very similar to ECB, in that
    #: encryption of one block is done independently of all other blocks.
    #: Unlike ECB, the block *position* contributes to the encryption and no
    #: information leaks about symbol frequency.
    #:
    #: Each message block is associated to a *counter* which must be unique
    #: across all messages that get encrypted with the same key (not just within
    #: the same message). The counter is as big as the block size.
    #:
    #: Counters can be generated in several ways. The most straightword one is
    #: to choose an *initial counter block* (which can be made public, similarly
    #: to the *IV* for the other modes) and increment its lowest **m** bits by
    #: one (modulo *2^m*) for each block. In most cases, **m** is chosen to be half
    #: the block size.
    #: 
    #: Reusing the same *initial counter block* for encryptions done with the same
    #: key lead to catastrophic cryptograhic failures.
    #:
    #: See `NIST SP800-38A`_ , Section 6.5 (for the mode) and Appendix B (for how
    #: to manage the *initial counter block*).
    #:
    #: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
    MODE_CTR = 6
    
    #: OpenPGP. This mode is a variant of CFB, and it is only used in PGP and OpenPGP_ applications.
    #: An Initialization Vector (*IV*) is required.
    #: 
    #: Unlike CFB, the IV is not transmitted to the receiver. Instead, the *encrypted* IV is.
    #: The IV is a random data block. Two of its bytes are duplicated to act as a checksum
    #: for the correctness of the key. The encrypted IV is therefore 2 bytes longer than
    #: the clean IV.
    #:
    #: .. _OpenPGP: http://tools.ietf.org/html/rfc4880
    MODE_OPENPGP = 7
    
    def _getParameter(name, index, args, kwargs, default=None):
        """Find a parameter in tuple and dictionary arguments a function receives"""
        param = kwargs.get(name)
        if len(args)>index:
            if param:
                raise ValueError("Parameter '%s' is specified twice" % name)
            param = args[index]
        return param or default
        
    class BlockAlgo:
        """Class modelling an abstract block cipher."""
    
        def __init__(self, factory, key, *args, **kwargs):
            self.mode = _getParameter('mode', 0, args, kwargs, default=MODE_ECB)
            self.block_size = factory.block_size
            
            if self.mode != MODE_OPENPGP:
                self._cipher = factory.new(key, *args, **kwargs)
                self.IV = self._cipher.IV
            else:
                # OPENPGP mode. For details, see 13.9 in RCC4880.
                #
                # A few members are specifically created for this mode:
                #  - _encrypted_iv, set in this constructor
                #  - _done_first_block, set to True after the first encryption
                #  - _done_last_block, set to True after a partial block is processed
                
                self._done_first_block = False
                self._done_last_block = False
                self.IV = _getParameter('iv', 1, args, kwargs)
                if not self.IV:
                    raise ValueError("MODE_OPENPGP requires an IV")
                
                # Instantiate a temporary cipher to process the IV
                IV_cipher = factory.new(key, MODE_CFB,
                        b('x00')*self.block_size,      # IV for CFB
                        segment_size=self.block_size*8)
               
                # The cipher will be used for...
                if len(self.IV) == self.block_size:
                    # ... encryption
                    self._encrypted_IV = IV_cipher.encrypt(
                        self.IV + self.IV[-2:] +        # Plaintext
                        b('x00')*(self.block_size-2)   # Padding
                        )[:self.block_size+2]
                elif len(self.IV) == self.block_size+2:
                    # ... decryption
                    self._encrypted_IV = self.IV
                    self.IV = IV_cipher.decrypt(self.IV +   # Ciphertext
                        b('x00')*(self.block_size-2)       # Padding
                        )[:self.block_size+2]
                    if self.IV[-2:] != self.IV[-4:-2]:
                        raise ValueError("Failed integrity check for OPENPGP IV")
                    self.IV = self.IV[:-2]
                else:
                    raise ValueError("Length of IV must be %d or %d bytes for MODE_OPENPGP"
                        % (self.block_size, self.block_size+2))
    
                # Instantiate the cipher for the real PGP data
                self._cipher = factory.new(key, MODE_CFB,
                    self._encrypted_IV[-self.block_size:],
                    segment_size=self.block_size*8)
    
        def encrypt(self, plaintext):
            """Encrypt data with the key and the parameters set at initialization.
            
            The cipher object is stateful; encryption of a long block
            of data can be broken up in two or more calls to `encrypt()`.
            That is, the statement:
                
                >>> c.encrypt(a) + c.encrypt(b)
    
            is always equivalent to:
    
                 >>> c.encrypt(a+b)
    
            That also means that you cannot reuse an object for encrypting
            or decrypting other data with the same key.
    
            This function does not perform any padding.
           
             - For `MODE_ECB`, `MODE_CBC`, and `MODE_OFB`, *plaintext* length
               (in bytes) must be a multiple of *block_size*.
    
             - For `MODE_CFB`, *plaintext* length (in bytes) must be a multiple
               of *segment_size*/8.
    
             - For `MODE_CTR`, *plaintext* can be of any length.
    
             - For `MODE_OPENPGP`, *plaintext* must be a multiple of *block_size*,
               unless it is the last chunk of the message.
    
            :Parameters:
              plaintext : byte string
                The piece of data to encrypt.
            :Return:
                the encrypted data, as a byte string. It is as long as
                *plaintext* with one exception: when encrypting the first message
                chunk with `MODE_OPENPGP`, the encypted IV is prepended to the
                returned ciphertext.
            """
    
            if self.mode == MODE_OPENPGP:
                padding_length = (self.block_size - len(plaintext) % self.block_size) % self.block_size
                if padding_length>0:
                    # CFB mode requires ciphertext to have length multiple of block size,
                    # but PGP mode allows the last block to be shorter
                    if self._done_last_block:
                        raise ValueError("Only the last chunk is allowed to have length not multiple of %d bytes",
                            self.block_size)
                    self._done_last_block = True
                    padded = plaintext + b('x00')*padding_length
                    res = self._cipher.encrypt(padded)[:len(plaintext)]
                else:
                    res = self._cipher.encrypt(plaintext)
                if not self._done_first_block:
                    res = self._encrypted_IV + res
                    self._done_first_block = True
                return res
    
            return self._cipher.encrypt(plaintext)
    
        def decrypt(self, ciphertext):
            """Decrypt data with the key and the parameters set at initialization.
            
            The cipher object is stateful; decryption of a long block
            of data can be broken up in two or more calls to `decrypt()`.
            That is, the statement:
                
                >>> c.decrypt(a) + c.decrypt(b)
    
            is always equivalent to:
    
                 >>> c.decrypt(a+b)
    
            That also means that you cannot reuse an object for encrypting
            or decrypting other data with the same key.
    
            This function does not perform any padding.
           
             - For `MODE_ECB`, `MODE_CBC`, and `MODE_OFB`, *ciphertext* length
               (in bytes) must be a multiple of *block_size*.
    
             - For `MODE_CFB`, *ciphertext* length (in bytes) must be a multiple
               of *segment_size*/8.
    
             - For `MODE_CTR`, *ciphertext* can be of any length.
    
             - For `MODE_OPENPGP`, *plaintext* must be a multiple of *block_size*,
               unless it is the last chunk of the message.
    
            :Parameters:
              ciphertext : byte string
                The piece of data to decrypt.
            :Return: the decrypted data (byte string, as long as *ciphertext*).
            """
            if self.mode == MODE_OPENPGP:
                padding_length = (self.block_size - len(ciphertext) % self.block_size) % self.block_size
                if padding_length>0:
                    # CFB mode requires ciphertext to have length multiple of block size,
                    # but PGP mode allows the last block to be shorter
                    if self._done_last_block:
                        raise ValueError("Only the last chunk is allowed to have length not multiple of %d bytes",
                            self.block_size)
                    self._done_last_block = True
                    padded = ciphertext + b('x00')*padding_length
                    res = self._cipher.decrypt(padded)[:len(ciphertext)]
                else:
                    res = self._cipher.decrypt(ciphertext)
                return res
    
            return self._cipher.decrypt(ciphertext)
    Python AES 解密,已存在与blockalgo.py文件中

    V、AES解密,并对解密后的数据进行拆分

    //c#
    byte[] btmpMsg = AES_decrypt(Input, Iv, Key); //调用解密方法解密
    
    //返回由字节数组中指定位置的四个字节转换来的 32 位有符号整数
    int len = BitConverter.ToInt32(btmpMsg, 16);
    //将数字由网络字节顺序转换为主机字节顺序。
    len = IPAddress.NetworkToHostOrder(len);
    
    byte[] bMsg = new byte[len];
    byte[] bCorpid = new byte[btmpMsg.Length - 20 - len];
    Array.Copy(btmpMsg, 20, bMsg, 0, len);
    Array.Copy(btmpMsg, 20+len , bCorpid, 0, btmpMsg.Length - 20 - len);
    string oriMsg = Encoding.UTF8.GetString(bMsg);
    corpid = Encoding.UTF8.GetString(bCorpid); //用来和m_sCorpID验证,解密是否正确
            try:
                pad = plain_text[-1]
                # 去除16位随机字符串
                content = plain_text[16:-pad]
                #struct.unpack("I",content[ : 4])[0]    返回由字节数组中指定位置的四个字节转换来的 32 位有符号整数
                #socket.ntohl 将数字由网络字节顺序转换为主机字节顺序。
                xml_len = socket.ntohl(struct.unpack("I",content[ : 4])[0])
                xml_content = content[4 : xml_len+4]
                from_corpid = content[xml_len+4:].decode("utf8")
                print(from_corpid)
            except Exception as e:
                print(e)
                return  ierror.WXBizMsgCrypt_IllegalBuffer,None    

    5、调用解密方法,返回明文及cpid

    //C#
    sReplyEchoStr = Cryptography.AES_decrypt(sEchoStr, m_sEncodingAESKey, ref cpid);
    #python
    pc = Prpcrypt(self.key)
    ret,sReplyEchoStr = pc.decrypt(sEchoStr,self.m_sCorpid)

    6、解密后需要比对m_sCorpID和cpid是否一致

    五、疑问

    由于技术有限,并没做过太多Socket编程,并不很了解如下内容

    如有高手路过,请指点一下,谢谢。

    //返回由字节数组中指定位置的四个字节转换来的 32 位有符号整数
    int len = BitConverter.ToInt32(btmpMsg, 16);
    //将数字由网络字节顺序转换为主机字节顺序。
    len = IPAddress.NetworkToHostOrder(len);
    xml_len = socket.ntohl(struct.unpack("I",content[ : 4])[0])

    六、Python3.*版本WXBizMsgCrypt文件下载

    修改后Python3.* 对应WXBizMsgCrypt.2014.09.28.zip文件。

    该版本仅使用本文中修改方法进行修改,并且仅测试过URL验证方法,其他方法暂时可能存在问题,后续慢慢完善。

    如有高手愿意提供WXBizMsgCrypt.py,请直接留言,谢谢。

    七、Python3.*版本 测试代码

    from http.server import BaseHTTPRequestHandler
    from http.server import HTTPServer
    from socketserver import ThreadingMixIn
    import urllib.parse
    from WXBizMsgCrypt import WXBizMsgCrypt
    import xml.etree.cElementTree as ET
    
     
    hostIP = ''
    portNum = 8080
    
    serverMessage = "msg_signature"
    
    sToken="QDG6eK"
    sEncodingAESKey="jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C"
    sCorpID="wx5823bf96d3bd56c7"
    
    class mySoapServer( BaseHTTPRequestHandler ):
        def do_head( self ):
            pass
       
        def do_GET( self ):
            try:           
                if self.path.find(serverMessage) == -1:
                    self.send_error( 404, message = None )
                    return
                
                #解析请求
                query=GetQuery(self.path)
    
                sVerifyMsgSig=query["msg_signature"][0]
                sVerifyTimeStamp=query["timestamp"][0]
                sVerifyNonce=query["nonce"][0]
                sVerifyEchoStr=query["echostr"][0] 
    
                wxcpt=WXBizMsgCrypt(sToken,sEncodingAESKey,sCorpID)
                ret,sEchoStr=wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp,sVerifyNonce,sVerifyEchoStr)
               
                self.send_response( 200, message = None )
                self.send_header( 'Content-type', 'text/html' )
                self.end_headers()            
    
                if ret == 0:                
                    res=sEchoStr.decode("utf8")
                else:
                    res = "%s" % ret
    
                self.wfile.write( res.encode( encoding = 'utf_8', errors = 'strict' ) )
            except IOError:
                self.send_error( 404, message = None )
       
    def GetQuery(str):
        params = str[str.index("?")+1:]
        
        parsed_result = {}
    
        list = [param for param in params.split('&')]
    
        for item in list:
            if item.find("=") > -1:
                name = item[:item.index("=")]
                value = urllib.parse.unquote(item[item.index("=")+1:])
            else:
                name = item
                value = ""
    
            if name in parsed_result:
                parsed_result[name].append(value)
            else:
                parsed_result[name] = [value]     
    
        return parsed_result  #urllib.parse.parse_qs(temp)
      
    
    class ThreadingHttpServer( ThreadingMixIn, HTTPServer ):
        pass
         
    myServer = ThreadingHttpServer( ( hostIP, portNum ), mySoapServer )
    print("Server Started ....")
    myServer.serve_forever()
    myServer.server_close()
  • 相关阅读:
    MVC项目发布IIS CSS无法加载
    加班理由总结
    查找SQL Server 自增ID值不连续记录
    MySQL按时间查找
    近期任务
    AngularJs使用过程中,在ng-repeat中使用track by
    Java保留字和关键字
    抽象类和接口的区别
    Java标识符
    Java方法重载
  • 原文地址:https://www.cnblogs.com/comsokey/p/enterprise_CheckToken.html
Copyright © 2011-2022 走看看