Wechat authentication authorization detailed code example

Keywords: JSON SHA1 SDK Vue

Wechat authentication authorization detailed code example

background

The company has an H5 page that doesn't display pictures after sharing to wechat, and the style is different from the sharing cards of other applications. Later it was found that the reason is that the unified sharing interface of wechat wasn't called. Here is my experience

Things to know before development

  • To have a official account, there will be appId and appsecret. if there is official account.
  • If you want to have a domain name that can be accessed by the Internet, you can use the intranet penetration tool, such as natapp
  • WeChat official account must start with http:// or https://, supporting 80 ports and 443 ports respectively.
  • Can read wechat Description document of JS-SDK

Operation steps

1 compilation of httputil tool class

The function of this tool class is to obtain a signature, which can be divided into four steps:

  1. Get access according to appId and appsecret_ token;
  2. Using access_token get jsapi_ticket;
  3. Using timestamp, random number, jsapi_ The ticket and the url to be accessed are spliced according to the signature algorithm;
  4. In the third step, the string is encrypted with SHA1 to get the signature;

Note: check_ clearLove in the signature method is my token, remember to replace it with your own

public class HttpUtil {

    /**
     * Get ACCESS_TOKEN
     * @param appId
     * @param appSecret
     * @return
     */
    public static String getAccess_token(String appId, String appSecret){

        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret;
        String accessToken = null;
        try
        {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            http.setRequestMethod("GET"); // Must be get request
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// Connection timeout 30 seconds
            System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // Read timeout 30 seconds
            http.connect();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, "UTF-8");
            JSONObject jsonObj = JSONObject.parseObject(message);
            accessToken = jsonObj.getString("access_token");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return accessToken;
    }

    /**
     * Get ACCESS_TICKET
     *
     * @Title: ACCESS_TICKET
     * @Description: Get ACCESS_TICKET
     * @param @return Settings file
     * @return String Return type
     * @throws
     */
    public static String getAccess_ticket(String access_token) {
        String ticket = null;
        String url = "https://api.weixin.qq .com/cgi-bin/ticket/getticket?access_ token="+ access_ Token + "& type = jsapi"; / / the url link and parameters cannot be changed
        try {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            http.setRequestMethod("GET"); // Must be get request
            http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// Connection timeout 30 seconds
            System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // Read timeout 30 seconds
            http.connect();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, "UTF-8");
            JSONObject demoJson = JSONObject.parseObject(message);
            System.out.println("JSON character string:"+demoJson);
            ticket = demoJson.getString("ticket");
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ticket;
    }

    /**
     * Get the encrypted string of SHA1
     * @param decript
     * @return
     */
    public static String SHA1(String decript) {
        try {
            MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
            digest.update(decript.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // Byte array converted to hex
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }
    
    /**
     * Verify signature
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static boolean check_signature(String signature,String timestamp,String nonce) {
        String[] arr = new String[]{timestamp, nonce, "clearLove"};
        Arrays.sort(arr);
        String s = arr[0] + arr[1] + arr[2];
        //String md = MessageDigest.getInstance("SHA-1");
        //byte[] digest = md.digest(s.getBytes("utf-8"));
        // return signature == bytes2HexString(digest);
        String md = SHA1(s);
        return signature.equals(md);
    }
}

be careful:
***access_token * * has an upper limit of 2000 accesses per day, and the effective time is up to two hours, so it is recommended to cache
access_token and access_ticket

2 application for official account number

  1. Log in wechat public platform to apply for test number
  2. The following is the JS interface security domain name, the domain name that can be accessed by the external network that you are ready to write is OK, without any verification. The official account number of the official account is modified with the number of times to modify, and the maximum number is changed three times a month.

Note!!!: I don't have the right match here. The domain name is the domain name. Don't bring http or https!!!
4. The above is the interface configuration information. It's not finished by filling in the URL and Token. Wechat will send a request to the interface you filled in, verify the signature, and the configuration will succeed only after the verification is passed

 /**
     * Wechat authentication
     * @param request
     * @param response
     * @throws IOException
     */
 @GetMapping(value = "/wx")
    public void weChart(HttpServletRequest request, HttpServletResponse response) throws IOException {
         Map<String,String> map = new HashMap<>();
        String qs = request.getQueryString();
        // signature=79a282667d19b5578a9857c4dc87e1982843c1da&echostr=7129991673103630289&timestamp=1591066064&nonce=1339036005
        String [] strs = qs.split("&");
        for(String s:strs){
            String [] str = s.split("=");
            map.put(str[0],str[1]);
        }
        String signature = map.get("signature");
        String echostr = map.get("echostr");
        String timestamp = map.get("timestamp");
        String nonce = map.get("nonce");
        if(HttpUtil.check_signature(signature,timestamp,nonce)){
             response.getWriter().println(echostr);
        }
    }

be careful:

  1. I tried the following method at the beginning, but I can't change the above method later. You can also try this:
  2. If the method is written, and it is determined that the echo host can be returned correctly, close the breakpoint and submit again, otherwise the response timeout configuration will fail

3 page related code

  1. Access on the page where js interface needs to be called JS file This is what this file looks like. Just copy it
  2. Inject permissions through the config interface and verify the configuration. This step also needs to use the HttpUtil we wrote earlier
wx.config({
    debug: true, // When debugging mode is turned on, the return values of all the APIs called will be alert ed out on the client side. To view the parameters passed in, you can open them on the pc side, and the parameter information will be printed out through the log, which will only be printed on the pc side.
    appId: '', // Required, the only sign of official account.
    timestamp: , // Required, generate signature timestamp
    nonceStr: '', // Required, generate random string of signature
    signature: '',// Required, signature
    jsApiList: [] // Required, list of JS interfaces to be used
});

The following is a relatively complete front-end code, which you can change:

$.ajax({
            url: 'XXXXXXXXX',
            type: 'POST',
            dataType: 'json',
            //The url needs to be encoded and if the complete url is divided into
            data: {"url":encodeURIComponent(window.location.href.split("#")[0])}
        })
        .done(function(res) {

            wx.config({
                debug: ture, //It is recommended to start the commissioning phase
                appId: res.appId,//APPID
                timestamp: res.timestamp,//timestamp obtained from the main method above
                nonceStr: res.nonceStr,//The random number nonceStr obtained from the main method above
                signature: res.signature,//signature obtained from the main method above
                //Method interface to call
                jsApiList: [ 'onMenuShareTimeline','onMenuShareAppMessage','onMenuShareWeibo','onMenuShareQQ','onMenuShareQZone']
            });
   }
 wx.ready(function(){
                // alert("I've come in");
                wx.onMenuShareTimeline({
                    title: title, // Share title
                    link: window.location.href, // Share the link, the domain name or path must be consistent with the official account JS security domain corresponding to the current page.
                    imgUrl: "//upload- images.jianshu.io/upload_ Images / 3429385-09282d70c0390d94. PNG? Imagemogr2 / Auto original / strip| imageview2 / 1 / w / 300 / h / 240 ", / / share Icon
                    success: function () {
                        // alert("success")
                        // The callback function executed after the user clicks share
                    }
                });
                wx.onMenuShareAppMessage({
                    title: title, // Share title
                    desc: descContent, // Share description
                    link: window.location.href, // Share the link, the domain name or path must be consistent with the official account JS security domain corresponding to the current page.
                    imgUrl: "//upload- images.jianshu.io/upload_ Images / 3429385-09282d70c0390d94. PNG? Imagemogr2 / Auto original / strip| imageview2 / 1 / w / 300 / h / 240 ", / / share Icon
                    type: '', // Sharing type, music, video or link, no default is link
                    dataUrl: '', // If the type is music or video, you need to provide data link, which is empty by default
                    success: function () {
                        // alert("success")
                        // The callback function executed after the user clicks share
                    }
                });
                wx.onMenuShareQQ({
                    title: title, // Share title
                    desc: descContent, // Share description
                    link: window.location.href, // Share links
                    imgUrl: "//upload- images.jianshu.io/upload_ Images / 3429385-09282d70c0390d94. PNG? Imagemogr2 / Auto original / strip| imageview2 / 1 / w / 300 / h / 240 ", / / share Icon
                    success: function () {
                        // alert("success")
                        // Callback function executed after user confirms sharing
                    },
                    cancel: function () {
                        // alert("failed")
                        // Callback function executed after user cancels sharing
                        // If the validation of config information fails, the error function will be executed. For example, if the signature expires, the validation fails. The specific error information can be viewed in the debug mode of config, or in the returned res parameter. For SPA, the signature can be updated here.
                    }
                });
            });

be careful:
In the vue project, JS SDK is introduced. In order to facilitate the user's use, wechat publishes the official jssdk to npm. There is one called Weixin JS SDK, but this is not what we need to use. On the Internet, many of the references in vue are this, but this is the version published for commonjs, which can only be introduced through require. Many people find that import wx from is introduced into vue ‘weixin-js-sdk’, console.log(wx) will appear undefined. In fact, in order to facilitate the use of es6, a Weixin JS API has been officially released, which is the jssdk we can reference in vue.

The following is the method to obtain configuration information in the background controller:

 @RequestMapping("/getConfig")
    protected void doPost(HttpServletRequest request, HttpServletResponse response){
        //appId and appSecret
        String appId = "wx001ad717b5e73574";
        String appSecret = "5eb0766e0fe465d1e95a7b460abc045c";
        //External incoming url to obtain url url needs to be the url opened in wechat or an error will be reported.
        String URL = request.getParameter("url");
        //Convert url
        String url="";
        //Need to convert decode url
        try {
            url = java.net.URLDecoder.decode(URL,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        //Get access_token
        String aeecss_token = HttpUtil.getAccess_token(appId, appSecret);
        //Get access_ticket
        String aeecss_ticket = HttpUtil.getAccess_ticket(aeecss_token);
        //3. Timestamps and random strings
        String nonceStr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//Random string
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//time stamp

        System.out.println("accessToken:"+aeecss_token+"\njsapi_ticket:"+aeecss_ticket+"\n Time stamp:"+timestamp+"\n Random string:"+nonceStr);

        //4. Get url
        //5. Sort and concatenate parameters
        String str = "jsapi_ticket="+aeecss_ticket+"&noncestr="+nonceStr+"&timestamp="+timestamp+"&url="+url;
        //6. sha1 encryption of strings
        String signature =HttpUtil.SHA1(str);
        System.out.println("Parameters:"+str+"\n Signature:"+signature);
        
        Map<String,String> map=new HashMap();
        map.put("appId",appId);
        map.put("timestamp",timestamp);
        map.put("accessToken",aeecss_token);
        map.put("ticket",aeecss_ticket);
        map.put("nonceStr",nonceStr);
        map.put("signature",signature);
        //JSONObject jsonObject = JSONObject.fromObject(map);
        String json = JSON.toJSONString(map);//map to String
        JSONObject jsonObject = JSON.parseObject(json);//String to json

        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setContentType("application/json;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");

        PrintWriter pw = null;
        try {
            pw = response.getWriter();
        } catch (IOException e1) {
            //logger.error("**********reponse getWriter exception**********");
            e1.printStackTrace();
        }
        pw.write(jsonObject.toString());
        pw.close();
    }

be careful:

  • If = = getConfig() = received url, you can change the request mode to get
  1. Successfully verified through the ready interface processing
wx.ready(function(){

    // Config information validation will execute the ready method. All interface calls must be obtained after the config interface gets the result. Config is an asynchronous operation of the client. Therefore, if the relevant interface is required when the page is loaded, the relevant interface must be put in the ready function to ensure the correct execution. For interfaces that are called only when triggered by the user, they can be called directly without being placed in the ready function.
});
  1. Handling failed validation through the error interface
wx.error(function(res){

    // If the validation of config information fails, the error function will be executed. For example, if the signature expires, the validation fails. The specific error information can be viewed in the debug mode of config, or in the returned res parameter. For SPA, the signature can be updated here.

});

Summary

I thought this thing should be very simple before, but I still stepped on a lot of holes in the middle, so I wrote this article to summarize. After I wrote this article, I found it was very simple. I don't know what to say when I put the code

Reference blog

Webpage share to wechat custom title, content and picture

Wechat public platform background access concise guide

Posted by axo on Tue, 09 Jun 2020 22:29:42 -0700