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);
        }
    }
}

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;
        }
    }

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;                       //释放资源
            }
        }
    }

 四、代码实现

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";
    
    
  

    
}

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;  
    }  



}

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;
    }







}

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(); 



    }

}

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);
        }
    }
}

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;
    }


}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

Mac OS下的wifi自动破解工具xwifi

由于macOS下有两个缺陷,目前没有合适的wifi破解方案,于是作者写了这样一个支持macOS新版本系统下的wifi破解工具xwifi,可以在新版本macOS上...

6937
来自专栏james大数据架构

Validation of viewstate MAC failed 解决办法

大部分人都说是在页里或web.config里加EnableEventValidation="false" EnableViewStateMac="false" ...

25610
来自专栏精讲JAVA

Java 配合 mitmproxy HTTPS 抓包调试

今天对接接口,对方给的 Demo 和已有项目用的 HTTP 工具不是一个;后来出现人家的好使,我的死活不通的情况;无奈之下开始研究 Java 抓包,所以怕忘了记...

3092
来自专栏salesforce零基础学习

salesforce零基础学习(八十三)analytics:reportChart实现Dashboard(仪表盘)功能效果

项目中经常会用到Report以及Dashboard来分析汇总数据,Dashboard可以指定view as user,如果针对不同的用户需要显示其允许查看的数据...

2089
来自专栏优启梦

nginx反向代理http和https共同使用 双存在

不能自适应协议,也不支持协议变量,各种百度啊,两个钟头,测试了各种,都不适用宝塔,

1776
来自专栏我就是马云飞

RxJava2 实战(1) - 后台执行耗时操作,实时通知 UI 更新

最近刚好也在学习rxjava2,So,一起学习咯。 前言 接触RxJava2已经很久了,也看了网上的很多文章,发现基本都是在对RxJava的基本思想介绍之后,再...

4558
来自专栏FreeBuf

如何破解Ubuntu磁盘加密和用户密码?

在Positive Hack Days V论坛上,我快速展示了eCryptfs和密码破解的过程。我在使用了Ubuntu的加密home目录功能后萌生了解密的想法,...

62310
来自专栏小樱的经验随笔

利用 John the Ripper 破解用户登录密码

看到这个标题,想必大家都很好奇,John the Ripper 是个什么东西呢?如果直译其名字的话就是: John 的撕裂者(工具)。 相比大家都会觉得摸不着头...

2802
来自专栏农夫安全

【笔记】记录Cy牛的任意密码重置姿势

跟第三个有点类似,只判断了接收端和验证码是否一致,未判断接收端是否和用户匹配,因此修改接收端可达到重置目的

1762
来自专栏耕耘实录

Linux环境中,GitHub的配置使用极简教程

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢

1361

扫码关注云+社区

领取腾讯云代金券