Java微信公众平台开发_02_启用服务器配置

Posted shirayner

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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();  
                }  
            }  
        } 以上是关于Java微信公众平台开发_02_启用服务器配置的主要内容,如果未能解决你的问题,请参考以下文章

微信公众平台深度开发JAVA版第一季 19.微信猜数字活动1

PHP微信公众号开发常用功能

微信公众平台里的服务器配置设置是干啥用的

微信公众平台——基础配置——务器配置:PHP版

微信公众平台开发教程_点击事件处理

微信公众平台开发视频教程-02-基本配置/ 填写服务器配置