微信靜默授權全步驟

首先,整理一下獲取授權的步驟。

一、配置微信服務器。

//省略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請求+展示自己的頁面,遇到問題再補充。

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