java Wechat Public Number Development, Authentication, Custom Menu, Message Push, Web Page Jump

Keywords: xml Java JSON SHA1

Because of the authentication problem, first use the test account, click on the developer tool, and enter the public platform test account.

Domain name validation:

1. Server validation, modifying interface configuration information:
Note that in the interface configuration information, url verifies the absolute path of token's legitimate address under your server. Here is the absolute path of hello/start under the server. Token is a agreed key. When the server verifies, Wechat encrypts a string with token. It sends other parameters used in the encrypted string to the server and performs it with the token defined by the server. Encryption and get the results, and then it will compare the results twice, if the same, it will pass the verification, so the token in the background of Wechat and the token on the server must be the same, using the same encryption method to get the same results.

The validation method is as follows: GET

    @RequestMapping(value="Start",method=RequestMethod.GET)
    public void start(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("Start signature verification");
        //Get four parameters from the server
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");

        ArrayList<String> array = new ArrayList<String>();
        array.add(signature);
        array.add(timestamp);
        array.add(nonce);

        // 1. Lexicographic ordering of three parameters: token, timestamp and non CE
        String sortString = sort(token, timestamp, nonce);
        // 2. sha1 encryption by splicing three parameter strings into one string
        String mytoken = Decript.SHA1(sortString);
        // 3. sha1 encrypted string can be compared with signature to indicate that the request originated from Wechat.
        if (mytoken != null && mytoken != "" && mytoken.equals(signature)) {
            System.out.println("Signature verification is passed.");
            response.getWriter().println(echostr); //If the echostr is output successfully, the Wechat server will receive the output before confirming the completion of the verification.

        } else {
            System.out.println("Signature verification failed.");
        }
    }

sort method:

    /**
     * Ranking method
     * @param token
     * @param timestamp
     * @param nonce
     * @return
     */
    public static String sort(String token, String timestamp, String nonce) {
        String[] strArray = { token, timestamp, nonce };
        Arrays.sort(strArray);

        StringBuilder sbuilder = new StringBuilder();
        for (String str : strArray) {
            sbuilder.append(str);
        }

        return sbuilder.toString();
    }

SHA1 encryption

/**
 * 
 * @param decript Fields to be encrypted
 * @return
 */
        public static String SHA1(String decript) {
            try {
                MessageDigest digest = MessageDigest
                        .getInstance("SHA-1");
                digest.update(decript.getBytes());
                byte messageDigest[] = digest.digest();
                // Create Hex String
                StringBuffer hexString = new StringBuffer();
                // Converting byte arrays to hexadecimal numbers
                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 "";
        }

After the same string generated by token on the Wechat server, it is validated.

Custom menu:
Using the online interface debugging tool, we can directly generate:
First, get a basic token with a public account password, get a call credential for the basic interface, create a menu, and then customize the menu.

The body example is as follows:

 {
     "button":[
     {  
               "type":"view",
               "name":"One button application",
               "url":"http://Scanhandle?Page=1"
      },
      {
             "type":"view",
               "name":"Schedule query",
               "url":"http://Scanhandle?Page=2"
       },
      {
             "type":"view",
               "name":"Personal Center",
               "url":"http://Scanhandle?Page=3"
       }]
 }

Submenus can also be generated to self-refer to official documents.

Message push:
In the process of interaction between micro-credit users and public numbers, some actions of users will cause the micro-credit server to notify the server address set by the developer at the developer's center through event push. Here is the address to verify the signature. The absolute path of hello/start under the server is POST:, because the message should conform to the structure of the micro-credit push XML data package, such as:

<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a test]]></Content>
 <MsgId>1234567890123456</MsgId>
 </xml>

Therefore, message sending needs to be processed in a certain format. First, the message sent by subscribers is parsed according to the format specification, and then the corresponding reply is made.

    @RequestMapping(value="Start",method=RequestMethod.POST)
    public void messageRecieve(HttpServletRequest request, HttpServletResponse response) throws IOException {

        //Message Acceptance, Processing, Response
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");

        String respMessage = null;
        //Calling core business types to receive and process messages requires corresponding format encapsulation
        respMessage = EastnetService.processRequest(request, response);
        //Response message
        PrintWriter out = response.getWriter();
        out.print(respMessage);
        out.close();

    }
import com.alibaba.fastjson.support.odps.udf.CodecCheck.A;
import com.sf.signing.weixin.util.ConstantUtil;
import com.sf.signing.wx_access.message.Article;
import com.sf.signing.wx_access.message.TextMessage;
import com.sf.signing.wx_access.util.MessageUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Map;


public class EastnetService {
    public static String processRequest(HttpServletRequest request, HttpServletResponse response) {
        String respMessage = null;
        //Text message class content returned by default
        String respContent = "";
        String fromUserName="";
        String toUserName ="";
        String msgType ="";
        String lantitude="";
        String longtitude="";
        try {
            //xml request parsing
            Map<String,String> requestMap = MessageUtil.pareXml(request);
            //Sender account (open_id)
            fromUserName = requestMap.get("FromUserName");
            //Public account number
            toUserName = requestMap.get("ToUserName");
            //Message type
            msgType = requestMap.get("MsgType");
//          String eventType = requestMap.get("Event");

            String fromContent=requestMap.get("Content");
            String userName="";

            //Subscribe
            String eventTypeSub = requestMap.get("Event");
            Article article=new Article();
            if((MessageUtil.EVENT_TYPE_SUBSCRIBE).equals(eventTypeSub)){


                article.setTitle("Welcome to Electronic Signing Platform");
                article.setDescription("Click to view details");
//              String classpath=request.getSession().getServletContext().getRealPath("/"); 
//              article.setPicUrl(ConstantUtil.BASE_PATH+"/file/get?path=welcom.jpg");
                article.setPicUrl(ConstantUtil.BASE_PATH+"/assets/pic/welcom.jpg");
                article.setUrl(ConstantUtil.BASE_PATH+"/welcome.html");

                respMessage="<xml><ToUserName><![CDATA["+requestMap.get("FromUserName")+"]]></ToUserName>"+
                        "<FromUserName><![CDATA["+requestMap.get("ToUserName")+"]]></FromUserName><CreateTime>"+
                                System.currentTimeMillis()+"</CreateTime><MsgType><![CDATA[news]]></MsgType>\r\n" + 
                                        "<ArticleCount>1</ArticleCount>\r\n" + 
                                        "<Articles>\r\n" + 
                                        "<item>\r\n" + 
                                        "<Title><![CDATA["+article.getTitle()+"]]></Title> \r\n" + 
                                        "<Description><![CDATA["+article.getDescription()+"]]></Description>\r\n" + 
                                        "<PicUrl><![CDATA["+article.getPicUrl()+"]]></PicUrl>\r\n" + 
                                        "<Url><![CDATA["+article.getUrl()+"]]></Url>\r\n" + 
                                        "</item>\r\n" + 
                                        "</Articles>\r\n" + 
                                        "</xml>";
            }

           //Reporting position
            if (MessageUtil.REQ_MESSSAGE_TYPE_LOCATION.equals(eventTypeSub)) {
//              lantitude=requestMap.get("Latitude");
//              longtitude=requestMap.get("Longitude");
//              respMessage=("<xml><ToUserName><![CDATA["+requestMap.get("FromUserName")+
//              "]]></ToUserName>"+"<FromUserName><![CDATA["+requestMap.get("ToUserName")
//              +"]]></FromUserName><CreateTime>"+System.currentTimeMillis()+"</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA["+"lantitude:  "+lantitude+"\nlongtitude:  "+longtitude+"]]></Content></xml>");
//
//          
            }
            //Automatic recovery
            if("Help".equals(fromContent)){
                respContent=fromUserName+"      "+toUserName+"Bind account:Return the reuse username binding+User name,example:Username binding fangw\n Trip view:Please reply to the itinerary\n Stroke adding:Please reply to the itinerary additions\n Stroke modification:Please reply to the revision of the itinerary\n";
                article.setTitle("Welcome to Electronic Signing Platform");
                article.setDescription("Click to view details");

                //String classpath = EastnetService.class.getClass().getResource("/").getPath().replaceFirst("/", "");
                //String webappRoot = classpath.replaceAll("/WEB-INF/classes/", "");

                article.setPicUrl(ConstantUtil.BASE_PATH+"/assets/pic/welcom.jpg");
                article.setUrl(ConstantUtil.BASE_PATH+"/welcome.html");
                respMessage="<xml><ToUserName><![CDATA["+requestMap.get("FromUserName")+"]]></ToUserName>"+
                        "<FromUserName><![CDATA["+requestMap.get("ToUserName")+"]]></FromUserName><CreateTime>"+
                                System.currentTimeMillis()+"</CreateTime><MsgType><![CDATA[news]]></MsgType>\r\n" + 
                                        "<ArticleCount>1</ArticleCount>\r\n" + 
                                        "<Articles>\r\n" + 
                                        "<item>\r\n" + 
                                        "<Title><![CDATA["+article.getTitle()+"]]></Title> \r\n" + 
                                        "<Description><![CDATA["+article.getDescription()+"]]></Description>\r\n" + 
                                        "<PicUrl><![CDATA["+article.getPicUrl()+"]]></PicUrl>\r\n" + 
                                        "<Url><![CDATA["+article.getUrl()+"]]></Url>\r\n" + 
                                        "</item>\r\n" + 
                                        "</Articles>\r\n" + 
                                        "</xml>";
            }
            //Reply to text messages
//          TextMessage textMessage = new TextMessage();
//          textMessage.setToUserName(toUserName);
//          textMessage.setFromUserName(fromUserName);
//          textMessage.setCreateTime(new Date().getTime());
//          textMessage.setMsgType(MessageUtil.RESP_MESSSAGE_TYPE_TEXT);
//          textMessage.setFuncFlag(0);
//          StringBuffer sb=new StringBuffer();



        } catch (Exception e) {
            respMessage=("<xml><ToUserName><![CDATA["+fromUserName+
                    "]]></ToUserName>"+"<FromUserName><![CDATA["+toUserName
                    +"]]></FromUserName><CreateTime>"+System.currentTimeMillis()+"</CreateTime><MsgType><![CDATA[Invalid request]]></MsgType><Content><![CDATA["+respContent+"]]></Content></xml>");
        }
        return respMessage;
    }
}

The related categories of methods are as follows:

import com.sf.signing.wx_access.message.Article;
import com.sf.signing.wx_access.message.MusicMessage;
import com.sf.signing.wx_access.message.NewsMessage;
import com.sf.signing.wx_access.message.TextMessage;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;



public class MessageUtil {
    /**
     * Return information type: text
     */
    public static final String  RESP_MESSSAGE_TYPE_TEXT = "text";
    /**
     * Return Information Type: Music
     */
    public static final String  RESP_MESSSAGE_TYPE_MUSIC = "music";
    /**
     * Return Information Type: Graphics and Text
     */
    public static final String  RESP_MESSSAGE_TYPE_NEWS = "news";
    /**
     * Request information type: text
     */
    public static final String  REQ_MESSSAGE_TYPE_TEXT = "text";
    /**
     * Request Information Type: Pictures
     */
    public static final String  REQ_MESSSAGE_TYPE_IMAGE = "image";
    /**
     * Request Information Type: Links
     */
    public static final String  REQ_MESSSAGE_TYPE_LINK = "link";
    /**
     * Request information type: geographic location
     */
    public static final String  REQ_MESSSAGE_TYPE_LOCATION = "LOCATION";
    /**
     * Request Information Type: Audio
     */
    public static final String  REQ_MESSSAGE_TYPE_VOICE = "voice";
    /**
     * Request information type: push
     */
    public static final String  REQ_MESSSAGE_TYPE_EVENT = "event";
    /**
     * Event type: subscribe
     */
    public static final String  EVENT_TYPE_SUBSCRIBE = "subscribe";
    /**
     * Event type: unsubscribe (unsubscribe)
     */
    public static final String  EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
    /**
     * Event type: click (custom menu click event)
     */
    public static final String  EVENT_TYPE_CLICK= "CLICK";

    /**
     * Event type: view (custom menu click event, return url)
     */
    public static final String  EVENT_TYPE_VIEW= "VIEW";
    /**
     * Parsing Request XML from Wechat 
     */
    @SuppressWarnings("unchecked")
    public static Map<String,String> pareXml(HttpServletRequest request) throws Exception {

        //Store parsed results in HashMap
        Map<String,String> reqMap = new HashMap<String, String>();

        //Get the input stream from request
        InputStream inputStream = request.getInputStream();
        //Read input stream
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        //Get the xml root element
        Element root = document.getRootElement();
        //Get all the child nodes of the root element
        List<Element> elementList = root.elements();
        //Traversing all the sub-nodes to get the information class content
        for(Element elem:elementList){
            reqMap.put(elem.getName(),elem.getText());
        }
        //Release resources
        inputStream.close();
        inputStream = null;

        return reqMap;      
    }
    /**
     * Response message converted to xml return
     * Text message object converted to xml
     */
    public  static String textMessageToXml(TextMessage textMessage) {
        xstream.alias("xml", textMessage.getClass());
        return xstream.toXML(textMessage);
    }
    /**
     * Converting objects of music messages into xml
     * 
     */
    public  static String musicMessageToXml(MusicMessage musicMessage) {
        xstream.alias("xml", musicMessage.getClass());
        return xstream.toXML(musicMessage);
    }
    /**
     * Object transformation of graphic message into xml
     * 
     */
    public  static String newsMessageToXml(NewsMessage newsMessage) {
        xstream.alias("xml", newsMessage.getClass());
        xstream.alias("item", new Article().getClass());
        return xstream.toXML(newsMessage);
    }
    /**
     * Extending xstream to support CDATA blocks
     * 
     */
    private static XStream xstream = new XStream(new XppDriver(){
        public HierarchicalStreamWriter createWriter(Writer out){
            return new PrettyPrintWriter(out){
                //Adding CDATA tags to all xml node transformations
                boolean cdata = true;

                @SuppressWarnings("unchecked")
                public void startNode(String name,Class clazz){
                    super.startNode(name,clazz);
                }

                protected void writeText(QuickWriter writer,String text){
                    if(cdata){
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    }else{
                        writer.write(text);
                    }
                }
            };
        }
    });

}

Message Entity Class:

public class Article {
    //Graphic message name
    private String Title;
    //Graphic and Textual Message Description
    private String Description;
    //Graphic and text links, support JPG, PNG, format, the better effect is large figure 640*320, small figure
    //80 * 80. Domain names that restrict image links need to be consistent with URLs in the basic information that developers fill in
    private String PicUrl;
    //Click on the link for text message jump
    private String Url;

    public String getTitle() {
        return Title;
    }

    public void setTitle(String title) {
        Title = title;
    }

    public String getDescription() {
        return Description;
    }

    public void setDescription(String description) {
        Description = description;
    }

    public String getPicUrl() {
        return PicUrl;
    }

    public void setPicUrl(String picUrl) {
        PicUrl = picUrl;
    }

    public String getUrl() {
        return Url;
    }

    public void setUrl(String url) {
        Url = url;
    }

}

At this point, the basic message interaction and the custom menu are completed, and then authorized for the web page. If the subscriber clicks on the menu and wants to bring information into the third-party website, the developer must obtain the authorized token through the user's consent before proceeding. After the user's consent, the developer can get the unique openid of the user. Using the token and user id authorized by the web page, the user can be obtained. Detailed information.
Firstly, the domain name of the authorized callback page is set up to allow the page authorization to obtain the user's basic information.


Note that this is the server domain name. If your code is placed under the baidu server, this is baidu.com. Don't add anything else, otherwise you will make a mistake.

After clicking on the custom menu before, it will jump to scandle controller. The controller code is as follows:

import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.sf.signing.weixin.service.SignClientService;
import com.sf.signing.weixin.util.ConstantUtil;
import com.sf.signing.wx_access.model.Token;
import com.sf.signing.wx_access.util.GetTokenUtil;
import com.sf.signing.wx_access.util.Oauth;

@Controller
public class LoginController {

public static Boolean check=false;

@Autowired
private SignClientService clinetService;

    @RequestMapping(value="Scanhandle",method=RequestMethod.GET)
    public void login(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{
        /*
         * @param code This is what Wechat provides.
         * @param jsonstring User information json string
         */
        String code = request.getParameter("code");
        String page=request.getParameter("Page");
        String open_id = null;
        //If there is no code, request the Web address provided to Wechat
        //state also has parameters that you need to process further after you get the user information.
        if (code == null) {
            /*
             * @param appid Wechat provides you with appid
             * @param redirect_uri Your own server address / project name / Servelt name on this page
             * @param state You also need to take parameters with you.
             * @param page  Jump page parameters, according to parameters to different pages
             */

            response.sendRedirect(ConstantUtil.URL_BEG+page+ConstantUtil.URL_PARAM);

        } else {
            check=true;
            //The parameters that need to be further processed in the future are taken out.
            String param = request.getParameter("state");
            //To oauth.javaAfter getting the user information, save it to Session
            ArrayList<String> list= new Oauth().getUserinfo(code);
            open_id = list.get(0);
//            String token=GetTokenUtil.getToken().getAccessToken();
//            String token= TokenThread.accessToken.getAccessToken();
            String token="";
            Token token1=GetTokenUtil.getToken();
            if(token1!=null) {
                token=token1.getAccessToken();
            }


            String netToken= list.get(1);
            request.getSession().setAttribute("open_id", open_id);
            request.getSession().setAttribute("access_token",netToken);
            String clientId = clinetService.getClient(open_id);
            request.getSession().setAttribute("client_id", clientId);
            //Here you can rewrite the code that you need to deal with further.
            // Jump to index.jsp
            if(page.equals("1")){
                //Push
              //  TemplateUtil.sentMsg(open_id,token);
                request.getRequestDispatcher("index.html").forward(request, response);
            }
            else if(page.equals("2")){
                request.getRequestDispatcher("progress-query.html").forward(request, response);
            }else if(page.equals("3")){
                request.getRequestDispatcher("user-center.html").forward(request, response);
            }

        }

    }


}

Class auth:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;


public class Oauth {
    public static String id=null;
    /**
     * 
     * @param code If the user authorizes access to the web page, there will be an authorization code
     * @return
     * @throws IOException
     */
    public ArrayList<String> getUserinfo(String code) throws IOException {
        ArrayList<String> strings=new ArrayList<String>();
        StringBuilder json = new StringBuilder();
        String url = null;
        BufferedReader in = null;
        String inputLine = null;
        String jsonstring = null;
        JSONObject jobject = null;
        // Here, the appid and secret are replaced by your own appid and secret, and the code is a sign of the user's consent to access.
        url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=ae&secret=e677ff&code="
                + code + "&grant_type=authorization_code";
        //logger.info("url1:"+url);
        in = new BufferedReader(new InputStreamReader(new URL(url)
                .openConnection().getInputStream(), "utf-8"));
        while ((inputLine = in.readLine()) != null) {
            json.append(inputLine);
        }
        //logger.info("String:");
        in.close();
        String openid = JSON.parseObject(json.toString()).getString("openid");
        String token = JSON.parseObject(json.toString()).getString("access_token");
        strings.add(openid);
        strings.add(token);
       // return  strings;
        //Here is the user details, obtained with id and token

        jsonstring = json.toString();
        jobject = JSON.parseObject(jsonstring);
        json = new StringBuilder();

        url = "https://api.weixin.qq.com/sns/userinfo?access_token="
                + jobject.getString("access_token") + "&openid="
                + jobject.getString("openid");
        //logger.info("url2:"+url);
        in = new BufferedReader(new InputStreamReader(new URL(url)
                .openConnection().getInputStream(), "utf-8"));
        inputLine = null;
        while ((inputLine = in.readLine()) != null) {
            json.append(inputLine);
        }
        in.close();
        jsonstring = json.toString();
        //logger.info("DetailString"+jsonstring);
        strings.add(jsonstring);
        return strings;

    }
}

Web pages only need to receive user information and return to relevant pages. The basic functions of wechat docking are all realized.

Posted by hwttdz on Sun, 26 May 2019 13:01:19 -0700