zoukankan      html  css  js  c++  java
  • 3.腾讯微博Android客户端开发——算法、编码、辅助方法编写 .

    在腾讯微博API OAuth认证介绍中,我们可以看到关于请求签名的介绍(http://open.t.qq.com/resource.php?i=1,2#tag0):所有TOKEN请求和受保护的资源请求必须被签名,微博开放平台会根据签名来判断请求的合法性。签名算法使用Signature Base String和密钥(Secret)生成签名,参数oauth_signature用于指定签名。这几句话对oauth_signature产生过程介绍的比较简单,通过阅读其它的资料,我们可知在oauth_signature生成值的过程中我们需要进行URL编码,使用HMAC-SHA1加密算法进行签名,最后进行Base64编码:

    上图显示我们需要URL编码方法。有过Java网络编程或者Web开发的朋友应该对中文乱码问题不会很陌生,有一种解决方法是对中文进行编码,也就是调用URLEncoder.encode(s, enc),在这里我们是否也可以使用这个方法呢?通过阅读Oauth提供的帮助文档(http://tools.ietf.org/html/draft-hammer-oauth-10#section-3.6 )我们可以得知OAuth中需要的Encode()方法与URLEncoder.encode(s, enc)存在差异:OAuth中需要把“+”和“*”这两个字符也使用“%XX”表示,而“~”不需要使用“%XX”表示,修改后的Encode()方法如下: 

    1. public static String encode(String s)  
    2.     {  
    3.         if (s == null)  
    4.         {  
    5.             return "";  
    6.         }  
    7.         String encoded = "";  
    8.         try  
    9.         {  
    10.             encoded=URLEncoder.encode(s, ENCODING);  
    11.         } catch (UnsupportedEncodingException e)  
    12.         {  
    13.             throw new RuntimeException(e.getMessage(), e);  
    14.         }  
    15.         StringBuilder sBuilder =new StringBuilder();  
    16.         for(int i=0;i<encoded.length();i++)  
    17.         {  
    18.             char c = encoded.charAt(i);  
    19.             if (c == '+')  
    20.             {  
    21.                 sBuilder.append("%20");  
    22.             }  
    23.             else if (c == '*')  
    24.             {  
    25.                 sBuilder.append("%2A");  
    26.             }  
    27.             //URLEncoder.encode()会把“~”使用“%7E”表示,因此在这里我们需要变成“~”   
    28.             else if ((c == '%')&& ((i + 1) < encoded.length())&&((i + 2) < encoded.length())&  
    29.                      (encoded.charAt(i + 1) == '7')&&(encoded.charAt(i + 2) == 'E'))   
    30.             {  
    31.                 sBuilder.append("~");  
    32.                 i+=2;  
    33.             }  
    34.             else  
    35.             {  
    36.                 sBuilder.append(c);  
    37.             }  
    38.         }  
    39.         return sBuilder.toString();  
    40.     }  

    Encode()方法编写完毕后,我们需要编写“HmacSHA1”签名算法,由于我对算法没有任何知识,所以不知道怎么写这个算法,这个使用我们就需要借助百度或者谷歌进行搜索,当然我们还可以参考OAuth官网给我们提供的参开代码,寻找过程比较繁琐,这个在视频中给大家演示。HmacSHA1签名算法如下:

    1. package com.szy.weibo.oauth;  
    2.   
    3. import java.io.UnsupportedEncodingException;  
    4. import java.security.InvalidKeyException;  
    5. import java.security.NoSuchAlgorithmException;  
    6.   
    7. import javax.crypto.Mac;  
    8. import javax.crypto.spec.SecretKeySpec;  
    9.   
    10. /** 
    11.  *@author coolszy 
    12.  *@date 2011-5-29 
    13.  *@blog http://blog.csdn.net/coolszy 
    14.  */  
    15.   
    16. public class HMAC_SHA1  
    17. {  
    18.     private static final String MAC_NAME = "HmacSHA1";  
    19.     private static final String ENCODING = "US-ASCII";  
    20.   
    21.     /** 
    22.      * 使用 HMAC-SHA1 签名方法对对encryptText进行签名 
    23.      *  
    24.      * @param encryptText 
    25.      *            被签名的字符串 
    26.      * @param encryptKey 
    27.      *            密钥 
    28.      * @return 
    29.      * @throws NoSuchAlgorithmException 
    30.      * @throws UnsupportedEncodingException 
    31.      * @throws InvalidKeyException 
    32.      * @see <a href = 
    33.      *      "http://tools.ietf.org/html/draft-hammer-oauth-10#section-3.4.2">HMAC-SHA1</a> 
    34.      */  
    35.     public static byte[] HmacSHA1Encrypt(String encryptText, String encryptKey) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException  
    36.     {  
    37.         Mac mac = Mac.getInstance(MAC_NAME);  
    38.         SecretKeySpec spec = new SecretKeySpec(encryptKey.getBytes("US-ASCII"), MAC_NAME);  
    39.         mac.init(spec);  
    40.         byte[] text = encryptText.getBytes(ENCODING);  
    41.         return mac.doFinal(text);  
    42.     }  
    43. }  

    当我们的参数进行HmacSHA1签名后,最后我们还需进行Base64的编码。这个我也不知道怎么写,只能百度,代码如下:

    1. package com.szy.weibo.oauth;  
    2.   
    3. /** 
    4.  *@author coolszy 
    5.  *@date 2011-5-29 
    6.  *@blog http://blog.csdn.net/coolszy 
    7.  */  
    8.   
    9. public class Base64  
    10. {  
    11.     private static final char last2byte = (char) Integer.parseInt("00000011"2);  
    12.     private static final char last4byte = (char) Integer.parseInt("00001111"2);  
    13.     private static final char last6byte = (char) Integer.parseInt("00111111"2);  
    14.     private static final char lead6byte = (char) Integer.parseInt("11111100"2);  
    15.     private static final char lead4byte = (char) Integer.parseInt("11110000"2);  
    16.     private static final char lead2byte = (char) Integer.parseInt("11000000"2);  
    17.     private static final char[] encodeTable = new char[]  
    18.     { 'A''B''C''D''E''F''G''H''I''J''K''L''M''N''O''P''Q''R''S''T''U''V''W''X''Y''Z''a''b''c''d''e''f''g''h''i''j''k''l''m''n''o''p''q''r''s''t''u''v''w''x''y''z''0''1''2''3''4''5''6''7''8''9''+''/' };  
    19.   
    20.     /** 
    21.      * Base64 encoding. 
    22.      *  
    23.      * @param from 
    24.      *            The src data. 
    25.      * @return 
    26.      */  
    27.     public static String encode(byte[] from)  
    28.     {  
    29.         StringBuffer to = new StringBuffer((int) (from.length * 1.34) + 3);  
    30.         int num = 0;  
    31.         char currentByte = 0;  
    32.         for (int i = 0; i < from.length; i++)  
    33.         {  
    34.             num = num % 8;  
    35.             while (num < 8)  
    36.             {  
    37.                 switch (num)  
    38.                 {  
    39.                 case 0:  
    40.                     currentByte = (char) (from[i] & lead6byte);  
    41.                     currentByte = (char) (currentByte >>> 2);  
    42.                     break;  
    43.                 case 2:  
    44.                     currentByte = (char) (from[i] & last6byte);  
    45.                     break;  
    46.                 case 4:  
    47.                     currentByte = (char) (from[i] & last4byte);  
    48.                     currentByte = (char) (currentByte << 2);  
    49.                     if ((i + 1) < from.length)  
    50.                     {  
    51.                         currentByte |= (from[i + 1] & lead2byte) >>> 6;  
    52.                     }  
    53.                     break;  
    54.                 case 6:  
    55.                     currentByte = (char) (from[i] & last2byte);  
    56.                     currentByte = (char) (currentByte << 4);  
    57.                     if ((i + 1) < from.length)  
    58.                     {  
    59.                         currentByte |= (from[i + 1] & lead4byte) >>> 4;  
    60.                     }  
    61.                     break;  
    62.                 }  
    63.                 to.append(encodeTable[currentByte]);  
    64.                 num += 6;  
    65.             }  
    66.         }  
    67.         if (to.length() % 4 != 0)  
    68.         {  
    69.             for (int i = 4 - to.length() % 4; i > 0; i--)  
    70.             {  
    71.                 to.append("=");  
    72.             }  
    73.         }  
    74.         return to.toString();  
    75.     }  
    76. }  

    至此在oauth_signature值生成过程中需要的几个方法我们已经编写完毕。下面我们在编写一个辅助方法:

    1.oauth_timestamp:时间戳, 其值是距1970 00:00:00 GMT的秒数,必须是大于0的整数。

    这个我们可以直接使用JDK给我们提供的类方法即可: 

    1. /** 
    2.      * 产生时间戳 
    3.      *  
    4.      * @return 
    5.      */  
    6.     private String generateTimeStamp()  
    7.     {  
    8.         return String.valueOf(System.currentTimeMillis() / 1000);  
    9.     }  

    2.oauth_nonce:单次值,随机生成的32位字符串,防止重放攻击(每次请求必须不同)。

    需要产生32位字符串,这个过程也比较简单,我们Random几次。对MD5加密了解的朋友应该知道MD5加密后是32位的,因此我们可以尝试使用MD5进行加密,最后代码如下: 

    1. /** 
    2.      * 产生单次值 
    3.      *  
    4.      * @param is32 
    5.      *            产生字符串长度是否为32位 
    6.      * @return 
    7.      */  
    8.     private String generateNonce(boolean is32)  
    9.     {  
    10.         Random random = new Random();  
    11.         // 产生123400至9999999随机数   
    12.         String result = String.valueOf(random.nextInt(9876599) + 123400);  
    13.         if (is32)  
    14.         {  
    15.             // 进行MD5加密   
    16.             try  
    17.             {  
    18.                 MessageDigest md = MessageDigest.getInstance("MD5");  
    19.                 md.update(result.getBytes());  
    20.                 byte b[] = md.digest();  
    21.                 int i;  
    22.   
    23.                 StringBuffer buf = new StringBuffer("");  
    24.                 for (int offset = 0; offset < b.length; offset++)  
    25.                 {  
    26.                     i = b[offset];  
    27.                     if (i < 0)  
    28.                         i += 256;  
    29.                     if (i < 16)  
    30.                         buf.append("0");  
    31.                     buf.append(Integer.toHexString(i));  
    32.                 }  
    33.                 result = buf.toString();  
    34.             } catch (NoSuchAlgorithmException e)  
    35.             {  
    36.                 e.printStackTrace();  
    37.             }  
    38.         }  
    39.         return result;  
    40.     }  

    这个方法有个参数判断是否为32位,为什么要这么写等我们调用这个方法的时候在给大家解释。

    本节课程下载地址:http://u.115.com/file/clizvrhw

    本节文档下载地址:http://download.csdn.net/source/3405209

  • 相关阅读:
    According to TLD or attribute directive in tag file, attribute end does not accept any expressions
    Several ports (8080, 8009) required by Tomcat v6.0 Server at localhost are already in use.
    sql注入漏洞
    Servlet—简单的管理系统
    ServletContext与网站计数器
    VS2010+ICE3.5运行官方demo报错----std::bad_alloc
    java 使用相对路径读取文件
    shell编程 if 注意事项
    Ubuntu12.04下eclipse提示框黑色背景色的修改方法
    解决Ubuntu环境变量错误导致无法正常登录
  • 原文地址:https://www.cnblogs.com/xiaoxiaoboke/p/2116228.html
Copyright © 2011-2022 走看看