微信大众号获取openid(1)

微信公众号获取openid(1)



       最近公司要用微信公众号开发关于停车场的功能(基本都是网页形式),官方文档毕竟很官方,读起来必然没有大白话通俗易懂,现整理整理流程和期间遇到的一些问题、注意事项记录下来,要是有同等需要的人可以参考下(以下操作是在申请测试号的前提下,正式号没什么差别,我还没有配置正式号,如果有差别回来再进行说明)。

 

  1. 去微信公众平台用邮箱注册一个测试号,网址为https://mp.weixin.qq.com。注册完成后微信平台会自动分配一个appID和appsecret,这两个参数是后续配置菜单,获取token等操作需要的凭证。
  2. 填写接口配置信息,此信息需要你有自己的服务器资源,填写的URL需要正确响应微信发送的Token验证,简单来说就是这个配置的url,就是你在服务器工程上写的接口,用户在微信上的一些操作(比如关注你的公众号、取关等),微信就会调用这个接口,然后你需要将微信发送过来的参数进行校验,校验成功,则返回给微信echostr,校验失败,微信没有接收到echostr回传字符串,就会终止用户的操作。微信大众号获取openid(1)其中的URL用域名,Token是与微信自定义的,看你心情定义了,保存配置的时候,微信会调用你填写的URL接口地址,校验成功了方能保存成功,否则保存配置失败。示例代码:
  3.    /**
         * 正确响应微信发送的token验证
         * @param request
         * @param response
         * @return
         */
        @RequestMapping(value="/tokenVerify")
        @ResponseBody
        public String tokenVerify(HttpServletRequest request,HttpServletResponse response){
            try {
                // 微信加密签名
                String signature = request.getParameter("signature");
                System.out.println("微信加密签名signature:-----------------------"+signature);
                // 时间戳
                String timestamp = request.getParameter("timestamp");
                System.out.println("时间戳timestamp:-----------------------"+timestamp);
                // 随机数
                String nonce = request.getParameter("nonce");
                System.out.println("随机数nonce:-----------------------"+nonce);
                // 随机字符串
                String echostr = request.getParameter("echostr");
                System.out.println("随机字符串echostr:-----------------------"+echostr);
               
                // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
                if (SignUtil.checkSignature(WEI_XIN_TOKEN, signature, timestamp, nonce)) {
                    return echostr;
                }
            } catch (Exception e) {
                System.out.println("/weixin/login异常");
                e.printStackTrace();
            }
         
            return "";
        }
  4. 请求校验工具类示例代码:
     * 微信请求校验工具类
     */
    public class SignUtil {
            
        /**
        * 验证签名
        * @param signature
        * @param timestamp
        * @param nonce
        * @return
        */
        public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {    
            String[] arr = new String[] { token, timestamp, nonce };
            // 将token、timestamp、nonce三个参数进行字典序排序
            Arrays.sort(arr);
            StringBuilder content = new StringBuilder();
            for (int i = 0; i < arr.length; i++) {
                content.append(arr[i]);
            }
            MessageDigest md = null;
            String tmpStr = null;
            try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToStr(digest);
            } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            }
            content = null;
            // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
            return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
        }
        /**
        * 将字节数组转换为十六进制字符串
        * @param byteArray
        * @return
        */
        private static String byteToStr(byte[] byteArray) {
            String strDigest = "";
            for (int i = 0; i < byteArray.length; i++) {
            strDigest += byteToHexStr(byteArray[i]);
            }
            return strDigest;
        }
        /**
        * 将字节转换为十六进制字符串
        * @param mByte
        * @return
        */
        private static String byteToHexStr(byte mByte) {
            char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
            char[] tempArr = new char[2];
            tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
            tempArr[1] = Digit[mByte & 0X0F];
            String s = new String(tempArr);
            return s;
        } 
    }
  5. 设置JS接口安全域后,通过关注该测试号,开发者即可在该域名下调用微信开放的JS接口,这个只需要填写服务器域名即可。
    微信大众号获取openid(1)
  6. 在体验接口权限表里面,找‘网页服务’类目下的第一条功能‘网页账号’,‘网页授权获取用户基本信息’接口,进行修改,填写服务器域名。用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。沙盒号回调地址支持域名和ip,正式公众号回调地址只支持域名。官方写的很清楚了,测试号也最好是写域名而不是ip,否则会有问题。
  7. 利用微信公众平台接口调试工具,进行access_token的获取,以及菜单的生成。调试工具地址:https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index&type基础支持&form=获取access_token接口/token。首先获取access_token,填写第一步生成的appID和appsecret,然后‘接口类型’选择‘自定义菜单’,利用刚获取的access_token,进行菜单的添加,示例代码:
  8. {
    "button":[
     {
                    "type": "view",
                    "name": "智慧停车",
                    "url": "http://域名/工程名/weiXinFront/loginPark",
                    "sub_button": [ ]
                },
     {
                    "type": "view",
                    "name": "我的车辆",
                    "url": "http://域名/工程名/weiXinFront/loginMycar",
                    "sub_button": [ ]
                },
     {
      "name":"测试1",
      "sub_button":[
      {
       "type":"click",
       "name":"子菜单名1",
       "key":"name1"
      },
      {
       "type":"click",
       "name":"子菜单名2",
       "key":"name2"
      }
      ]
     },
     {
      "type":"view",
      "name":"百度",
      "url":"http://www.baidu.com"
     }
    ]
    },其中‘智慧停车’和‘我的车辆’是我需要的菜单,其他就是测试的包括子级菜单,用户在点击‘智慧停车’菜单时微信会访问在你生成菜单时填写的url地址,"url": "http://域名/工程名/weiXinFront/loginPark", 在loginPark接口里设置redirecrtUrl,用户同意授权后,微信会将code(用于获取用户openId)传至redirecrtUrl你设置的接口里,在这个接口里我们可以利用收到的code获取用户openId和跳转页面。示例代码:
  9.    @RequestMapping(value="loginPark")
       @ResponseBody
       public void loginPark(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException{
        //跳转接口地址
        String redirecrtUri = Host+"weiXinFront/openidPark";
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+ appID +"&redirect_uri="+URLEncoder.encode(redirecrtUri, "utf-8")+"&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
        try {
            System.out.println("loginPark==url============="+redirecrtUri);
         response.sendRedirect(url);
        } catch (IOException e) {
         e.printStackTrace();
        }
       }
  10.   @RequestMapping(value="openidPark")
       public String openidPark(HttpServletRequest request, HttpServletResponse response){
           //微信发送过来的code
        String code = request.getParameter("code");
        //获取access_token、get请求
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appID+"&secret="+ appsecret +"&code="+code+"&grant_type=authorization_code";
        String result = HttpUtil.sendGet(url, "utf-8", 30000);//发送get请求
        if(StringUtils.isNotBlank(result)){
         JSONObject jo = JSONObject.fromObject(result);
         try{
             HttpSession session = request.getSession();
             String openid = jo.optString("openid");
          synchronized (this) {
              //查找该用户是否已经存在,不存在则新增用户信息
            HUser huser = huserService.getByOpenId(openid);
           if(huser == null){
               huser = new HUser();
            huser.setOpenId(openid);
            huserService.save(huser);
           }
          }
          session.setAttribute("openId", openid);
          //重定向到访问页
          return "redirect:"+Host+"static/weixin/zhihui_tingche.jsp";
         }catch(Exception e) {
          e.printStackTrace();
          return "";
         }
        }
        //异常页
        return "modules/weixin/NewFile";
       }
  11. HttpUtil发送get请求工具类示例代码:public class HttpUtil {
        /***
         * 模拟get请求
         * @param url
         * @param charset
         * @param timeout
         * @return
         */
         public static String sendGet(String url, String charset, int timeout)
          {
            String result = "";
            BufferedReader in = null;
            try
            {
              URL u = new URL(url);
              try
              {
                URLConnection conn = u.openConnection();
                conn.connect();
                conn.setConnectTimeout(timeout);
                in = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset));
                String line="";
                while ((line = in.readLine()) != null)
                {
                  result = result + line;
                }
                //in.close();
              } catch (IOException e) {
                return result;
              }
            }
            catch (MalformedURLException e)
            {
              return result;
            }
           finally{
            try {
             if(in!=null){
              in.close();
             }
       } catch (Exception e2) {
        e2.printStackTrace();
       }
           }
            return result;
          }
        
         /*
          * post
          * url:请求地址
          * param:参数
          */
         public static String sendPost(String url, String param) {
             PrintWriter out = null;
             BufferedReader in = null;
             String result = "";
             try {
                 URL realUrl = new URL(url);
                 // 打开和URL之间的连接
                 URLConnection conn = realUrl.openConnection();
                 // 设置通用的请求属性
                 conn.setRequestProperty("accept", "*/*");
                 conn.setRequestProperty("connection", "Keep-Alive");
    //             conn.setRequestProperty("user-agent",
    //                     "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
                 // 发送POST请求必须设置如下两行
                 conn.setDoOutput(true);
                 conn.setDoInput(true);
                 // 获取URLConnection对象对应的输出流
                 out = new PrintWriter(conn.getOutputStream());
                 // 发送请求参数
                 out.print(param);
                 // flush输出流的缓冲
                 out.flush();
                 // 定义BufferedReader输入流来读取URL的响应
                 in = new BufferedReader(
                         new InputStreamReader(conn.getInputStream()));
                 String line;
                 while ((line = in.readLine()) != null) {
                     result += line;
                 }
             } catch (Exception e) {
                 System.out.println("发送 POST 请求出现异常!"+e);
                 e.printStackTrace();
             }
             //使用finally块来关闭输出流、输入流
             finally{
                 try{
                     if(out!=null){
                         out.close();
                     }
                     if(in!=null){
                         in.close();
                     }
                 }
                 catch(IOException ex){
                     ex.printStackTrace();
                 }
             }
             return result;
         }  
    }
  12. 在你自己的页面里(zhihui_tingche.jsp)就可以进行功能操作了(ajax获取数据,需要openId的话则在后台接口里,用session获取)。