天天看點

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

很多IT搬磚員,磕磕碰碰,都比較生怕遇到自己未接觸過的東西,但是作為開發,遲早有一天,某個上司跟你說,某個需求,需要接入微信公衆号,需要擷取微信使用者資訊。

雖然說微信提供了相關的文檔,但是免不了還是很多初學者看了又看還是一臉懵逼(當初我也是),是以我今天來出一份小白都能看懂的教程,甚至你轉行過來跟着一步步來,你也能學會,怎麼擷取微信使用者資訊。

前期準備工作Part 1  申請微信公衆号測試号(如果你有直接可以用的微信公衆号或者訂閱号,你可以不理這個Part 1):

申請完成後, 登入,點選頁面左側的開發-開發者工具:(我們來申請一個微信公衆平台測試帳号,因為我們絕大多數人沒有已認證的微信公衆号可以用)

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

進入到公衆平台測試帳号界面,

接下來你需要做填寫的有:

1.跟微信碰頭的 驗證Token位址以及Token參數值

這個是做什麼的?怎麼達到驗證一說?

就是所填寫的URL ,就是一個接口,然後下面填寫的Token值是個自定義參數。 

在這個接口暢通的情況下,點選送出,微信這邊就會帶着時間戳timestamp、随機數nonce還有填的token值,使用微信的加密算法(後面代碼會有介紹)進行簽名,生成一個signature,然後還有額外的一個随機字元串echostr,去通路URL對應的接口。

接口裡面需要用簽名驗證的邏輯函數(後面代碼都會有介紹,這裡先簡單講講流程),拿到微信傳過來的時間戳timestamp、随機數nonce加上我們自己自定義的token值,也使用微信的加密算法進行簽名,生成一個signature;然後和微信傳過來的進行比對,如果一樣,OK,碰頭确認成功,把微信發過來的随機字元串echostr傳回去即可;如果不一樣,不好意思,碰頭不成功,傳回null即可(基本就是接口不通、生成簽名方法有誤)。

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

2.JS接口安全域名

這個就是一個你即将要編碼的項目,釋出部署後,提供接口對應的域名。

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

3.網頁授權域名

這裡一樣,也是項目的域名。

JAVA 擷取微信使用者資訊,看完這篇你必須得學會
JAVA 擷取微信使用者資訊,看完這篇你必須得學會

好了,總體我們看了一下,我們需要填寫的三個子產品的資訊,都有一個非常關鍵的東西,域名。 

那怎麼有域名呢?本地寫demo的小夥伴眉頭不由自主一皺?

前期準備工作Part 2  内網穿透 整一個域名出來(如果你有已經可以用的域名,那麼你可以不理這個Part 2)

具體内網穿透,将本地127.0.0.1 對應一個外網可以通路的域名。 本來我是寫了具體操作示例的,但是涉嫌打廣告,導緻文章被下架了,是以這個part2,就隻能删除了。 請了解。

 (微信公衆平台測試号的資訊還沒填?别急)

接下來我們建一個java項目,

第一個工具類,  用來調微信接口的,HttpClientUtil.java:

import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
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.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Author : JCccc
 * @CreateTime : 2019/8/2
 * @Description :
 **/
public class HttpClientUtil {

    public static String doGet(String url, Map<String, String> param) {

        // 建立Httpclient對象
        CloseableHttpClient httpclient = HttpClients.createDefault();

        String resultString = "";
        CloseableHttpResponse response = null;
        try {
            // 建立uri
            URIBuilder builder = new URIBuilder(url);
            if (param != null) {
                for (String key : param.keySet()) {
                    builder.addParameter(key, param.get(key));
                }
            }
            URI uri = builder.build();

            // 建立http GET請求
            HttpGet httpGet = new HttpGet(uri);

            // 執行請求
            response = httpclient.execute(httpGet);
            // 判斷傳回狀态是否為200
            if (response.getStatusLine().getStatusCode() == 200) {
                resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                httpclient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return resultString;
    }

    public static String doGet(String url) {
        return doGet(url, null);
    }

    public static String doPost(String url, Map<String, String> param) {
        // 建立Httpclient對象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
        try {
            // 建立Http Post請求
            HttpPost httpPost = new HttpPost(url);
            // 建立參數清單
            if (param != null) {
                List<NameValuePair> paramList = new ArrayList<>();
                for (String key : param.keySet()) {
                    paramList.add(new BasicNameValuePair(key, param.get(key)));
                }
                // 模拟表單
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
                httpPost.setEntity(entity);
            }
            // 執行http請求
            response = httpClient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return resultString;
    }

    public static String doPost(String url) {
        return doPost(url, null);
    }

    public static String doPostJson(String url, String json) {
        // 建立Httpclient對象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        String resultString = "";
        try {
            // 建立Http Post請求
            HttpPost httpPost = new HttpPost(url);
            // 建立請求内容
            StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);
            // 執行http請求
            response = httpClient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return resultString;
    }
}      

第二個工具類,跟微信碰頭驗證用的,SignUtil.java: 

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/**
 * @Author : JCccc
 * @CreateTime : 2019/8/2
 * @Description :
 **/
public class SignUtil {

  private static String token = "weixinCoursexxxxx";//填你自己的


  public static boolean checkSignature(String signature, String timestamp, String nonce) {
    String[] paramArr = new String[] { token, timestamp, nonce };
    Arrays.sort(paramArr);
    String content = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);

    String ciphertext = null;
    try {
      MessageDigest md = MessageDigest.getInstance("SHA-1");
      byte[] digest = md.digest(content.toString().getBytes());
      ciphertext = byteToStr(digest);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }

    return ciphertext != null ? ciphertext.equals(signature.toUpperCase()) : false;
  }


  private static String byteToStr(byte[] byteArray) {
    String strDigest = "";
    for (int i = 0; i < byteArray.length; i++) {
      strDigest += byteToHexStr(byteArray[i]);
    }
    return strDigest;
  }


  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;
  }
}      

 然後最關鍵的東西,接口,WxLoginController.java:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.springmvc.util.HttpClientUtil;

import com.springmvc.util.SignUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

/**
 * @Author : JCccc
 * @CreateTime : 2019/8/2
 * @Description :
 **/

@RestController
@RequestMapping("/wxAuth")
public class WxLoginController {
    private  static  String APPID="xxxxxx";//填你自己的
    private  static  String APPSECRET="xxxxx";//填你自己的


    /**
     * 用于給微信驗證token
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @RequestMapping("/checkToken")
    public void checkToken(HttpServletRequest request,HttpServletResponse response) throws IOException {
        // 微信加密簽名
        String signature = request.getParameter("signature");
        // 時間戳
        String timestamp = request.getParameter("timestamp");
        // 随機數
        String nonce = request.getParameter("nonce");
        // 随機字元串
        String echostr = request.getParameter("echostr");

        if (SignUtil.checkSignature(signature, timestamp, nonce)) {
            System.out.println("校驗token成功");
            response.getWriter().print(echostr);
        }
    }

    /**
     * 用于擷取出回調位址  (引導使用者調用此接口,成功後自動調取回調位址然後取出使用者資訊)
     * @param response
     * @throws IOException
     */
    @RequestMapping("/login")
    public void wxLogin(HttpServletResponse response) throws IOException {
        //請求擷取code的回調位址
        //用線上環境的域名或者用内網穿透,不能用ip
        String callBack = "http://3xXXXXXXXi.natappfree.cc/wxAuth/callBack";//域名填你自己的

        //請求位址
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize" +
                "?appid=" + APPID +
                "&redirect_uri=" + URLEncoder.encode(callBack) +
                "&response_type=code" +
                "&scope=snsapi_userinfo" +
                "&state=STATE#wechat_redirect";
       
        System.out.println(url);
        //重定向
        response.sendRedirect(url);
    }


    /**
     * 回調方法
     * @param request
     * @param response
     * @throws IOException
     */
    //  回調方法
    @RequestMapping("/callBack")
    public String wxCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String code = request.getParameter("code");

        //擷取access_token
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token" +
                "?appid=" + APPID +
                "&secret=" + APPSECRET +
                "&code=" + code +
                "&grant_type=authorization_code";

        String result = HttpClientUtil.doGet(url);

        System.out.println("請求擷取access_token:" + result);
        //傳回結果的json對象
        JSONObject resultObject = JSON.parseObject(result);

        //請求擷取userInfo
        String infoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                "?access_token=" + resultObject.getString("access_token") +
                "&openid=" + resultObject.getString("openid") +
                "&lang=zh_CN";

        String resultInfo = HttpClientUtil.doGet(infoUrl);

        //此時已擷取到userInfo,再根據業務進行處理
        System.out.println("請求擷取userInfo:" + resultInfo);

        return  "hello!";
    }
}      

 好了,到這裡,我們其實已經準備就緒了。 就這麼點代碼足夠了。那麼接下來我們回去補全剛剛需要填的東西。

第一部分:

URL   http://+域名+代碼裡面的驗證接口URI 

Token 自己随便寫,但是要跟SignUtil.java裡面的token值保持一緻

(!!記得把項目跑起來,這裡填完送出是會調項目接口的!!)

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

 第二部分:

​​http://+域名​

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

第三部分:

域名

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

OK,到此已經完全接入完畢了,接下來就是測試了。

先确定,項目已經跑起來了。

測試之前,這是怎麼擷取使用者微信号資訊的。

首先你需要做的是,引導使用者在微信端,記得是微信端,必須在微信端,去調用我們寫的​​​http://3xXXXXX.natappfree.cc/wxAuth/login​​​  接口  。

也就是說使用者點選了我們公衆号某個按鈕,或者說是在微信用戶端不知道在哪裡點選了某個東西, 對應調用的接口是我們的

​​​http://3xXXXXX.natappfree.cc/wxAuth/login​​​ 接口。

 接下來會發生什麼(建議結合代碼看),

一.我們接口會帶上我們微信公衆号(測試号)的APPID,以及按照規則進行接口URL拼接,通路接口。

二.通路完之後,微信會自動從我們拼接的參數裡面擷取出我們的回調接口,callback,去調用我們的接口(帶着code值)。

三.我們的回調接口裡面擷取出微信送過來的code值又進行接口URL拼接,通路接口,去擷取access_token和使用者的openid等;

四.最後再進行接口URL拼接,通路接口,去擷取出使用者的各種資訊,昵稱、openid、頭像位址等等。

好像說了這麼多,我相信很多人還是不會怎麼弄,那麼我們來模拟一個場景順便來測試下最終結果(很多人看不到使用者資訊擷取的一瞬間還是會有疑慮的,那麼我們一起來測試下)。

我們來在我們的公衆号上面,建一個菜單,菜單裡面放入我們的 ‘引導接口‘, 然後我們用微信關注下公衆号,點選下,就明白了了。

我們用線上接口調試工具,去在微信公衆号測試号上面建一個菜單(當然那些用自己已有公衆号的夥伴不需要這樣,直接建個菜單,配置下連結就行)

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

 然後,

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

 把access_token複制下,

JAVA 擷取微信使用者資訊,看完這篇你必須得學會

接着,創一個菜單,把我們的引導接口放在一個二級菜單裡面,點選

JAVA 擷取微信使用者資訊,看完這篇你必須得學會
JAVA 擷取微信使用者資訊,看完這篇你必須得學會

OK,我們的公衆測試号,菜單已經建立完了,用微信關注下公衆号(測試号在頁面掃描關注):

JAVA 擷取微信使用者資訊,看完這篇你必須得學會