微信开发显示网址链接_微信开发 分享到微信群的链接 和实际的链接不一样 - CSDN
  • 个人github:https://github.com/qiilee 欢迎star概述原文链接:http://blog.csdn.net/u011506468/article/details/47305261 微信参考:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html ...

    个人github:https://github.com/qiilee  欢迎star

    概述

    原文链接:http://blog.csdn.net/u011506468/article/details/47305261 
    微信参考:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html 
    思路:此篇主要介绍如何在点击微信的菜单后获得用户的信息并跳转至该网页。 
    网页授权分为四步: 
    1. 引导用户进入授权页面同意授权,获取code 
    2. 通过code换取网页授权access_token(与基础支持中的access_token不同) 
    3. 如果需要,开发者可以刷新网页授权access_token,避免过期 
    4. 通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)

    配置授权回调域名

    如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。所以第一步是配置域名,在微信公众号的公众号设置中可以配置,域名是需要备案的。

    获取code

    接口请求为:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 
    redirect_uri为请求后重定向地址,也就是你要跳转至的网页地址,state为重定向后的参数。 
    scope的区别说明,有2种授权方式,根据自己的需要进行处理:

    • scope为snsapi_base,静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
    • scope为snsapi_userinfo,这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息

    获取网页授权的access_token

    获取code后,请求以下链接获取access_token,code为上一步得到的code: 
    https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

    代码说明

    新用户进来的时候是没有cookie的,而且type=2,首先是要授权,授权的代码在下面。这个时候可以给其设置一个cookie,设置存活时间为10小时。授权完成后,还是会重定向进入这个方法来处理,只是type变化,这个时候进入测试或者正式环境,根据参数menuType进行判断是哪个目录被点击,然后进入相对应的页面。若cookie不为空,则直接跳转测试或者正式环境相对应的页面。


    /**
         * 
         * @param type 0-测试, 1-正式, 2-跳转获取CODE,3:认证过的测试号
         * @param menuType
         * @param request
         * @param wechatUserId
         * @param response
         * @return
         */
        @RequestMapping("/view")
        public ModelAndView view(Integer type,Integer menuType, Integer wechatUserId, String redirect,HttpServletRequest request, HttpServletResponse response) 
        {
            Cookie cookie = CookieUtil.getCookieByName(request, "wechatUserId");
            log.info("type:" + type + ",menuType:" + menuType + ",wechatUserId:" + wechatUserId + ",redirect:" + redirect);
    
            String url  = null;
            if(cookie == null)
            {
                log.info("Cookie已过期.....");
                if(type == 0)
                {
                    CookieUtil.addCookie(response, "wechatUserId", Randoms.getInt(1, 53)+"", 60 * 10);       /* 测试环境 */
                    url = "view?format=json&type=0&menuType=" + menuType + "&redirect=" + redirect;
                    log.info("url:" + url);
                    return new ModelAndView(new RedirectView(url));
                }
                else if(type == 1)
                {
                    CookieUtil.addCookie(response, "wechatUserId", wechatUserId+"", (60 * 60 * 10)); 
                    /* 生产环境 */
                    url = "view?format=json&type=1&menuType=" + menuType  + "&redirect=" + redirect;
                    log.info("url:" + url);
                    return new ModelAndView(new RedirectView(url));
                }
                else if(type == 2)
                {
                    String wechatRedirece = UrlUtil.encode(wechatConfig.getHOST() + "wechat/user/auth?format=json&type=1&menuType=" + menuType + "&redirect=" + redirect);
                    /**
                     * 授权的链接 
                     * 注意redirect_uri为重定向地址,/auth在下面的代码中
                     * public String getAUTHORIZE_URL() {
                     * return "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+getAPPID() +"&redirect_uri=";
        }
                     */ 
                    url = wechatConfig.getAUTHORIZE_URL() + wechatRedirece + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
                    log.info("url:" + url);
                    return new ModelAndView(new RedirectView(url));
                }
                else
                {
                    return new ModelAndView(new RedirectView(url));
                }
            }
            else
            {
                log.info("Cookie未过期.....");
                if(type == 0)
                {
                    switch (menuType) 
                    {
                        case 0:
                            url = AESCryptoSecurity.decrypt(redirect, wechatConfig.getAPPID());
                            break;
                        case 1:
                            //社区
                            url = wechatConfig.getHOST_FRONT() + "page/topicList.html";
                            break;
                        case 2:
                            //活动
                            url = wechatConfig.getHOST_FRONT() + "page/activityList.html";
                            break;
                    }
                }
                else
                {
                        switch (menuType) 
                        {
                            case 0:
                                url = AESCryptoSecurity.decrypt(redirect, wechatConfig.getAPPID());
                                break;
                            case 1:
                                //社区
                                url = wechatConfig.getHOST_FRONT() + "page/topicList.html";
                                break;
                            case 2:
                                //活动
                                url = wechatConfig.getHOST_FRONT() + "page/activityList.html";
                                break;
                        }
                }
                return new ModelAndView(new RedirectView(url));
            }
        }

    下面的代码为获取code,获取access_token,获取用户信息等,认证完跳转至对应的页面

    @RequestMapping("/auth")
        public ModelAndView auth(String code, Integer type, Integer menuType, String redirect) throws Exception
        {
            log.info("code:" + code + ",type:" + type + ",menuType:" + menuType);
            /* 向微信发请求获取access_token */
            Map<String, Object> map = wechatUserService.getPageAccessToken(code);
            /* 向微信发请求,用access_token获取用户信息并保存 */
            WechatUser pageWechatUser = wechatUserService.getPageWechatUser(map.get("access_token").toString(), map.get("openid").toString());
            String url  = null;
            if(type == 1)
            {
                /* 权限认证完成后,将type改为1或者0,重定向进入上面的方法进行页面跳转 */
                url = wechatConfig.getHOST() + "wechat/menu/view?&type=1&menuType=" + menuType + "&wechatUserId=" + pageWechatUser.getWechatId() + "&redirect=" + redirect;
                log.info("url:" + url);
            }
            return new ModelAndView(new RedirectView(url));
        }




    展开全文
  • 贴几个不错的链接: ... 有一个问题一直无法解决,就是在...在微信开发的过程中遇到了分享时自定义标题和图片的需求,通过微信分享的链接会出现无法指定标题和说明以及图片。 下面贴出代码: package com.ruhr.o...

    贴几个不错的链接:

    http://www.cnblogs.com/backtozero/p/7064247.html

    有一个问题一直无法解决,就是在本地测试的时候,除非前后端代码在一起,不然通过内网映射以后,前端代码无法使用。

    在微信开发的过程中遇到了分享时自定义标题和图片的需求,通过微信分享的链接会出现无法指定标题和说明以及图片。

    下面贴出代码:

    package com.ruhr.ow.module.vote.util;
    
    import com.alibaba.fastjson.JSONObject;
    import com.ruhr.core.data.mysql.multiple.logging.LogUtils;
    import com.ruhr.core.utils.StringUtils;
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.UnsupportedEncodingException;
    import java.net.ConnectException;
    import java.net.URL;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Formatter;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 获取微信帮助类.
     *
     * @author yi wei
     * @date create in 2019/1/17 19:32  *
     **/
    @RestController
    public class WeChatUtil {
    
      private Logger log = LogUtils.getLogger(WeChatUtil.class);
    
      private static final String APPID = "wx3b8fa9fddd320ab6";
    
      private static final String APP_SECRET = "9213714edeebde5a22e37db96dda9f82";
    
    
      private String accessToken;
      private String jsApiTicket;
      private Long getTiketTime = 0L;
      private Long getTokenTime = 0L;
      private Long tokenExpireTime = 0L;
      private Long ticketExpireTime = 0L;
    
      /**
       * 获取微信参数.
       **/
      @RequestMapping("/wechatParam")
      @ResponseBody
      public Map<String, String> getWechatParam(String url) {
        //当前时间
        long now = System.currentTimeMillis();
        log.info("currentTime====>" + now + "ms");
    
        //判断accessToken是否已经存在或者token是否过期
        if (StringUtils.isBlank(accessToken) || (now - getTokenTime > tokenExpireTime * 1000)) {
          JSONObject tokenInfo = getAccessToken();
          if (tokenInfo != null) {
            log.info("tokenInfo====>" + tokenInfo.toJSONString());
            accessToken = tokenInfo.getString("access_token");
            tokenExpireTime = tokenInfo.getLongValue("expires_in");
            //获取token的时间
            getTokenTime = System.currentTimeMillis();
            log.info("accessToken====>" + accessToken);
            log.info("tokenExpireTime====>" + tokenExpireTime + "s");
            log.info("getTokenTime====>" + getTokenTime + "ms");
          } else {
            log.info("====>tokenInfo is null~");
            log.info("====>failure of getting tokenInfo,please do some check~");
          }
    
        }
    
        //判断jsApiTicket是否已经存在或者是否过期
        if (StringUtils.isBlank(jsApiTicket) || (now - getTiketTime > ticketExpireTime * 1000)) {
          JSONObject ticketInfo = getJsApiTicket();
          if (ticketInfo != null) {
            log.info("ticketInfo====>" + ticketInfo.toJSONString());
            jsApiTicket = ticketInfo.getString("ticket");
            ticketExpireTime = ticketInfo.getLongValue("expires_in");
            getTiketTime = System.currentTimeMillis();
            log.info("jsApiTicket====>" + jsApiTicket);
            log.info("ticketExpireTime====>" + ticketExpireTime + "s");
            log.info("getTiketTime====>" + getTiketTime + "ms");
          } else {
            log.info("====>ticketInfo is null~");
            log.info("====>failure of getting tokenInfo,please do some check~");
          }
        }
    
        //生成微信权限验证的参数
        Map<String, String> wechatParam = makeWXTicket(jsApiTicket, url);
        return wechatParam;
      }
    
      private JSONObject getAccessToken() {
        String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        String requestUrl = accessTokenUrl.replace("APPID", APPID).replace("APPSECRET", APP_SECRET);
        log.info("getAccessToken.requestUrl====>" + requestUrl);
        return httpsRequest(requestUrl, "GET", null);
      }
    
      private JSONObject getJsApiTicket() {
        String apiTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
        String requestUrl = apiTicketUrl.replace("ACCESS_TOKEN", accessToken);
        log.info("getJsApiTicket.requestUrl====>" + requestUrl);
        return httpsRequest(requestUrl, "GET", null);
      }
    
      //生成微信权限验证的参数
      public Map<String, String> makeWXTicket(String jsApiTicket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonceStr = createNonceStr();
        String timestamp = createTimestamp();
        String string1;
        String signature = "";
    
        //注意这里参数名必须全部小写,且必须有序
        string1 = "jsapi_ticket=" + jsApiTicket +
            "&noncestr=" + nonceStr +
            "&timestamp=" + timestamp +
            "&url=" + url;
        log.info("String1=====>" + string1);
        try {
          MessageDigest crypt = MessageDigest.getInstance("SHA-1");
          crypt.reset();
          crypt.update(string1.getBytes("UTF-8"));
          signature = byteToHex(crypt.digest());
          log.info("signature=====>" + signature);
        } catch (NoSuchAlgorithmException e) {
          log.error("WeChatController.makeWXTicket=====Start");
          log.error(e.getMessage(), e);
          log.error("WeChatController.makeWXTicket=====End");
        } catch (UnsupportedEncodingException e) {
          log.error("WeChatController.makeWXTicket=====Start");
          log.error(e.getMessage(), e);
          log.error("WeChatController.makeWXTicket=====End");
        }
    
        ret.put("url", url);
        ret.put("jsapi_ticket", jsApiTicket);
        ret.put("nonceStr", nonceStr);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
        ret.put("appid", APPID);
    
        return ret;
      }
    
      private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
          formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
      }
    
      /**
       * 发送https请求.
       *
       * @param requestUrl 请求地址
       * @param requestMethod 请求方式(GET、POST)
       * @param outputStr 提交的数据
       * @return JSONObject(通过JSONObject.get ( key)的方式获取json对象的属性值)
       */
      private JSONObject httpsRequest(String requestUrl, String requestMethod,
          String outputStr) {
        JSONObject jsonObject = null;
        try {
          // 创建SSLContext对象,并使用我们指定的信任管理器初始化
          TrustManager[] tm = {new MyX509TrustManagerUtil()};
          SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
          sslContext.init(null, tm, new java.security.SecureRandom());
          // 从上述SSLContext对象中得到SSLSocketFactory对象
          SSLSocketFactory ssf = sslContext.getSocketFactory();
    
          URL url = new URL(requestUrl);
          HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
          conn.setSSLSocketFactory(ssf);
    
          conn.setDoOutput(true);
          conn.setDoInput(true);
          conn.setUseCaches(false);
          // 设置请求方式(GET/POST)
          conn.setRequestMethod(requestMethod);
    
          // 当outputStr不为null时向输出流写数据
          if (null != outputStr) {
            OutputStream outputStream = conn.getOutputStream();
            // 注意编码格式
            outputStream.write(outputStr.getBytes("UTF-8"));
            outputStream.close();
          }
    
          // 从输入流读取返回内容
          InputStream inputStream = conn.getInputStream();
          InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
          BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
          String str;
          StringBuffer buffer = new StringBuffer();
          while ((str = bufferedReader.readLine()) != null) {
            buffer.append(str);
          }
    
          // 释放资源
          bufferedReader.close();
          inputStreamReader.close();
          inputStream.close();
          conn.disconnect();
          jsonObject = JSONObject.parseObject(buffer.toString());
        } catch (ConnectException ce) {
          log.error("连接超时:{}", ce);
        } catch (Exception e) {
          log.error("https请求异常:{}", e);
        }
        return jsonObject;
      }
    
    
      //生成随机字符串
      private static String createNonceStr() {
        return UUID.randomUUID().toString();
      }
    
      //生成时间戳
      private static String createTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
      }
    
    
    }
    
    package com.ruhr.ow.module.vote.util;
    
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import javax.net.ssl.X509TrustManager;
    
    /**
     * @Author:yiwei@ruhrtec.cn
     * @Date: create in 2017/10/18 17:12
     * 信任管理器:用于获取acces_token的帮助类(不能删除)
     **/
    public class MyX509TrustManagerUtil implements X509TrustManager{
        // 检查客户端证书
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }
    
        // 检查服务器端证书
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }
    
        // 返回受信任的X509证书数组
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }
    

     

    展开全文
  • 如图,图1是其它游戏分享链接显示效果(前提是在微信中打开并分享出去),图2是我们游戏页面(同样在微信中打开并分享出去)分享链接显示效果。 要达到图1的自定义文案与图标的效果,经研究与各种尝试,得到的...

    一、需求描述

    如图,图1是其它游戏分享链接的显示效果(前提是在微信中打开并分享出去),图2是我们游戏页面(同样在微信中打开并分享出去)分享链接的显示效果。

    要达到图1的自定义文案与图标的效果,经研究与各种尝试,得到的最佳解决方案为采用微信提供的JS-SDK,微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。

     

    二、解决方案

    1、如图,根据微信JS-SDK的接口文档说明,首先需要有一个为认证号的企业号,这样才有权限调用分享接口。

     

    2、如图,绑定域名,登录微信公众平台,进入“公众号设置”的功能设置里面填写js接口安全域名,这个是要填写的是你微信浏览器要打开的域名地址。不能添加IP地址。

    3、前端注入js,并调用jssdk所提供的分享接口,其中需要后端提供一个接口服务来实现生成签名等必要的参数( timestamp, noceStr,singature)。

    // 微信分享链接自定义
                const head = document.head
                const script = document.createElement('script')
                script.src = 'https://res.wx.qq.com/open/js/jweixin-1.0.0.js'
                head.appendChild(script)
                var thisUrl = window.location.href
                rootState.http.post(SIGN_WECHAT_JS_SDK, {url: thisUrl}).then((res) => {
                    if (res.code === '200' && res.data && wx) {
                        // res.data = {
                        //     "noncestr":"fb5e70b3-d065-4a61-b",
                        //     "jsapi_ticket":"kgt8ON7yVITDhtdwci0qeXRbC6q_7G2lYHoLfD0A3G4YCppaM5umcoE7ZhFGHIT-5cnzUJ4c5Y6ba_mu4sHNbA",
                        //     "timestamp":1531825770,
                        //     "url":"http://cat.youximao.tv/view/gamePage/1529489187009439/2403296/detail",
                        //     "signature":"4d643a79184a95aecf9d24051f7c1cdf99b52ff2",
                        //     "appId":"wx91f15555be5d81f8"
                        // }
                        wx.config({
                            debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                            appId: res.data.appId, // 必填,公众号的唯一标识
                            timestamp: res.data.timestamp, // 必填,生成签名的时间戳
                            nonceStr: res.data.noncestr, // 必填,生成签名的随机串
                            signature: res.data.signature, // 必填,签名
                            jsApiList: [
                            "onMenuShareTimeline","onMenuShareAppMessage","onMenuShareQQ","onMenuShareQZone"
                            ] // 必填,需要使用的JS接口列表
                        })
    
                        // 自定义文案及图片
                        var title = data.gameDetail.gameName
                        var link = thisUrl
                        var imgUrl = data.gameDetail.icon1
                        var desc = data.gameDetail.gameSummary
                        wx.ready(function () {
                            wx.onMenuShareTimeline({
                                title: title, // 分享标题
                                link: link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                                imgUrl: imgUrl, // 分享图标
                                success: function () {
                                    // 用户确认分享后执行的回调函数
                                },
                                cancel: function () {
                                    // 用户取消分享后执行的回调函数
                                }
                            })
                            wx.onMenuShareAppMessage({
                                title: title, // 分享标题
                                desc: desc, // 分享描述
                                link: link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                                imgUrl: imgUrl, // 分享图标
                                type: '', // 分享类型,music、video或link,不填默认为link
                                dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
                                success: function () {
                                    // 用户确认分享后执行的回调函数
                                },
                                cancel: function () {
                                    // 用户取消分享后执行的回调函数
                                }
                            })
                            wx.onMenuShareQQ({
                                title: title, // 分享标题
                                desc: desc, // 分享描述
                                link: link, // 分享链接
                                imgUrl: imgUrl, // 分享图标
                                success: function () {
                                    // 用户确认分享后执行的回调函数
                                },
                                cancel: function () {
                                    // 用户取消分享后执行的回调函数
                                }
                            })
                            wx.onMenuShareQZone({
                                title: title, // 分享标题
                                desc: desc, // 分享描述
                                link: link, // 分享链接
                                imgUrl: imgUrl, // 分享图标
                                success: function () {
                                    // 用户确认分享后执行的回调函数
                                },
                                cancel: function () {
                                    // 用户取消分享后执行的回调函数
                                }
                            })
                        })
                    }
                }).catch((error) => {
                    // alert('SIGN_WECHAT_JS_SDK is error')
                    // commit(SHOW_ERROR_TOAST, error)
                })

    具体实现请参考:网页分享到微信自定义标题,内容和图片

    4、参阅资料

    JS-SDK文档:微信JS-SDK说明文档

     

    展开全文
  • 上一篇《微信开发学习总结(一)——微信开发环境搭建》我们已经完成了微信开发的准备工作,准备工作完成之后,就要开始步入正题了。 一、微信公众平台的基本原理  在开始做之前,先简单介绍了微信公众平台的基本...

    上一篇《微信开发学习总结(一)——微信开发环境搭建》我们已经完成了微信开发的准备工作,准备工作完成之后,就要开始步入正题了。

    一、微信公众平台的基本原理

      在开始做之前,先简单介绍了微信公众平台的基本原理。

      微信服务器就相当于一个转发服务器,终端(手机、Pad等)发起请求至微信服务器,微信服务器然后将请求转发给我们的应用服务器。应用服务器处理完毕后,将响应数据回发给微信服务器,微信服务器再将具体响应信息回复到微信App终端。

      通信协议为:HTTP

      数据传输格式为:XML

      具体的流程如下图所示:

      

      来一张更加直观的图吧:

      

      我们需要做的事情,就是对微信服务器转发的HTTP请求做出响应。具体的请求内容,我们按照特定的XML格式去解析,处理完毕后,也要按照特定的XML格式返回。

    二、微信公众号接入

      在微信公众平台开发者文档上,关于公众号接入这一节内容在接入指南上写的比较详细的,文档中说接入公众号需要3个步骤,分别是:

      1、填写服务器配置
      2、验证服务器地址的有效性
      3、依据接口文档实现业务逻辑

      其实,第3步已经不能算做公众号接入的步骤,而是接入之后,开发人员可以根据微信公众号提供的接口所能做的一些开发。

      第1步中服务器配置包含服务器地址(URL)、Token和EncodingAESKey。

      服务器地址即公众号后台提供业务逻辑的入口地址,目前只支持80端口,之后包括接入验证以及任何其它的操作的请求(例如消息的发送、菜单管理、素材管理等)都要从这个地址进入。接入验证和其它请求的区别就是,接入验证时是get请求,其它时候是post请求;

      Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性);

      EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。本例中全部以未加密的明文消息方式,不涉及此配置项。

      第2步,验证服务器地址的有效性,当点击“提交”按钮后,微信服务器将发送一个http的get请求到刚刚填写的服务器地址,并且携带四个参数:

      

      接到请求后,我们需要做如下三步,若确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效,否则接入失败。

      1. 将token、timestamp、nonce三个参数进行字典序排序
      2. 将三个参数字符串拼接成一个字符串进行sha1加密
      3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

      下面我们用Java代码来演示一下这个验证过程

      使用IDE(Eclipse或者IntelliJ IDEA)创建一个JavaWeb项目,这里我使用的是IntelliJ IDEA,项目目录结构如下图所示:

      

      编写一个servlevt,在其中的doGet方法中定义校验方法,具体代码如下:

    复制代码
      1 package me.gacl.wx.web.servlet;
      2 
      3 import javax.servlet.ServletException;
      4 import javax.servlet.annotation.WebServlet;
      5 import javax.servlet.http.HttpServlet;
      6 import javax.servlet.http.HttpServletRequest;
      7 import javax.servlet.http.HttpServletResponse;
      8 import java.io.IOException;
      9 import java.security.MessageDigest;
     10 import java.security.NoSuchAlgorithmException;
     11 import java.util.Arrays;
     12 
     13 /**
     14  * Created by xdp on 2016/1/25.
     15  * 使用@WebServlet注解配置WxServlet,urlPatterns属性指明了WxServlet的访问路径
     16  */
     17 @WebServlet(urlPatterns="/WxServlet")
     18 public class WxServlet extends HttpServlet {
     19 
     20     /**
     21      * Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)
     22      * 比如这里我将Token设置为gacl
     23      */
     24     private final String TOKEN = "gacl";
     25 
     26     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     27 
     28     }
     29 
     30     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     31         System.out.println("开始校验签名");
     32         /**
     33          * 接收微信服务器发送请求时传递过来的4个参数
     34          */
     35         String signature = request.getParameter("signature");//微信加密签名signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
     36         String timestamp = request.getParameter("timestamp");//时间戳
     37         String nonce = request.getParameter("nonce");//随机数
     38         String echostr = request.getParameter("echostr");//随机字符串
     39         //排序
     40         String sortString = sort(TOKEN, timestamp, nonce);
     41         //加密
     42         String mySignature = sha1(sortString);
     43         //校验签名
     44         if (mySignature != null && mySignature != "" && mySignature.equals(signature)) {
     45             System.out.println("签名校验通过。");
     46             //如果检验成功输出echostr,微信服务器接收到此输出,才会确认检验完成。
     47             //response.getWriter().println(echostr);
     48             response.getWriter().write(echostr);
     49         } else {
     50             System.out.println("签名校验失败.");
     51         }
     52 
     53     }
     54 
     55     /**
     56      * 排序方法
     57      *
     58      * @param token
     59      * @param timestamp
     60      * @param nonce
     61      * @return
     62      */
     63     public String sort(String token, String timestamp, String nonce) {
     64         String[] strArray = {token, timestamp, nonce};
     65         Arrays.sort(strArray);
     66         StringBuilder sb = new StringBuilder();
     67         for (String str : strArray) {
     68             sb.append(str);
     69         }
     70 
     71         return sb.toString();
     72     }
     73 
     74     /**
     75      * 将字符串进行sha1加密
     76      *
     77      * @param str 需要加密的字符串
     78      * @return 加密后的内容
     79      */
     80     public String sha1(String str) {
     81         try {
     82             MessageDigest digest = MessageDigest.getInstance("SHA-1");
     83             digest.update(str.getBytes());
     84             byte messageDigest[] = digest.digest();
     85             // Create Hex String
     86             StringBuffer hexString = new StringBuffer();
     87             // 字节数组转换为 十六进制 数
     88             for (int i = 0; i < messageDigest.length; i++) {
     89                 String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
     90                 if (shaHex.length() < 2) {
     91                     hexString.append(0);
     92                 }
     93                 hexString.append(shaHex);
     94             }
     95             return hexString.toString();
     96 
     97         } catch (NoSuchAlgorithmException e) {
     98             e.printStackTrace();
     99         }
    100         return "";
    101     }
    102 }
    复制代码

      我这里用的Servlet3.0,使用Servlet3.0的好处就是可以直接使用@WebServlet注解映射Servlet的访问路径,不再需要在web.xml文件中进行配置.

      将WxStudy项目部署到Tomcat服务器中运行,直接启动项目,然后用ngrok将本地8080端口映射到外网(如何使用ngrok请参考博客《微信开发学习总结(一)——微信开发环境搭建》)。如下图所示:

      

      测试是否可以通过http://xdp.ngrok.natapp.cn地址正常访问,测试结果如下:

      

      可以看到,我们的项目已经可以被外网正常访问到了。

      进入微信测试公众号管理界面,在接口配置信息中填入映射的外网地址和token,如下图所示:

     

      点击提交按钮,页面会提示配置成功,

      

      IDE的控制台中输出了校验通过的信息,如下图所示:

      

      到此,我们的公众号应用已经能够和微信服务器正常通信了,也就是说我们的公众号已经接入到微信公众平台了。

    三、access_token管理

    3.1、access_token介绍

      我们的公众号和微信服务器对接成功之后,接下来要做的就是根据我们的业务需求调用微信公众号提供的接口来实现相应的逻辑了。在使用微信公众号接口中都需要一个access_token。

      关于access_token,在微信公众平台开发者文档上的获取接口调用凭据有比较详细的介绍:access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token,开发者需要妥善保存access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。并且每天调用获取access_token接口的上限是2000次。

      总结以上说明,access_token需要做到以下两点:

      1.因为access_token有2个小时的时效性,要有一个机制保证最长2个小时重新获取一次。

      2.因为接口调用上限每天2000次,所以不能调用太频繁。

    3.2、微信公众平台提供的获取access_token的接口

      关于access_token的获取方式,在微信公众平台开发者文档上有说明,公众号可以调用一个叫"获取access token"的接口来获取access_token。

      获取access token接口调用请求说明

        http请求方式: GET

        请求的URL地址:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
        
      

      我们可以看到,调用过程中需要传递appID和AppSecret,appID和AppSecret是在申请公众号的时候自动分配给公众号的,相当于公众号的身份标示,使用微信公众号的注册帐号登录到腾讯提供的微信公众号管理后台就可以看到自己申请的公众号的AppID和AppSecret,如下图所示:

      

      这是我申请公众号测试帐号时分配到的AppID和AppSecret。

    3.3、获取access_token方案以及具体实现

      这里采用的方案是这样的,定义一个默认启动的servlet,在init方法中启动一个Thread,这个进程中定义一个无限循环的方法,用来获取access_token,当获取成功后,此进程休眠7000秒(7000秒=1.944444444444444小时),否则休眠3秒钟继续获取。流程图如下:

      

      下面正式开始在工程中实现以上思路,因为返回的数据都是json格式,这里会用到阿里的fastjson库,为构造请求和处理请求后的数据序列化和反序列化提供支持。

      1.定义一个AccessToken实体类

    复制代码
     1 package me.gacl.wx.entry;
     2 
     3 /**
     4  * AccessToken的数据模型
     5  * Created by xdp on 2016/1/25.
     6  */
     7 public class AccessToken {
     8 
     9     //获取到的凭证
    10     private String accessToken;
    11     //凭证有效时间,单位:秒
    12     private int expiresin;
    13 
    14     public String getAccessToken() {
    15         return accessToken;
    16     }
    17 
    18     public void setAccessToken(String accessToken) {
    19         this.accessToken = accessToken;
    20     }
    21 
    22     public int getExpiresin() {
    23         return expiresin;
    24     }
    25 
    26     public void setExpiresin(int expiresin) {
    27         this.expiresin = expiresin;
    28     }
    29 }
    复制代码

     2.定义一个AccessTokenInfo类,用于存放获取到的AccessToken,代码如下:

    复制代码
     1 package me.gacl.wx.Common;
     2 
     3 import me.gacl.wx.entry.AccessToken;
     4 
     5 /**
     6  * Created by xdp on 2016/1/25.
     7  */
     8 public class AccessTokenInfo {
     9 
    10     //注意是静态的
    11     public static AccessToken accessToken = null;
    12 }
    复制代码

      3.编写一个用于发起https请求的工具类NetWorkHelper,代码如下:

    复制代码
     1 package me.gacl.wx.util;
     2 
     3 import javax.net.ssl.*;
     4 import java.io.BufferedReader;
     5 import java.io.InputStream;
     6 import java.io.InputStreamReader;
     7 import java.net.URL;
     8 import java.security.cert.CertificateException;
     9 import java.security.cert.X509Certificate;
    10 
    11 /**
    12  * 访问网络用到的工具类
    13  */
    14 public class NetWorkHelper {
    15 
    16     /**
    17      * 发起Https请求
    18      * @param reqUrl 请求的URL地址
    19      * @param requestMethod
    20      * @return 响应后的字符串
    21      */
    22     public String getHttpsResponse(String reqUrl, String requestMethod) {
    23         URL url;
    24         InputStream is;
    25         String resultData = "";
    26         try {
    27             url = new URL(reqUrl);
    28             HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
    29             TrustManager[] tm = {xtm};
    30 
    31             SSLContext ctx = SSLContext.getInstance("TLS");
    32             ctx.init(null, tm, null);
    33 
    34             con.setSSLSocketFactory(ctx.getSocketFactory());
    35             con.setHostnameVerifier(new HostnameVerifier() {
    36                 @Override
    37                 public boolean verify(String arg0, SSLSession arg1) {
    38                     return true;
    39                 }
    40             });
    41 
    42 
    43             con.setDoInput(true); //允许输入流,即允许下载
    44 
    45             //在android中必须将此项设置为false
    46             con.setDoOutput(false); //允许输出流,即允许上传
    47             con.setUseCaches(false); //不使用缓冲
    48             if (null != requestMethod && !requestMethod.equals("")) {
    49                 con.setRequestMethod(requestMethod); //使用指定的方式
    50             } else {
    51                 con.setRequestMethod("GET"); //使用get请求
    52             }
    53             is = con.getInputStream();   //获取输入流,此时才真正建立链接
    54             InputStreamReader isr = new InputStreamReader(is);
    55             BufferedReader bufferReader = new BufferedReader(isr);
    56             String inputLine;
    57             while ((inputLine = bufferReader.readLine()) != null) {
    58                 resultData += inputLine + "\n";
    59             }
    60             System.out.println(resultData);
    61 
    62         } catch (Exception e) {
    63             e.printStackTrace();
    64         }
    65         return resultData;
    66     }
    67 
    68     X509TrustManager xtm = new X509TrustManager() {
    69         @Override
    70         public X509Certificate[] getAcceptedIssuers() {
    71             return null;
    72         }
    73 
    74         @Override
    75         public void checkServerTrusted(X509Certificate[] arg0, String arg1)
    76                 throws CertificateException {
    77 
    78         }
    79 
    80         @Override
    81         public void checkClientTrusted(X509Certificate[] arg0, String arg1)
    82                 throws CertificateException {
    83 
    84         }
    85     };
    86 }
    复制代码

      getHttpsResponse方法是请求一个https地址,参数requestMethod为字符串“GET”或者“POST”,传null或者“”默认为get方式。

      4.定义一个默认启动的servlet,在init方法中启动一个新的线程去获取accessToken

    复制代码
     1 package me.gacl.wx.web.servlet;
     2 
     3 import com.alibaba.fastjson.JSON;
     4 import com.alibaba.fastjson.JSONObject;
     5 import me.gacl.wx.Common.AccessTokenInfo;
     6 import me.gacl.wx.entry.AccessToken;
     7 import me.gacl.wx.util.NetWorkHelper;
     8 
     9 import javax.servlet.ServletException;
    10 import javax.servlet.annotation.WebInitParam;
    11 import javax.servlet.annotation.WebServlet;
    12 import javax.servlet.http.HttpServlet;
    13 
    14 /**
    15  * 用于获取accessToken的Servlet
    16  * Created by xdp on 2016/1/25.
    17  */
    18 @WebServlet(
    19         name = "AccessTokenServlet",
    20         urlPatterns = {"/AccessTokenServlet"},
    21         loadOnStartup = 1,
    22         initParams = {
    23                 @WebInitParam(name = "appId", value = "wxbe4d433e857e8bb1"),
    24                 @WebInitParam(name = "appSecret", value = "ccbc82d560876711027b3d43a6f2ebda")
    25         })
    26 public class AccessTokenServlet extends HttpServlet {
    27 
    28     @Override
    29     public void init() throws ServletException {
    30         System.out.println("启动WebServlet");
    31         super.init();
    32 
    33         final String appId = getInitParameter("appId");
    34         final String appSecret = getInitParameter("appSecret");
    35 
    36         //开启一个新的线程
    37         new Thread(new Runnable() {
    38             @Override
    39             public void run() {
    40                 while (true) {
    41                     try {
    42                         //获取accessToken
    43                         AccessTokenInfo.accessToken = getAccessToken(appId, appSecret);
    44                         //获取成功
    45                         if (AccessTokenInfo.accessToken != null) {
    46                             //获取到access_token 休眠7000秒,大约2个小时左右
    47                             Thread.sleep(7000 * 1000);
    48                             //Thread.sleep(10 * 1000);//10秒钟获取一次
    49                         } else {
    50                             //获取失败
    51                             Thread.sleep(1000 * 3); //获取的access_token为空 休眠3秒
    52                         }
    53                     } catch (Exception e) {
    54                         System.out.println("发生异常:" + e.getMessage());
    55                         e.printStackTrace();
    56                         try {
    57                             Thread.sleep(1000 * 10); //发生异常休眠1秒
    58                         } catch (Exception e1) {
    59 
    60                         }
    61                     }
    62                 }
    63 
    64             }
    65         }).start();
    66     }
    67 
    68     /**
    69      * 获取access_token
    70      *
    71      * @return AccessToken
    72      */
    73     private AccessToken getAccessToken(String appId, String appSecret) {
    74         NetWorkHelper netHelper = new NetWorkHelper();
    75         /**
    76          * 接口地址为https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET,其中grant_type固定写为client_credential即可。
    77          */
    78         String Url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, appSecret);
    79         //此请求为https的get请求,返回的数据格式为{"access_token":"ACCESS_TOKEN","expires_in":7200}
    80         String result = netHelper.getHttpsResponse(Url, "");
    81         System.out.println("获取到的access_token="+result);
    82         //使用FastJson将Json字符串解析成Json对象
    83         JSONObject json = JSON.parseObject(result);
    84         AccessToken token = new AccessToken();
    85         token.setAccessToken(json.getString("access_token"));
    86         token.setExpiresin(json.getInteger("expires_in"));
    87         return token;
    88     }
    89 }
    复制代码

      AccessTokenServlet采用注解的方式进行配置
      至此代码实现完毕,将项目部署,看到控制台输出如下:

      

      为了方便看效果,可以把休眠时间设置短一点,比如10秒获取一次,然后将access_token输出。

      下面做一个测试jsp页面,并把休眠时间设置为10秒,这样过10秒刷新页面,就可以看到变化

    复制代码
     1 <%-- Created by IntelliJ IDEA. --%>
     2 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
     3 <%@ page import="me.gacl.wx.Common.AccessTokenInfo"%>
     4 <html>
     5   <head>
     6     <title></title>
     7   </head>
     8   <body>
     9     微信学习
    10     <hr/>
    11     access_token为:<%=AccessTokenInfo.accessToken.getAccessToken()%>
    12   </body>
    13 </html>
    复制代码

      

      10秒钟后刷新页面,access_token变了,如下图所示:

      

    四、接收微信服务器发送的消息并做出响应

      经过上述的三步,我们开发前的准备工作已经完成了,接下来要做的就是接收微信服务器发送的消息并做出响应

      从微信公众平台接口消息指南中可以了解到,当用户向公众帐号发消息时,微信服务器会将消息通过POST方式提交给我们在接口配置信息中填写的URL,而我们就需要在URL所指向的请求处理类WxServlet的doPost方法中接收消息、处理消息和响应消息。

    4.1.编写一个用于处理消息的工具类

      编写处理消息的工具栏,工具类代码如下:

    复制代码
      1 package me.gacl.wx.util;
      2 
      3 import org.dom4j.Document;
      4 import org.dom4j.Element;
      5 import org.dom4j.io.SAXReader;
      6 
      7 import javax.servlet.http.HttpServletRequest;
      8 import java.io.InputStream;
      9 import java.text.DateFormat;
     10 import java.text.SimpleDateFormat;
     11 import java.util.Date;
     12 import java.util.HashMap;
     13 import java.util.List;
     14 import java.util.Map;
     15 
     16 /**
     17  * 消息处理工具类
     18  * Created by xdp on 2016/1/26.
     19  */
     20 public class MessageHandlerUtil {
     21 
     22     /**
     23      * 解析微信发来的请求(XML)
     24      * @param request
     25      * @return map
     26      * @throws Exception
     27      */
     28     public static Map<String,String> parseXml(HttpServletRequest request) throws Exception {
     29         // 将解析结果存储在HashMap中
     30         Map<String,String> map = new HashMap();
     31         // 从request中取得输入流
     32         InputStream inputStream = request.getInputStream();
     33         System.out.println("获取输入流");
     34         // 读取输入流
     35         SAXReader reader = new SAXReader();
     36         Document document = reader.read(inputStream);
     37         // 得到xml根元素
     38         Element root = document.getRootElement();
     39         // 得到根元素的所有子节点
     40         List<Element> elementList = root.elements();
     41 
     42         // 遍历所有子节点
     43         for (Element e : elementList) {
     44             System.out.println(e.getName() + "|" + e.getText());
     45             map.put(e.getName(), e.getText());
     46         }
     47 
     48         // 释放资源
     49         inputStream.close();
     50         inputStream = null;
     51         return map;
     52     }
     53 
     54     // 根据消息类型 构造返回消息
     55     public static String buildXml(Map<String,String> map) {
     56         String result;
     57         String msgType = map.get("MsgType").toString();
     58         System.out.println("MsgType:" + msgType);
     59         if(msgType.toUpperCase().equals("TEXT")){
     60             result = buildTextMessage(map, "孤傲苍狼在学习和总结微信开发了,构建一条文本消息:Hello World!");
     61         }else{
     62             String fromUserName = map.get("FromUserName");
     63             // 开发者微信号
     64             String toUserName = map.get("ToUserName");
     65             result = String
     66                     .format(
     67                             "<xml>" +
     68                                     "<ToUserName><![CDATA[%s]]></ToUserName>" +
     69                                     "<FromUserName><![CDATA[%s]]></FromUserName>" +
     70                                     "<CreateTime>%s</CreateTime>" +
     71                                     "<MsgType><![CDATA[text]]></MsgType>" +
     72                                     "<Content><![CDATA[%s]]></Content>" +
     73                                     "</xml>",
     74                             fromUserName, toUserName, getUtcTime(),
     75                             "请回复如下关键词:\n文本\n图片\n语音\n视频\n音乐\n图文");
     76         }
     77 
     78         return result;
     79     }
     80 
     81     /**
     82      * 构造文本消息
     83      *
     84      * @param map
     85      * @param content
     86      * @return
     87      */
     88     private static String buildTextMessage(Map<String,String> map, String content) {
     89         //发送方帐号
     90         String fromUserName = map.get("FromUserName");
     91         // 开发者微信号
     92         String toUserName = map.get("ToUserName");
     93         /**
     94          * 文本消息XML数据格式
     95          * <xml>
     96              <ToUserName><![CDATA[toUser]]></ToUserName>
     97              <FromUserName><![CDATA[fromUser]]></FromUserName>
     98              <CreateTime>1348831860</CreateTime>
     99              <MsgType><![CDATA[text]]></MsgType>
    100              <Content><![CDATA[this is a test]]></Content>
    101              <MsgId>1234567890123456</MsgId>
    102          </xml>
    103          */
    104         return String.format(
    105                 "<xml>" +
    106                         "<ToUserName><![CDATA[%s]]></ToUserName>" +
    107                         "<FromUserName><![CDATA[%s]]></FromUserName>" +
    108                         "<CreateTime>%s</CreateTime>" +
    109                         "<MsgType><![CDATA[text]]></MsgType>" +
    110                         "<Content><![CDATA[%s]]></Content>" + "</xml>",
    111                 fromUserName, toUserName, getUtcTime(), content);
    112     }
    113 
    114     private static String getUtcTime() {
    115         Date dt = new Date();// 如果不需要格式,可直接用dt,dt就是当前系统时间
    116         DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");// 设置显示格式
    117         String nowTime = df.format(dt);
    118         long dd = (long) 0;
    119         try {
    120             dd = df.parse(nowTime).getTime();
    121         } catch (Exception e) {
    122 
    123         }
    124         return String.valueOf(dd);
    125     }
    126 }
    复制代码

      为了方便解析微信服务器发送给我们的xml格式的数据,这里我们借助于开源框架dom4j去解析xml(这里使用的是dom4j-2.0.0-RC1.jar)

      

    4.2.在WxServlet的doPost方法中处理请求

      WxServlet的doPost方法的代码如下:

    复制代码
     1  /**
     2      * 处理微信服务器发来的消息
     3      */
     4     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     5         // TODO 接收、处理、响应由微信服务器转发的用户发送给公众帐号的消息
     6         // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
     7         request.setCharacterEncoding("UTF-8");
     8         response.setCharacterEncoding("UTF-8");
     9         System.out.println("请求进入");
    10         String result = "";
    11         try {
    12             Map<String,String> map = MessageHandlerUtil.parseXml(request);
    13             System.out.println("开始构造消息");
    14             result = MessageHandlerUtil.buildXml(map);
    15             System.out.println(result);
    16             if(result.equals("")){
    17                 result = "未正确响应";
    18             }
    19         } catch (Exception e) {
    20             e.printStackTrace();
    21             System.out.println("发生异常:"+ e.getMessage());
    22         }
    23         response.getWriter().println(result);
    24     }
    复制代码

      到此,我们的WxServlet已经可以正常处理用户的请求并做出响应了.接下来我们测试一下我们开发好的公众号应用是否可以正常和微信用户交互

      将WxStudy部署到Tomcat服务器,启动服务器,记得使用ngrok将本地Tomcat服务器的8080端口映射到外网,保证接口配置信息的URL地址:http://xdp.ngrok.natapp.cn/WxServlet可以正常与微信服务器通信

      登录到我们的测试公众号的管理后台,然后用微信扫描一下测试号的二维码,如下图所示:

      

     

      

      

      关注成功后,我们开发好的公众号应用会先给用户发一条提示用户操作的文本消息,微信用户根据提示操作输入"文本",我们的公众号应用接收到用户请求后就给用户回复了一条我们自己构建好的文本消息,如下图所示:

      

      我们的公众号应用响应给微信用户的文本消息的XML数据如下:

    复制代码
    1 <xml>
    2   <ToUserName><![CDATA[ojADgs0eDaqh7XkTM9GvDmdYPoDw]]></ToUserName>
    3   <FromUserName><![CDATA[gh_43df3882c452]]></FromUserName>
    4   <CreateTime>1453755900000</CreateTime>
    5   <MsgType><![CDATA[text]]></MsgType>
    6   <Content><![CDATA[孤傲苍狼在学习和总结微信开发了,构建一条文本消息:Hello World!]]></Content>
    7 </xml>
    复制代码

      测试公众号的管理后台也可以看到关注测试号的用户列表,如下图所示:

      

      通过这个简单的入门程序,我们揭开了微信开发的神秘面纱了.

      本篇的内容就这么多了。本文的内容和代码参考了用java开发微信公众号:公众号接入和access_token管理(二)这篇博客,在此对作者"风的姿态"表示感谢。

      下载测试项目部署运行时,由于项目中使用的是Servlet3.0,所以要求部署的Tomcat必须是7.x,以前也写过几篇关于Servlet3.0的博客,大家有兴趣的话可以去看看,本篇博文对应的测试项目代码下载http://pan.baidu.com/s/1hrfcGks

    展开全文
  • 准备工作最近学习微信公众平台的开发,在申请一个微信公众号的时候,发现如果不是公司或组织只能申请订阅号(并且是个人的),而且不能使用微信提供的高级接口,所以如果想要使用公众平台的高级接口的话,申请一个...
  • 如图,图1是其它游戏分享链接显示效果(前提是在微信中打开并分享出去),图2是我们游戏页面(同样在微信中打开并分享出去)分享链接显示效果。 要达到图1的自定义文案与图标的效果,经研究与各种尝试,得到的...
  • 关于手机端的浏览器的兼容性问题相信一直是开发者们的一块心病,对于微信开发前端组件这一块总是找不到合适的移动端组件,什么都要自己去实现,这个开发成本就大了去了。于是乎博主打算另辟蹊径,找找基于微信开发的...
  • 网址这样写可以吗,显示链接无法访问。参数对了好几遍了。JS接口安全域名为 www.wodeschool.cn ``` function jump_author() { var appid="wx8d03b75a183070f8"; var redirect_uri=...
  •  开发环境参考:微信开发(一)--分享接口 点击打开链接 由于是基于weixin-java-tools 封装的java sdk,所以在微信底层处理上我们可以直接调用WxMpService. 一.数据准备: 进入页面前,根据条件查询相应的图片...
  • 微信授权页面打开显示链接无法访问   首先确定您用的不是测试账号(腾讯测试账号必须关注才能授权),第二,确定您的wifi网络正常。   微信授权页面打开白板   这个问题困扰了很久了,而且只有Android分享...
  • 微信接口配置需要用到http开头的域名,然后将自己的api.php路径填写正确 如下 下面是api.php文件的代码 &lt;?php /** * wechat php test */ include "WeChat_message.php"; header('Content-...
  • 一:登录微信公众平台进行设置JS接口安全域名 二:获取access_token凭证 access_token的接口地址 https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=...
  • C#微信开发

    2017-12-24 10:35:37
    C#开发微信门户及应用教程   作者:伍华聪   C#开发微信门户及应用(1)--开始使用微信接口 6 1、微信账号 6 2、微信菜单定义 7 3、接入微信链接处理 8 4、使用开发方式创建菜单 14 5、我创建的菜单案例 17 C#...
  • 现象:在浏览器打开链接显示如图 现象原因:调用了微信的接口 解决:不调用微信的接口即可 应用场景:如果需要在浏览器调试微信公众号网页,可以模拟调用必要微信接口的信息或者阻塞非必要微信接口调用...
  • 最近开发是碰见了一个微信巨坑的地方,就是在分享链接后,收到的链接显示缩略图的情况,结果没想到的原因竟然是:  描述或标题中含有 红包、现金 等关键字...
  •   1.开发语言使用Java  ...3.使用natapp(原ngrok)将本地地址映射到外网地址实现微信公众号的本地调试 ...最近学习微信公众平台的开发,在申请一个微信公众号的时候,发现如果不是公司或组织只能申请订...
  • 微信开发之获取openid

    2018-12-31 15:05:16
    微信公众号支付的接口即预下单接口需要传openid,但是单元测试调该接口的话,就得直接传固定的测试参数,没有办法像实际生产流程中那样获取openid,所以这里先通过微信官方的网页授权方法获取openid。 首先我们先...
  • 域名被微信封杀,显示的是链接无法访问; 域名被微信拦截,是跟淘宝一样 - 如需浏览,请长按网址复制后使用浏览器访问。 域名被微信屏蔽,顾名思义,屏蔽,肯定是看不到!!   域名被信封,确实很多因素有关...
  • 小程序页面链接
1 2 3 4 5 ... 20
收藏数 31,974
精华内容 12,789
关键字:

微信开发显示网址链接