Java微信公眾平台開發_02_啟用服務器配置


一、准備階段

需要准備事項:

1.一個能在公網上訪問的項目:

                     見:【  Java微信公眾平台開發_01_本地服務器映射外網  】

 

2.一個微信公眾平台賬號: 

                     去注冊:(https://mp.weixin.qq.com/

 

3.策略文件

                     見:【 Java企業微信開發_Exception_02_java.security.InvalidKeyException: Illegal key size 】

4.微信官方消息加解密工具包

需要下載微信官方的消息加解密的工具包,主要是AES加密工具。由於這是企業微信的加解密包,因此我們后面還需要對這個加解密包進行一些修改。

 下載地址:https://wximg.gtimg.com/shake_tv/mpwiki/cryptoDemo.zip

 下載完將其添加進工程中:

 

 

二、填寫服務器配置

1.記住 AppID 和 AppSecret

登錄微信公眾平台,開發—>基本配置—>公眾號開發信息

 

 

2.設置IP白名單

(1)登錄微信公眾平台,開發—>基本配置—>公眾號開發信息—>ip白名單—>查看—>修改

(2)需要將服務器的公網ip添加進去。若需要添加多個ip,則每行添加一個ip即可。

(3)查詢服務器的公網ip:

          window系統: 直接百度搜索ip即可

          Linux系統: 參見【  Linux_服務器_01_查看公網IP  】     

 

 

 

3.填寫服務器配置

登錄微信公眾平台,開發—>基本配置—>服務器配置—>修改配置

3.1 URL:

開發者用來接收微信消息和事件的接口URL 。在三種情況下會請求這個URL:

(1)回調模式:

填寫完服務器配置,點擊提交,微信服務器將發送GET請求到填寫的服務器地址URL上,並攜帶上四個參數 signature 、timestamp、nonce、echostr

(2)接收消息:

當用戶發送消息給公眾號時,消息將被以POST方式推送到到填寫的服務器地址URL上,

在安全模式(推薦)下,攜帶上六個參數 signature 、timestamp、nonce、openid、encrypt_type(加密類型,為aes)和msg_signature(消息體簽名,用於驗證消息體的正確性)

在明文模式(默認)下,攜帶上六個參數 signature 、timestamp、nonce、openid

(3)接收事件

當特定的用戶操作引發事件推送時,(如用戶點擊某個click菜單按鈕時),事件將被以POST方式推送到到填寫的服務器地址URL上。 請求參數  同  接受消息

 

3.2 Token:

隨機填寫,要與代碼中保持一致。生成加解密工具類、生成簽名 時會用到

 

3.3 EncodingAESKey:

隨機生成,要與代碼中保持一致,生成加解密工具類時會用到。EncodingAESKey即消息加解密Key,長度固定為43個字符,從a-z,A-Z,0-9共62個字符中選取。

 

3.4 消息加解密方式

這里選擇安全模式,這樣在接收消息和事件時,都需要進行消息加解密。若選明文模式,則在接收消息和事件時,都不需要進行消息加解密。

 

 

 

三、驗證服務器地址的有效性

1. 設置失敗

填寫完服務器配置后,這時我們點擊提交,會提示設置失敗。這是因為我們點擊提交后,微信服務器將發送GET請求到填寫的服務器地址URL上,並攜帶上四個參數 signature 、timestamp、nonce、echostr ,開發者接收到者四個參數之后,需要對這四個參數與token一起 進行簽名校驗。

2.簽名校驗的流程

(1)將token、timestamp、nonce三個參數進行字典序排序

(2)將三個參數字符串拼接成一個字符串進行sha1加密 得到一個簽名signature1

(3)開發者將獲得加密后的字符串signature1與signature進行比較

(4)若二者相同,則認為此次GET請求來自微信服務器,可原樣返回echostr參數內容,配置成功。

         若二者不相同,則認為此次GET請求不是來自微信服務器,不可原樣返回echostr參數內容,配置失敗。

 

總結:用 token、timestamp、nonce 生成一個簽名signature1,並與signature比較,若相同,則原樣返回echostr,若不同則配置失敗。

 

3.微信服務器怎么判斷簽名校驗是否成功?

若微信服務器收到原樣的echostr,則任務校驗成功。也就是說如果你收到signature 、timestamp、nonce、echostr 后,什么都不做,就只原樣返回一個echostr,微信服務器還是認為你校驗成功了。

 

4.簽名校驗的代碼實現

4.1  用SHA1算法生成安全簽名 signature1

/**
     * 微信公眾號SHA1加密算法
     * 用SHA1算法生成安全簽名
     * @param token 票據
     * @param timestamp 時間戳
     * @param nonce 隨機字符串
     * @return 安全簽名
     * @throws AesException 
     */
    public static String getSHA1_WXGZ(String token, String timestamp, String nonce) throws AesException
              {
        try {
            String[] array = new String[] { token, timestamp, nonce };
            StringBuffer sb = new StringBuffer();
            //1.將token、timestamp、nonce三個參數進行字典序排序
            Arrays.sort(array);
            
            //2.將三個參數字符串拼接成一個字符串進行sha1加密
            for (int i = 0; i < 3; i++) {
                sb.append(array[i]);
            }
            String str = sb.toString();
            //2.2 SHA1簽名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.ComputeSignatureError);
        }
    }
}
View Code

 

4.2 驗證signature1是否與signature相同

/**
     * 
     * 驗證URL
     * @param msgSignature 
     * @param timeStamp 時間戳,對應URL參數的timestamp
     * @param nonce 隨機串,對應URL參數的nonce
     * @param echoStr 隨機串,對應URL參數的echostr
     * 
     * @return 解密之后的echostr
     * @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息
     */
    /**
     * @desc :微信公眾號  驗證url
     *  
     * @param msgSignature  簽名串,對應URL參數的msg_signature
     * @param token 公眾平台上,開發者設置的token
     * @param timeStamp   時間戳,對應URL參數的timestamp
     * @param nonce 隨機數,對應URL參數的nonce
     * @return
     *    true  驗證成功
     * @throws AesException boolean
     */
    public boolean verifyUrl_WXGZ(String msgSignature, String token , String timeStamp, String nonce) throws AesException {
        //1.進行SHA1加密
        String signature = SHA1.getSHA1_WXGZ(token, timeStamp, nonce);

        //2.驗證 token、timestamp、nonce進行SHA1加密生成的signature 是否與url傳過來msgSignature相同
        if (!signature.equals(msgSignature)) {
            throw new AesException(AesException.ValidateSignatureError);
        //3.若相同,則url驗證成功,返回true
        }else{
            return true;
        }
    }
View Code

 

4.3 在servlet的doGet方法中進行url的校驗

    //1.接收  回調模式  的請求
    protected void doGet(HttpServletRequest request, HttpServletResponse response)  {
        logger.info("get--------------");
        //一、校驗URL
        //1.准備校驗參數
        // 微信加密簽名  
        String msgSignature = request.getParameter("signature");  
        // 時間戳  
        String timeStamp = request.getParameter("timestamp");  
        // 隨機數  
        String nonce = request.getParameter("nonce");  
        // 隨機字符串  
        String echoStr = request.getParameter("echostr");  

        PrintWriter out=null;
        try {
            //2.校驗url
            //2.1 創建加解密類
            WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);

            //2.2進行url校驗
            //不拋異常就說明校驗成功
            boolean mark= wxcpt.verifyUrl_WXGZ(msgSignature, Env.TOKEN, timeStamp, nonce);

            //2.3若校驗成功,則原樣返回 echoStr
            if (mark) {  
                out = response.getWriter(); 
                out.print(echoStr);  
            }



        } catch (AesException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();  
                out = null;                       //釋放資源
            }
        }
    }
View Code

 

 

 四、代碼實現

1.微信配置類—Env.java

微信公眾號接入配置類

package com.ray.weixin.gz.config;

/**@desc  : 微信公眾號接入配置
 * 
 * @author: shirayner
 * @date  : 2017年9月27日 下午4:57:36
 */

public class Env {

    /**
     * 1. 企業應用接入秘鑰相關
     */
    //public static final String APP_ID = "wx4dca3424bebef2cc";
   // public static final String APP_SECRET = "068e2599abf88ba78491a07906f3c56e";
  
    //測試號
    public static final String APP_ID = "wxa0064ea657f80062";
    public static final String APP_SECRET = "fcc960840df869ad1a46af7993784917";
    

    /**
     * 2.服務器配置:
     * 啟用並設置服務器配置后,用戶發給公眾號的消息以及開發者需要的事件推送,將被微信轉發到該URL中
     */
    public static final String TOKEN = "weixin";
    public static final String ENCODING_AES_KEY = "JvJ1Dww6tjUU2psC3pmokXvOHHfovfWP3LfX1xrriz1";
    
    
  

    
}
View Code

 

2.HTTP請求工具類—HttpHelper.java

package com.ray.weixin.gz.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.util.EntityUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;


/**
 * HTTP請求封裝,建議直接使用sdk的API
 */
public class HttpHelper {

    /**
     * @desc :1.發起GET請求
     *  
     * @param url
     * @return JSONObject
     * @throws Exception 
     */
    public static JSONObject doGet(String url) throws Exception {

        //1.生成一個請求
        HttpGet httpGet = new HttpGet(url);
        //2.配置請求的屬性
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();//2000
        httpGet.setConfig(requestConfig);

        //3.發起請求,獲取響應信息    
        //3.1 創建httpClient 
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        try {
            //3.2 發起請求,獲取響應信息    
            response = httpClient.execute(httpGet, new BasicHttpContext());

            //如果返回結果的code不等於200,說明出錯了  
            if (response.getStatusLine().getStatusCode() != 200) {

                System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
                        + ", url=" + url);
                return null;
            }
            //4.解析請求結果
            HttpEntity entity = response.getEntity();      //reponse返回的數據在entity中 
            if (entity != null) {
                String resultStr = EntityUtils.toString(entity, "utf-8");  //將數據轉化為string格式  
                System.out.println("GET請求結果:"+resultStr);
                JSONObject result = JSON.parseObject(resultStr);    //將String轉換為 JSONObject

                if(result.getInteger("errcode")==null) {
                    return result;
                }else if (0 == result.getInteger("errcode")) {
                    return result;
                }else {
                    System.out.println("request url=" + url + ",return value=");
                    System.out.println(resultStr);
                    int errCode = result.getInteger("errcode");
                    String errMsg = result.getString("errmsg");
                    throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                }
            }
        } catch (IOException e) {
            System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
            e.printStackTrace();
        } finally {
            if (response != null) try {
                response.close();                     //釋放資源

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }


    /** 2.發起POST請求
     * @desc :
     *  
     * @param url   請求url
     * @param data  請求參數(json)
     * @return
     * @throws Exception JSONObject
     */
    public static JSONObject doPost(String url, Object data) throws Exception {
        //1.生成一個請求
        HttpPost httpPost = new HttpPost(url);

        //2.配置請求屬性
        //2.1 設置請求超時時間
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
        httpPost.setConfig(requestConfig);
        //2.2 設置數據傳輸格式-json
        httpPost.addHeader("Content-Type", "application/json");
        //2.3 設置請求參數
        StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8");
        httpPost.setEntity(requestEntity);

        //3.發起請求,獲取響應信息    
        //3.1 創建httpClient 
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;

        try {


            //3.3 發起請求,獲取響應
            response = httpClient.execute(httpPost, new BasicHttpContext());

            if (response.getStatusLine().getStatusCode() != 200) {

                System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
                        + ", url=" + url);
                return null;
            }

            //獲取響應內容
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String resultStr = EntityUtils.toString(entity, "utf-8");
                System.out.println("POST請求結果:"+resultStr);

                //解析響應內容
                JSONObject result = JSON.parseObject(resultStr);

                if(result.getInteger("errcode")==null) {
                    return result;
                }else if (0 == result.getInteger("errcode")) {
                    return result;
                }else {
                    System.out.println("request url=" + url + ",return value=");
                    System.out.println(resultStr);
                    int errCode = result.getInteger("errcode");
                    String errMsg = result.getString("errmsg");
                    throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                }
            }
        } catch (IOException e) {
            System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
            e.printStackTrace();
        } finally {
            if (response != null) try {
                response.close();              //釋放資源

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    /**
     * @desc : 3.上傳文件
     *  
     * @param url   請求url
     * @param file  上傳的文件
     * @return
     * @throws Exception JSONObject
     */
    public static JSONObject uploadMedia(String url, File file) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse response = null;
        CloseableHttpClient httpClient = HttpClients.createDefault();
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();
        httpPost.setConfig(requestConfig);

        HttpEntity requestEntity = MultipartEntityBuilder.create().addPart("media",
                new FileBody(file, ContentType.APPLICATION_OCTET_STREAM, file.getName())).build();
        httpPost.setEntity(requestEntity);

        try {
            response = httpClient.execute(httpPost, new BasicHttpContext());

            if (response.getStatusLine().getStatusCode() != 200) {

                System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
                        + ", url=" + url);
                return null;
            }
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String resultStr = EntityUtils.toString(entity, "utf-8");

                JSONObject result = JSON.parseObject(resultStr);
                //上傳臨時素材成功
                if (result.getString("errcode")== null) {
                    // 成功
                    //result.remove("errcode");
                    //result.remove("errmsg");
                    return result;
                } else {
                    System.out.println("request url=" + url + ",return value=");
                    System.out.println(resultStr);
                    int errCode = result.getInteger("errcode");
                    String errMsg = result.getString("errmsg");
                    throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                }
            }
        } catch (IOException e) {
            System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
            e.printStackTrace();
        } finally {
            if (response != null) try {
                response.close();                  //釋放資源


            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    /**
     * @desc : 4.下載文件 -get
     *  
     * @param url  請求url
     * @param fileDir  下載路徑
     * @return
     * @throws Exception File
     */
    public static File downloadMedia(String url, String fileDir) throws Exception  {
        //1.生成一個請求
        HttpGet httpGet = new HttpGet(url);
        //2.配置請求屬性
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
        httpGet.setConfig(requestConfig);

        //3.發起請求,獲取響應信息    
        //3.1 創建httpClient 
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;

        //4.設置本地保存的文件  
        //File file = new File(fileDir);
        File file = null;
        try {
            //5. 發起請求,獲取響應信息    
            response = httpClient.execute(httpGet, new BasicHttpContext());
            System.out.println("HttpStatus.SC_OK:"+HttpStatus.SC_OK);  
            System.out.println("response.getStatusLine().getStatusCode():"+response.getStatusLine().getStatusCode());  
            System.out.println("http-header:"+JSON.toJSONString( response.getAllHeaders() ));  
            System.out.println("http-filename:"+getFileName(response) );  

            //請求成功  
            if(HttpStatus.SC_OK==response.getStatusLine().getStatusCode()){  

                //6.取得請求內容  
                HttpEntity entity = response.getEntity();  

                if (entity != null) {  
                    //這里可以得到文件的類型 如image/jpg /zip /tiff 等等 但是發現並不是十分有效,有時明明后綴是.rar但是取到的是null,這點特別說明  
                    System.out.println(entity.getContentType());  
                    //可以判斷是否是文件數據流  
                    System.out.println(entity.isStreaming());  

                    //6.1 輸出流
                    //6.1.1獲取文件名,拼接文件路徑
                    String fileName=getFileName(response);
                    fileDir=fileDir+fileName;
                    file = new File(fileDir);
                    //6.1.2根據文件路徑獲取輸出流
                    FileOutputStream output = new FileOutputStream(file);  

                    //6.2 輸入流:從釘釘服務器返回的文件流,得到網絡資源並寫入文件  
                    InputStream input = entity.getContent();  

                    //6.3 將數據寫入文件:將輸入流中的數據寫入到輸出流
                    byte b[] = new byte[1024];  
                    int j = 0;  
                    while( (j = input.read(b))!=-1){  
                        output.write(b,0,j);  
                    }  
                    output.flush();  
                    output.close();   
                }  
                if (entity != null) {  
                    entity.consumeContent();  
                }  
            }  
        } catch (IOException e) {
            System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
            e.printStackTrace();
        } finally {
            if (response != null) try {
                response.close();                       //釋放資源


            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return file;
    }


    /**
     * @desc : 5.下載文件 - post
     *  
     * @param url  請求url
     * @param data  post請求參數
     * @param fileDir 文件下載路徑
     * @return
     * @throws Exception File
     */
    public static File downloadMedia(String url, Object data, String fileDir) throws Exception  {
        //1.生成一個請求
        HttpPost httpPost = new HttpPost(url);

        //2.配置請求屬性
        //2.1 設置請求超時時間
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
        httpPost.setConfig(requestConfig);
        //2.2 設置數據傳輸格式-json
        httpPost.addHeader("Content-Type", "application/json");
        //2.3 設置請求參數
        StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8");
        httpPost.setEntity(requestEntity);

        //3.發起請求,獲取響應信息    
        //3.1 創建httpClient 
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;

        //4.設置本地保存的文件  
        //File file = new File(fileDir);
        File file = null;
        try {
            //5. 發起請求,獲取響應信息    
            response = httpClient.execute(httpPost, new BasicHttpContext());
            System.out.println("HttpStatus.SC_OK:"+HttpStatus.SC_OK);  
            System.out.println("response.getStatusLine().getStatusCode():"+response.getStatusLine().getStatusCode());  
            System.out.println("http-header:"+JSON.toJSONString( response.getAllHeaders() ));  
            System.out.println("http-filename:"+getFileName(response) );  

            //請求成功  
            if(HttpStatus.SC_OK==response.getStatusLine().getStatusCode()){  

                //6.取得請求內容  
                HttpEntity entity = response.getEntity();  

                if (entity != null) {  
                    //這里可以得到文件的類型 如image/jpg /zip /tiff 等等 但是發現並不是十分有效,有時明明后綴是.rar但是取到的是null,這點特別說明  
                    System.out.println(entity.getContentType());  
                    //可以判斷是否是文件數據流  
                    System.out.println(entity.isStreaming());  

                    //6.1 輸出流
                    //6.1.1獲取文件名,拼接文件路徑
                    String fileName=getFileName(response);
                    fileDir=fileDir+fileName;
                    file = new File(fileDir);
                    //6.1.2根據文件路徑獲取輸出流
                    FileOutputStream output = new FileOutputStream(file);  

                    //6.2 輸入流:從釘釘服務器返回的文件流,得到網絡資源並寫入文件  
                    InputStream input = entity.getContent();  

                    //6.3 將數據寫入文件:將輸入流中的數據寫入到輸出流
                    byte b[] = new byte[1024];  
                    int j = 0;  
                    while( (j = input.read(b))!=-1){  
                        output.write(b,0,j);  
                    }  
                    output.flush();  
                    output.close();   
                }  
                if (entity != null) {  
                    entity.consumeContent();  
                }  
            }  
        } catch (IOException e) {
            System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
            e.printStackTrace();
        } finally {
            if (response != null) try {
                response.close();                       //釋放資源


            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return file;
    }


    /** 5. 獲取response header中Content-Disposition中的filename值 
     * @desc :
     *  
     * @param response  響應
     * @return String
     */
    public static String getFileName(HttpResponse response) {  
        Header contentHeader = response.getFirstHeader("Content-Disposition");  
        String filename = null;  
        if (contentHeader != null) {  
            HeaderElement[] values = contentHeader.getElements();  
            if (values.length == 1) {  
                NameValuePair param = values[0].getParameterByName("filename");  
                if (param != null) {  
                    try {  
                        //filename = new String(param.getValue().toString().getBytes(), "utf-8");  
                        //filename=URLDecoder.decode(param.getValue(),"utf-8");  
                        filename = param.getValue();  
                    } catch (Exception e) {  
                        e.printStackTrace();  
                    }  
                }  
            }  
        }  
        return filename;  
    }  



}
View Code

 

3.Token相關工具類—AuthHelper.java

package com.ray.weixin.gz.util;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.alibaba.fastjson.JSONObject;
import com.ray.weixin.gz.config.Env;


/**
 * 微信公眾號 Token、配置工具類
 * @desc  : AccessToken、Jsticket 、Jsapi
 * 
 * @author: shirayner
 * @date  : 2017年9月27日 下午5:00:25
 */
public class AuthHelper {
    private static final Logger logger = LogManager.getLogger(AuthHelper.class);

    //1.獲取access_token的接口地址,有效期為7200秒
    private static final String GET_ACCESSTOKEN_URL="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; 
    //2.獲取getJsapiTicket的接口地址,有效期為7200秒 
    private static final String GET_JSAPITICKET_URL="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi"; 

    //3.通過code換取網頁授權access_token
    private static final String GET_ACCESSTOKEN_BYCODE_URL="https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; 


    /**
     * @desc :1.獲取access_token 
     *  
     * @param appId  第三方用戶唯一憑證
     * @param appSecret  第三方用戶唯一憑證密鑰,即appsecret
     * 
     * @return
     *      access_token    獲取到的憑證
     *      expires_in    憑證有效時間,單位:秒
     * @throws Exception String
     */
    public static String getAccessToken(String appId,String appSecret) throws Exception {
        //1.獲取請求url
        String url=GET_ACCESSTOKEN_URL.replace("APPID", appId).replace("APPSECRET", appSecret);

        //2.發起GET請求,獲取返回結果
        JSONObject jsonObject=HttpHelper.doGet(url);
        logger.info("jsonObject:"+jsonObject.toJSONString());

        //3.解析結果,獲取accessToken
        String accessToken="";  
        if (null != jsonObject) {  
            //4.錯誤消息處理
            if (jsonObject.getInteger("errcode")!=null && 0 != jsonObject.getInteger("errcode")) {  
                int errCode = jsonObject.getInteger("errcode");
                String errMsg = jsonObject.getString("errmsg");
                throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                //5.成功獲取accessToken
            }else {
                accessToken=jsonObject.getString("access_token");
            }  
        }  


        return accessToken;
    }


    /**
     * @desc :2.獲取JsapiTicket
     *  
     * @param accessToken  有效憑證
     * @return
     * @throws Exception String
     */
    public static String getJsapiTicket(String accessToken) throws Exception {
        //1.獲取請求url
        String url=GET_JSAPITICKET_URL.replace("ACCESS_TOKEN", accessToken);

        //2.發起GET請求,獲取返回結果
        JSONObject jsonObject=HttpHelper.doGet(url);
        logger.info("jsonObject:"+jsonObject.toJSONString());

        //3.解析結果,獲取accessToken
        String jsapiTicket="";  
        if (null != jsonObject) {  
            //4.錯誤消息處理
            if (jsonObject.getInteger("errcode")!=null && 0 != jsonObject.getInteger("errcode")) {  
                int errCode = jsonObject.getInteger("errcode");
                String errMsg = jsonObject.getString("errmsg");
                throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                //5.成功獲取jsapiTicket
            }else {
                jsapiTicket=jsonObject.getString("ticket");
            }  
        }  


        return jsapiTicket;
    }

    /**
     * @desc : 3.通過code換取網頁授權access_token
     *  
     * @param appId  第三方用戶唯一憑證
     * @param appSecret  第三方用戶唯一憑證密鑰,即appsecret
     * @param Code  code作為換取access_token的票據,每次用戶授權帶上的code將不一樣,code只能使用一次,5分鍾未被使用自動過期。
     * 
     * @return
     * access_token    網頁授權接口調用憑證,注意:此access_token與基礎支持的access_token不同
     * expires_in    access_token接口調用憑證超時時間,單位(秒)
     * refresh_token    用戶刷新access_token
     * openid    用戶唯一標識,請注意,在未關注公眾號時,用戶訪問公眾號的網頁,也會產生一個用戶和公眾號唯一的OpenID
     * scope    用戶授權的作用域,使用逗號(,)分隔
     * 
     * @throws Exception String
     */
    public static JSONObject getAccessTokenByCode(String appId,String appSecret,String code) throws Exception {
        //1.獲取請求url
        String url=GET_ACCESSTOKEN_BYCODE_URL.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);

        //2.發起GET請求,獲取返回結果
        JSONObject jsonObject=HttpHelper.doGet(url);
        logger.info("jsonObject:"+jsonObject.toJSONString());

        //3.解析結果,獲取accessToken
        JSONObject returnJsonObject=null;
        if (null != jsonObject) {  
            //4.錯誤消息處理
            if (jsonObject.getInteger("errcode")!=null && 0 != jsonObject.getInteger("errcode")) {  
                int errCode = jsonObject.getInteger("errcode");
                String errMsg = jsonObject.getString("errmsg");
                throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                //5.成功獲取accessToken
            }else {
                returnJsonObject=jsonObject;
            }  
        }  


        return returnJsonObject;
    }


    /**
     * @desc :4.獲取前端jsapi需要的配置參數
     *  
     * @param request
     * @return String
     */
    public static String getJsapiConfig(HttpServletRequest request){  

        //1.准備好參與簽名的字段
        //1.1 url
        /* 
         *以http://localhost/test.do?a=b&c=d為例 
         *request.getRequestURL的結果是http://localhost/test.do 
         *request.getQueryString的返回值是a=b&c=d 
         */  
        String urlString = request.getRequestURL().toString();
        String queryString = request.getQueryString();
        String queryStringEncode = null;
        String url;
        if (queryString != null) {
            queryStringEncode = URLDecoder.decode(queryString);
            url = urlString + "?" + queryStringEncode;
        } else {
            url = urlString;
        }

        //1.2 noncestr
        String nonceStr=UUID.randomUUID().toString();      //隨機數
        //1.3 timestamp
        long timeStamp = System.currentTimeMillis() / 1000;     //時間戳參數  

        String signedUrl = url;

        String accessToken = null;
        String ticket = null;

        String signature = null;       //簽名


        try {  
            //1.4 jsapi_ticket
            accessToken=getAccessToken(Env.APP_ID, Env.APP_SECRET);  
            ticket=getJsapiTicket(accessToken);  

            //2.進行簽名,獲取signature
            signature=getSign(ticket,nonceStr,timeStamp,signedUrl);  


        } catch (Exception e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  

        logger.info("accessToken:"+accessToken);
        logger.info("ticket:"+ticket);
        logger.info("nonceStr:"+nonceStr);
        logger.info("timeStamp:"+timeStamp);
        logger.info("signedUrl:"+signedUrl);
        logger.info("signature:"+signature);
        logger.info("appId:"+Env.APP_ID);




        String configValue = "{signature:'" + signature + "',nonceStr:'" + nonceStr + "',timeStamp:'"
                + timeStamp + "',appId:'" + Env.APP_ID + "'}";
        logger.info("configValue:"+configValue);

        return configValue;  
    }  


    /**
     * @desc : 3.生成簽名的函數 
     *  
     * @param ticket jsticket
     * @param nonceStr 隨機串,自己定義
     * @param timeStamp 生成簽名用的時間戳 
     * @param url 需要進行免登鑒權的頁面地址,也就是執行dd.config的頁面地址 
     * @return
     * @throws Exception String
     */

    public static String getSign(String jsTicket, String nonceStr, Long timeStamp, String url) throws Exception {  
        String plainTex = "jsapi_ticket=" + jsTicket + "&noncestr=" + nonceStr + "&timestamp=" + timeStamp + "&url=" + url;
        System.out.println(plainTex);
        try {  
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(plainTex.getBytes("UTF-8"));
            return byteToHex(crypt.digest());
        } catch (NoSuchAlgorithmException e) {  
            throw new Exception(e.getMessage());  
        } catch (UnsupportedEncodingException e) {  
            throw new Exception(e.getMessage());  
        }  
    }  

    //將bytes類型的數據轉化為16進制類型  
    private static String byteToHex(byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", new Object[] { Byte.valueOf(b) });
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }







}
View Code

 

4.驗證URL的servlet—WeiXinServlet

package com.ray.weixin.gz.controller;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.ray.weixin.gz.config.Env;
import com.ray.weixin.gz.service.message.ReplyMessageService;



/**
 * Servlet implementation class WeiXinServlet
 */         
public class WeiXinServlet extends HttpServlet {
    private static final Logger logger = LogManager.getLogger(WeiXinServlet.class);



    private static final long serialVersionUID = 1L;

    /**
     * Default constructor. 
     */
    public WeiXinServlet() {
        // TODO Auto-generated constructor stub
    }

    //1.接收  回調模式  的請求
    protected void doGet(HttpServletRequest request, HttpServletResponse response)  {
        logger.info("get--------------");
        //一、校驗URL
        //1.准備校驗參數
        // 微信加密簽名  
        String msgSignature = request.getParameter("signature");  
        // 時間戳  
        String timeStamp = request.getParameter("timestamp");  
        // 隨機數  
        String nonce = request.getParameter("nonce");  
        // 隨機字符串  
        String echoStr = request.getParameter("echostr");  

        PrintWriter out=null;
        try {
            //2.校驗url
            //2.1 創建加解密類
            WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);

            //2.2進行url校驗
            //不拋異常就說明校驗成功
            String sEchoStr= wxcpt.verifyUrl_WXGZ(msgSignature, Env.TOKEN, timeStamp, nonce,echoStr);

            //2.3若校驗成功,則原樣返回 echoStr

            out = response.getWriter(); 
            out.print(sEchoStr);  




        } catch (AesException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();  
                out = null;                       //釋放資源
            }
        }
    }

    //2.接收  微信消息和事件  的請求
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        logger.info("post--------------");
        //1.將請求、響應的編碼均設置為UTF-8(防止中文亂碼)  
        request.setCharacterEncoding("UTF-8");  
        response.setCharacterEncoding("UTF-8");  

        //2.調用消息業務類接收消息、處理消息  
        String respMessage = ReplyMessageService.reply(request);  

        //3.響應消息  
        PrintWriter out = response.getWriter();  
        out.print(respMessage);  
        out.close(); 



    }

}
View Code

 

5.微信公眾號SHA1加密算法工具類—SHA1.java

此類是信官方的消息加解密的工具包中的一個類,我在原來的基礎上增加了一個方法 getSHA1_WXGZ(String, String, String) 

/**
 * 對公眾平台發送給公眾賬號的消息加解密示例代碼.
 * 
 * @copyright Copyright (c) 1998-2014 Tencent Inc.
 */

// ------------------------------------------------------------------------

package com.qq.weixin.mp.aes;

import java.security.MessageDigest;
import java.util.Arrays;

/**
 * SHA1 class
 *
 * 計算公眾平台的消息簽名接口.
 */
class SHA1 {

    /**
     * 用SHA1算法生成安全簽名
     * @param token 票據
     * @param timestamp 時間戳
     * @param nonce 隨機字符串
     * @param encrypt 密文
     * @return 安全簽名
     * @throws AesException 
     */
    public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException
              {
        try {
            String[] array = new String[] { token, timestamp, nonce, encrypt };
            StringBuffer sb = new StringBuffer();
            // 字符串排序
            Arrays.sort(array);
            for (int i = 0; i < 4; i++) {
                sb.append(array[i]);
            }
            String str = sb.toString();
            // SHA1簽名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.ComputeSignatureError);
        }
    }
    
    /**
     * 微信公眾號SHA1加密算法
     * 用SHA1算法生成安全簽名
     * @param token 票據
     * @param timestamp 時間戳
     * @param nonce 隨機字符串
     * @return 安全簽名
     * @throws AesException 
     */
    public static String getSHA1_WXGZ(String token, String timestamp, String nonce) throws AesException
              {
        try {
            String[] array = new String[] { token, timestamp, nonce };
            StringBuffer sb = new StringBuffer();
            //1.將token、timestamp、nonce三個參數進行字典序排序
            Arrays.sort(array);
            
            //2.將三個參數字符串拼接成一個字符串進行sha1加密
            for (int i = 0; i < 3; i++) {
                sb.append(array[i]);
            }
            String str = sb.toString();
            //2.2 SHA1簽名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.ComputeSignatureError);
        }
    }
}
View Code

 

6.校驗url—WXBizMsgCrypt.java

此類是信官方的消息加解密的工具包中的一個類,我在原來的基礎上增加了一個方法  verifyUrl_WXGZ(String msgSignature, String token , String timeStamp, String nonce,String echoStr) 

/**
 * 對公眾平台發送給公眾賬號的消息加解密示例代碼.
 * 
 * @copyright Copyright (c) 1998-2014 Tencent Inc.
 */

// ------------------------------------------------------------------------

/**
 * 針對org.apache.commons.codec.binary.Base64,
 * 需要導入架包commons-codec-1.9(或commons-codec-1.8等其他版本)
 * 官方下載地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
 */
package com.qq.weixin.mp.aes;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

/**
 * 提供接收和推送給公眾平台消息的加解密接口(UTF8編碼的字符串).
 * <ol>
 *     <li>第三方回復加密消息給公眾平台</li>
 *     <li>第三方收到公眾平台發送的消息,驗證消息的安全性,並對消息進行解密。</li>
 * </ol>
 * 說明:異常java.security.InvalidKeyException:illegal Key Size的解決方案
 * <ol>
 *     <li>在官方網站下載JCE無限制權限策略文件(JDK7的下載地址:
 *      http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
 *     <li>下載后解壓,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
 *     <li>如果安裝了JRE,將兩個jar文件放到%JRE_HOME%\lib\security目錄下覆蓋原來的文件</li>
 *     <li>如果安裝了JDK,將兩個jar文件放到%JDK_HOME%\jre\lib\security目錄下覆蓋原來文件</li>
 * </ol>
 */
public class WXBizMsgCrypt {
    static Charset CHARSET = Charset.forName("utf-8");
    Base64 base64 = new Base64();
    byte[] aesKey;
    String token;
    String corpId;

    /**
     * 構造函數
     * @param token 公眾平台上,開發者設置的token
     * @param encodingAesKey 公眾平台上,開發者設置的EncodingAESKey
     * @param corpId 企業的corpid
     * 
     * @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息
     */
    public WXBizMsgCrypt(String token, String encodingAesKey, String corpId) throws AesException {
        if (encodingAesKey.length() != 43) {
            throw new AesException(AesException.IllegalAesKey);
        }

        this.token = token;
        this.corpId = corpId;
        aesKey = Base64.decodeBase64(encodingAesKey + "=");
    }

    // 生成4個字節的網絡字節序
    byte[] getNetworkBytesOrder(int sourceNumber) {
        byte[] orderBytes = new byte[4];
        orderBytes[3] = (byte) (sourceNumber & 0xFF);
        orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
        orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
        orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
        return orderBytes;
    }

    // 還原4個字節的網絡字節序
    int recoverNetworkBytesOrder(byte[] orderBytes) {
        int sourceNumber = 0;
        for (int i = 0; i < 4; i++) {
            sourceNumber <<= 8;
            sourceNumber |= orderBytes[i] & 0xff;
        }
        return sourceNumber;
    }

    // 隨機生成16位字符串
    String getRandomStr() {
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 16; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 對明文進行加密.
     * 
     * @param text 需要加密的明文
     * @return 加密后base64編碼的字符串
     * @throws AesException aes加密失敗
     */
    String encrypt(String randomStr, String text) throws AesException {
        ByteGroup byteCollector = new ByteGroup();
        byte[] randomStrBytes = randomStr.getBytes(CHARSET);
        byte[] textBytes = text.getBytes(CHARSET);
        byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);
        byte[] corpidBytes = corpId.getBytes(CHARSET);

        // randomStr + networkBytesOrder + text + corpid
        byteCollector.addBytes(randomStrBytes);
        byteCollector.addBytes(networkBytesOrder);
        byteCollector.addBytes(textBytes);
        byteCollector.addBytes(corpidBytes);

        // ... + pad: 使用自定義的填充方式對明文進行補位填充
        byte[] padBytes = PKCS7Encoder.encode(byteCollector.size());
        byteCollector.addBytes(padBytes);

        // 獲得最終的字節流, 未加密
        byte[] unencrypted = byteCollector.toBytes();

        try {
            // 設置加密模式為AES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

            // 加密
            byte[] encrypted = cipher.doFinal(unencrypted);

            // 使用BASE64對加密后的字符串進行編碼
            String base64Encrypted = base64.encodeToString(encrypted);

            return base64Encrypted;
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.EncryptAESError);
        }
    }

    /**
     * 對密文進行解密.
     * 
     * @param text 需要解密的密文
     * @return 解密得到的明文
     * @throws AesException aes解密失敗
     */
    String decrypt(String text) throws AesException {
        byte[] original;
        try {
            // 設置解密模式為AES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
            cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);

            // 使用BASE64對密文進行解碼
            byte[] encrypted = Base64.decodeBase64(text);

            // 解密
            original = cipher.doFinal(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.DecryptAESError);
        }

        String xmlContent, from_corpid;
        try {
            // 去除補位字符
            byte[] bytes = PKCS7Encoder.decode(original);

            // 分離16位隨機字符串,網絡字節序和corpId
            byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);

            int xmlLength = recoverNetworkBytesOrder(networkOrder);

            xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);
            from_corpid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length),
                    CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.IllegalBuffer);
        }

        // corpid不相同的情況
        if (!from_corpid.equals(corpId)) {
            throw new AesException(AesException.ValidateCorpidError);
        }
        return xmlContent;

    }

    /**
     * 將公眾平台回復用戶的消息加密打包.
     * <ol>
     *     <li>對要發送的消息進行AES-CBC加密</li>
     *     <li>生成安全簽名</li>
     *     <li>將消息密文和安全簽名打包成xml格式</li>
     * </ol>
     * 
     * @param replyMsg 公眾平台待回復用戶的消息,xml格式的字符串
     * @param timeStamp 時間戳,可以自己生成,也可以用URL參數的timestamp
     * @param nonce 隨機串,可以自己生成,也可以用URL參數的nonce
     * 
     * @return 加密后的可以直接回復用戶的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串
     * @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息
     */
    public String EncryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException {
        // 加密
        String encrypt = encrypt(getRandomStr(), replyMsg);

        // 生成安全簽名
        if (timeStamp == "") {
            timeStamp = Long.toString(System.currentTimeMillis());
        }

        String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt);

        // System.out.println("發送給平台的簽名是: " + signature[1].toString());
        // 生成發送的xml
        String result = XMLParse.generate(encrypt, signature, timeStamp, nonce);
        return result;
    }

    /**
     * 檢驗消息的真實性,並且獲取解密后的明文.
     * <ol>
     *     <li>利用收到的密文生成安全簽名,進行簽名驗證</li>
     *     <li>若驗證通過,則提取xml中的加密消息</li>
     *     <li>對消息進行解密</li>
     * </ol>
     * 
     * @param msgSignature 簽名串,對應URL參數的msg_signature
     * @param timeStamp 時間戳,對應URL參數的timestamp
     * @param nonce 隨機串,對應URL參數的nonce
     * @param postData 密文,對應POST請求的數據
     * 
     * @return 解密后的原文
     * @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息
     */
    public String DecryptMsg(String msgSignature, String timeStamp, String nonce, String postData)
            throws AesException {

        // 密鑰,公眾賬號的app secret
        // 提取密文
        Object[] encrypt = XMLParse.extract(postData);

        // 驗證安全簽名
        String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());

        // 和URL中的簽名比較是否相等
        // System.out.println("第三方收到URL中的簽名:" + msg_sign);
        // System.out.println("第三方校驗簽名:" + signature);
        if (!signature.equals(msgSignature)) {
            throw new AesException(AesException.ValidateSignatureError);
        }

        // 解密
        String result = decrypt(encrypt[1].toString());
        return result;
    }

    /**
     * 驗證URL
     * @param msgSignature 簽名串,對應URL參數的msg_signature
     * @param timeStamp 時間戳,對應URL參數的timestamp
     * @param nonce 隨機串,對應URL參數的nonce
     * @param echoStr 隨機串,對應URL參數的echostr,在企業微信中是加密過的,需要解密后返回給企業微信官方服務器
     * 
     * @return 解密之后的echostr
     * @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息
     */
    public String VerifyURL(String msgSignature, String timeStamp, String nonce, String echoStr)
            throws AesException {
        String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr);

        if (!signature.equals(msgSignature)) {
            throw new AesException(AesException.ValidateSignatureError);
        }

        String result = decrypt(echoStr);
        return result;
    }


    /**
     * @desc :微信公眾號  驗證url
     *  
     * @param msgSignature  簽名串,對應URL參數的msg_signature
     * @param token 公眾平台上,開發者設置的token
     * @param timeStamp   時間戳,對應URL參數的timestamp
     * @param nonce 隨機數,對應URL參數的nonce
     * @param echoStr 隨機串,對應URL參數的echostr,在微信公眾號中是明文的,直接原樣返回給微信公眾平台官方服務器
     * @return
     *    String  驗證成功后,原樣返回echoStr
     * @throws AesException   執行失敗,請查看該異常的錯誤碼和具體的錯誤信息
     */
    public String verifyUrl_WXGZ(String msgSignature, String token , String timeStamp, String nonce,String echoStr) throws AesException {
        //1.進行SHA1加密
        String signature = SHA1.getSHA1_WXGZ(token, timeStamp, nonce);

        //2.驗證 token、timestamp、nonce進行SHA1加密生成的signature 是否與url傳過來msgSignature相同
        if (!signature.equals(msgSignature)) {
            throw new AesException(AesException.ValidateSignatureError);

        }
        //3.若不拋異常,則url驗證成功,原樣返回echoStr
        String result = echoStr;
        return result;
    }


}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM