微信静默授权全步骤

首先,整理一下获取授权的步骤。

一、配置微信服务器。

//省略import部分
@RequestMapping("/wechat")
@Controller
public class MobileWechatController {

    private static String token = "wechat";

    @RequestMapping(value = "/testWx")
    public void get(HttpServletRequest request, HttpServletResponse response) throws Exception {

        System.out.println("========WechatController========= ");

        Enumeration pNames = request.getParameterNames();
        while (pNames.hasMoreElements()) {
            String name = (String) pNames.nextElement();
            String value = request.getParameter(name);
            // out.print(name + "=" + value);

            String log = "name =" + name + "     value =" + value;
            System.out.println(log + "aaaa");
        }
        String signature = request.getParameter("signature");/// 微信加密签名
        String timestamp = request.getParameter("timestamp");/// 时间戳
        String nonce = request.getParameter("nonce"); /// 随机数
        String echostr = request.getParameter("echostr"); // 随机字符串
        PrintWriter out = response.getWriter();
        if (checkSignature(signature, timestamp, nonce)) {
            System.out.println("--------接入成功--------");
            out.print(echostr);
        }
        out.close();
        out = null;
    }
    private static boolean checkSignature(String signature, String timestamp, String nonce) {
        System.out.println("signature:" + signature + "timestamp:" + timestamp + "nonce:" + 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对比,标识该请求来源于微信
        System.out.println(tmpStr.equals(signature.toUpperCase()));
        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;
    }

}

二、封装向微信服务器发送请求的工具类。

  /**
         * 调用对方接口方法
         * @param path 对方或第三方提供的路径
         * @param data 向对方或第三方发送的数据,大多数情况下给对方发送JSON数据让对方解析
         */
    public static String interfaceUtil(String path, String data) {
            try {
                URL url = new URL(path);
                //打开和url之间的连接
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                PrintWriter out = null;

                /**设置URLConnection的参数和普通的请求属性****start***/

                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("connection", "Keep-Alive");
                conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

                /**设置URLConnection的参数和普通的请求属性****end***/

                //设置是否向httpUrlConnection输出,设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
                //最常用的Http请求无非是get和post,get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet,
                //post与get的 不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内。
                conn.setDoOutput(true);
                conn.setDoInput(true);

                conn.setRequestMethod("GET");//GET和POST必须全大写
                /**GET方法请求*****start*/
                /**
                 * 如果只是发送GET方式请求,使用connet方法建立和远程资源之间的实际连接即可;
                 * 如果发送POST方式的请求,需要获取URLConnection实例对应的输出流来发送请求参数。
                 * */
                conn.connect();

                /**GET方法请求*****end*/

                /***POST方法请求****start*/

            /*out = new PrintWriter(conn.getOutputStream());//获取URLConnection对象对应的输出流

            out.print(data);//发送请求参数即数据

            out.flush();//缓冲数据
            */
                /***POST方法请求****end*/

                //获取URLConnection对象对应的输入流
                InputStream is = conn.getInputStream();
                //构造一个字符流缓存
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                String str = "";
                String sss = "";
                while ((str = br.readLine()) != null) {
                    str=new String(str.getBytes(),"UTF-8");//解决中文乱码问题
                    System.out.println(str);
                    sss = str;
                }
                //关闭流
                is.close();
                //断开连接,最好写上,disconnect是在底层tcp socket链接空闲时才切断。如果正在被其他线程使用就不切断。
                //固定多线程的话,如果不disconnect,链接会增多,直到收发不出信息。写上disconnect后正常一些。
                conn.disconnect();
                System.out.println("完整结束");
                //数据库存入本次运行的accesstoken quartz中一小时五十九分钟刷新一次

                return sss;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

微信所有的请求和方法都可以通过这个方法进行,举例:

String wechatLogin = WechatUtils.interfaceUtil(
                "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + GRANTTYPE
                        + "&appid=" + APPID
                        + "&secret=" + APPSECRET
                , "");
        // 发起GET请求获取凭证
JSONObject jsonObject = JSON.parseObject(wechatLogin);

使用上述的方法,将需要发送到微信服务器的url地址按照要求组装好,即可接收到回传的wechatLogin json字符串,再稍微进行处理即可使用回传数据。

三、开始微信静默授权。

开始静默授权前,我们先整理一下这个过程。

首先,在我们的公众号上通过代码创建一个菜单

 /**
     * 创建菜单
     *
     * @param menu 菜单实例
     * @param accessToken 有效的access_token
     * @return 0表示成功,其他值表示失败
     */
    public static int createMenu(Menu menu, String accessToken) {
        int result = 0;
        // 拼装创建菜单的url
        String url = MENU_CREATE_URL.replace("ACCESS_TOKEN", accessToken);
        System.out.println("url:"+url);
        // 将菜单对象转换成json字符串
        String jsonMenu = JSONObject.toJSON(menu).toString();
        System.out.println("jsonMenu"+jsonMenu);
        // 调用接口创建菜单
        JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);
        System.out.println("jsonObject"+jsonObject);
        if (null != jsonObject) {
            if (0 != jsonObject.getInteger("errcode")) {
                result = jsonObject.getInteger("errcode");
                logger.error("创建菜单失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
            }
        }

        return result;
    }

这里有两个参数,menu和accessToken,accessToken根据微信官方文档中的方法进行获取,每个accessToken的使用时间为两小时,但是由于它的获取次数有限制,所以推荐在服务器端将accessToken的获取设置成一段时间获取一次,需要的时候去数据库里取用即可。menu是我们再公众号前台展示的按钮样式,可以通过一个实体类进行组装

//修改菜单
    private static Menu getMenu(){
        Button btn1 = new Button();
        btn1.setName("配电巡检");
        btn1.setType("view");
        btn1.setKey("11");
        btn1.setUrl("https://open.weixin.qq.com/connect/oauth2/authorize?" +
        "appid=************&" +
        "redirect_uri=http%3A%2F%2Fwww.*******.cn%2F******&" +
        "response_type=code&" +
        "scope=snsapi_base&" +
        "state=123#wechat_redirect");
        System.out.println(btn1.getUrl());
        Menu menu = new Menu();
        menu.setButton(new Button[]{btn1});
        return menu;
    }

这里重点说一下redirect_uri这个参数,uri中的参数路径需要与微信公众平台中的 开发——接口权限——网页授权获得用户基本信息——修改 中的网页授权域名对应,否则点击之前配置好的menu会出现redirect_url参数错误或redirect_url和服务器配置不一样的错误,出现这两个错误,只要关注改正menu参数的配置和网页授权域名这两个部分即可。

 

这些配置都成功以后,点击菜单对应的按钮后,用户界面展示的应该是一个参数中完整的redirect_url+code=*********,

这里的code就是拉取授权的关键,我们使用code组装下一个uri,用于获取用户对公众号唯一的openid。

(这里附一个小方法,用于获取回传页面上的code参数)

   public static String getCode(HttpServletRequest request){
        String code = request.getParameter("code");
        System.out.println(code);
        return code;
    }
String code = WechatUtils.getCode(request);
System.out.println("code ----- " + code);
LoginAccessToken loginAccessToken = null;
String accessToken = WechatUtils.interfaceUtil(
          "https://api.weixin.qq.com/sns/oauth2/access_token?" +
          "appid=" + APPID +
          "&secret=" + APPSECRET +
          "&code=" + code +
          "&grant_type=authorization_code"
          ,""
        );
JSONObject jsonObject = JSON.parseObject(accessToken);

注意:这里的accessToken和全局的accessToken不一样。

至此,用户的openid已经在不知不觉中被我们获取到了,这个openid可以用做发送模板消息等操作,总结下来发现,微信公众号的开发其实不难,无外乎 发送http请求+展示自己的页面,遇到问题再补充。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章