微信jsapi开发教程之微信jsapi与java初步接入(第四课)
| 参数名 | 描述 |
| appId | 应用ID 登录微信公众号管理平台可查询 |
| timestamp | 必填,生成签名的时间戳 |
| nonceStr | 必填,生成签名的随机串 |
| signature | 必填,签名,见附录1 |
上述表格中的参数,我们在前一章节已经说的很明白,之所以做出一个表格是因为如果想要成功接入微信jsapi这四个参数是凭证,也就是相当于一个门必须要有四把钥匙才能打开,缺一不可 。
接下来的案例采用java的servlet做的跳转页面,没有用到springMVC,大家可把请求的路径更换成controller路径即可。
WxJsAPIServlet代码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | package com.test; /*** * V型知识库 www.vxzsk.com */ import java.io.IOException; import java.io.PrintWriter; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.test.util.JsapiTicketUtil; import com.test.util.Sign; public class WxJsAPIServlet extends HttpServlet { /** * Constructor of the object. */ public WxJsAPIServlet() { super (); } /** * Destruction of the servlet. */ public void destroy() { super .destroy(); // Just puts "destroy" string in log // Put your code here } /** * The doGet method of the servlet. * * This method is called when a form has its tag value method equals to get. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println( "wxJSAPI====================" ); String jsapi_ticket =JsapiTicketUtil.getJSApiTicket();; Map "http://www.vxzsk.com/weChat/wxJsAPIServlet" ); String timestamp = map.get( "timestamp" ); String nonceStr = map.get( "nonceStr" ); String signature = map.get( "signature" ); String appId = "应用Id" ; request.setAttribute( "appId" , appId); request.setAttribute( "timestamp" , timestamp); request.setAttribute( "signature" ,signature); request.setAttribute( "nonceStr" , nonceStr); request.getRequestDispatcher( "jsapi/jsapi.jsp" ).forward(request, response); } /** * The doPost method of the servlet. * * This method is called when a form has its tag value method equals to post. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this .doGet(request, response); } /** * Initialization of the servlet. * * @throws ServletException if an error occurs */ public void init() throws ServletException { // Put your code here } } |
第44行是生成 jsapi_ticket的工具类,在下面有贴出工具类的代码。
第45行 Sign类的sign方法,把表格中的最后三个参数封装放到Map集合中了。其中参数就是请求的servlet地址并跳转到调用微信jsapi的jsp界面。
第49行 appId替换成你自己的应用id,如果不知道应用id 可登陆微信公众平台管理中心查询。
servlet对应的web.xml代码
| 1 2 3 4 5 6 7 8 9 10 | < servlet > < description >This is the description of my J2EE component description > < display-name >This is the display name of my J2EE component display-name > < servlet-name >WxJsAPIServlet servlet-name > < servlet-class >com.test.WxJsAPIServlet servlet-class > servlet > < servlet-mapping > < servlet-name >WxJsAPIServlet servlet-name > < url-pattern >/wxJsAPIServlet url-pattern > servlet-mapping > |
生成签名算法类Sign代码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | package com.test.util; /*** * V型知识库 www.vxzsk.com */ import java.util.UUID; import java.util.Map; import java.util.HashMap; import java.util.Formatter; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.io.UnsupportedEncodingException; public class Sign { public static Map Map new HashMap String nonce_str = create_nonce_str(); String timestamp = create_timestamp(); String string1; String signature = "" ; //注意这里参数名必须全部小写,且必须有序 string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url; System.out.println(string1); try { MessageDigest crypt = MessageDigest.getInstance( "SHA-1" ); crypt.reset(); crypt.update(string1.getBytes( "UTF-8" )); signature = byteToHex(crypt.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ret.put( "url" , url); ret.put( "jsapi_ticket" , jsapi_ticket); ret.put( "nonceStr" , nonce_str); ret.put( "timestamp" , timestamp); ret.put( "signature" , signature); return ret; } private static String byteToHex( final byte [] hash) { Formatter formatter = new Formatter(); for ( byte b : hash) { formatter.format( "%02x" , b); } String result = formatter.toString(); formatter.close(); return result; } private static String create_nonce_str() { return UUID.randomUUID().toString(); } private static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000 ); } public static void main(String[] args) { String jsapi_ticket =JsapiTicketUtil.getJSApiTicket(); // 注意 URL 一定要动态获取,不能 hardcode String url = "http://www.vxzsk.com/xx/x.do" ;//url是你请求的一个action或者controller地址,并且方法直接跳转到使用jsapi的jsp界面 Map for (Map.Entry entry : ret.entrySet()) { System.out.println(entry.getKey() + ", " + entry.getValue()); } }; } |
生成jsapi_ticket参数的工具类JsapiTicketUtil代码
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | package com.test.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import net.sf.json.JSONObject; import com.test.weixin.TestAcessToken; /*** * @author V型知识库 www.vxzsk.com * */ public class JsapiTicketUtil { /*** * 模拟get请求 * @param url * @param charset * @param timeout * @return */ public static String sendGet(String url, String charset, int timeout) { String result = "" ; try { URL u = new URL(url); try { URLConnection conn = u.openConnection(); conn.connect(); conn.setConnectTimeout(timeout); BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream(), charset)); String line= "" ; while ((line = in.readLine()) != null ) { result = result + line; } in.close(); } catch (IOException e) { return result; } } catch (MalformedURLException e) { return result; } return result; } /*** * 获取acess_token * 来源www.vxzsk.com * @return */ public static String getAccessToken(){ String appid= "你公众号基本设置里的应用id" ; //应用ID String appSecret= "你公众号基本设置里的应用密钥" ; //(应用密钥) String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" +appid+ "&secret=" +appSecret+ "" ; String backData=TestAcessToken.sendGet(url, "utf-8" , 10000 ); String accessToken = (String) JSONObject.fromObject(backData).get( "access_token" ); return accessToken; } /*** * 获取jsapiTicket * 来源 www.vxzsk.com * @return */ public static String getJSApiTicket(){ //获取token String acess_token= JsapiTicketUtil.getAccessToken(); String urlStr = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" +acess_token+ "&type=jsapi" ; String backData=TestAcessToken.sendGet(urlStr, "utf-8" , 10000 ); String ticket = (String) JSONObject.fromObject(backData).get( "ticket" ); return ticket; } public static void main(String[] args) { String jsapiTicket = JsapiTicketUtil.getJSApiTicket(); System.out.println( "调用微信jsapi的凭证票为:" +jsapiTicket); } } |
上述代码中有个获取access_token的方法,请读者更换自己的参数即可
jsapi.jsp代码
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> < html > < head > < base href="<%=basePath%>"> < title >微信jsapi测试-V型知识库 title > < meta name = "viewport" content = "width=320.1,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" > < script src = "http://res.wx.qq.com/open/js/jweixin-1.1.0.js" > script > head > < body > < center >< h3 >欢迎来到微信jsapi测试界面-V型知识库 h3 > center > < br > < p >timestamp:${ timestamp} p > < p >nonceStr:${ nonceStr} p > < p >signature:${ signature} p > < p >appId:${ appId} p > < input type = "button" value = "upload" onclick = "uploadImg();" /> < input type = "button" value = "获取当前位置" onclick = "getLocation();" /> < br > < script type = "text/javascript" > wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: '${appId}', // 必填,公众号的唯一标识 timestamp: '${ timestamp}' , // 必填,生成签名的时间戳 nonceStr: '${ nonceStr}', // 必填,生成签名的随机串 signature: '${ signature}',// 必填,签名,见附录1 jsApiList: ['chooseImage','getLocation','openLocation'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ alert("ready"); }); wx.error(function (res) { alert("调用微信jsapi返回的状态:"+res.errMsg); }); function uploadImg() { wx.checkJsApi({ jsApiList: ['chooseImage','openLocation','getLocation'], // 需要检测的JS接口列表,所有JS接口列表见附录2, success: function(res) { // 以键值对的形式返回,可用的api值true,不可用为false // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"} alert(res); wx.chooseImage({ count: 1, // 默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success: function (res) { var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片 alert(localIds); } }); } }); } function getLocation() { var latitude = ""; var longitude = ""; wx.getLocation({ type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02' success: function (res) { latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90 longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。 var speed = res.speed; // 速度,以米/每秒计 var accuracy = res.accuracy; // 位置精度 wx.openLocation({ latitude: latitude, // 纬度,浮点数,范围为90 ~ -90 longitude: longitude, // 经度,浮点数,范围为180 ~ -180。 name: '你当前的位置', // 位置名 address: 'currentLocation', // 地址详情说明 scale: 26, // 地图缩放级别,整形值,范围从1~28。默认为最大 infoUrl: '' // 在查看位置界面底部显示的超链接,可点击跳转 }); } }); } script > body > html > |
测试场景:打开微信公众号,点击菜单回复带有请求servlet地址,跳转到jsapi.jsp界面链接地址,然后界面会弹出调用微信jsapi成功或失败的窗口信息,所以还需要接下来的代码:
WeChatServlet为微信接入的servlet,不清楚的同学可学习我们的微信开发教程。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | package com.test; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.test.message.resp.TextMessage; import com.test.util.MessageUtil; /** * 核心请求处理类 * @author V型知识库 www.vxzsk.com * * doGet方法里 有个weixinTest,这个是公众管理平台里面自己设置的token 大家根据自己的token替换 */ public class WeChatServlet extends HttpServlet { private static final long serialVersionUID = 1508798736675904038L; /** * 确认请求来自微信服务器 */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println( "V型知识库原创www.vxzsk.com" ); // 微信加密签名 String signature = request.getParameter( "signature" ); System.out.println( "微信加密签名signature:-----------------------" +signature); // 时间戳 String timestamp = request.getParameter( "timestamp" ); System.out.println( "时间戳timestamp:-----------------------" +timestamp); // 随机数 String nonce = request.getParameter( "nonce" ); System.out.println( "随机数nonce:-----------------------" +nonce); // 随机字符串 String echostr = request.getParameter( "echostr" ); System.out.println( "随机字符串echostr:-----------------------" +echostr); //System.out.println("token-----------------------:"+token); PrintWriter out = response.getWriter(); // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败 if (SignUtil.checkSignature( "weixinTest" , signature, timestamp, nonce)) { out.print(echostr); //System.out.println("这是:"+echostr); } out.close(); out = null ; } /** * 处理微信服务器发来的消息 * 实例源码在文章顶部有下载连接 */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println( "V型知识库原创www.vxzsk.com" ); System.out.println( "微信服务器发来消息------------" ); // 将请求、响应的编码均设置为UTF-8(防止中文乱码) request.setCharacterEncoding( "UTF-8" ); response.setCharacterEncoding( "UTF-8" ); String respMessage = null ; try { //xml请求解析 Map //接收微信发过来的xml格式 //发送方帐号(open_id) String fromUserName = requestMap.get( "FromUserName" ); //公众帐号 String toUserName = requestMap.get( "ToUserName" ); //消息类型 String msgType = requestMap.get( "MsgType" ); //消息创建时间 String createTime = requestMap.get( "CreateTime" ); //微信服务器post过来的内容 String weixinContent = requestMap.get( "Content" ); System.out.println( "公众号用户发送过来的文本消息内容:" +weixinContent); //接下来我们用上一章节自己封装好的工具类 if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { //文本类型 用户回复 “hh” 微信自动回复此条消息 //回复换行的文本消息 TextMessage textMessage = new TextMessage(); textMessage.setToUserName(fromUserName); textMessage.setFromUserName(toUserName); textMessage.setCreateTime( new Date().getTime()); textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); textMessage.setFuncFlag( 0 ); //回复用户的换行字符串 \n表示换行 StringBuffer buffer = new StringBuffer(); if (weixinContent.equals( "hh" )){ //如果用户发送”hh“ buffer.append( "欢迎访问" ).append( "\n" ); buffer.append( "微信jsapi测试界面" ).append( "\n\n" ); buffer.append( "回复'hh'二字即可能显示此条消息" ); } else { buffer.append( "您好我是V型知识库" ); } textMessage.setContent(buffer.toString()); respMessage = MessageUtil.textMessageToXml(textMessage); //转换成xml格式 } // 响应回复消息 PrintWriter out = response.getWriter(); out.print(respMessage); out.close(); } catch (Exception e){ e.printStackTrace(); } } } |
MessageUtil工具类
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | package com.test.util; /*** * V型知识库www.vxzsk.com * */ import java.io.InputStream; import java.io.Writer; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.test.message.resp.Article; import com.test.message.resp.MusicMessage; import com.test.message.resp.NewsMessage; import com.test.message.resp.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; /** * 消息工具类 */ public class MessageUtil { /** * 返回消息类型:文本 */ public static final String RESP_MESSAGE_TYPE_TEXT = "text" ; /** * 返回消息类型:音乐 */ public static final String RESP_MESSAGE_TYPE_MUSIC = "music" ; /** * 返回消息类型:图文 */ public static final String RESP_MESSAGE_TYPE_NEWS = "news" ; /** * 请求消息类型:文本 */ public static final String REQ_MESSAGE_TYPE_TEXT = "text" ; /** * 请求消息类型:图片 */ public static final String REQ_MESSAGE_TYPE_IMAGE = "image" ; /** * 请求消息类型:链接 */ public static final String REQ_MESSAGE_TYPE_LINK = "link" ; /** * 请求消息类型:地理位置 */ public static final String REQ_MESSAGE_TYPE_LOCATION = "location" ; /** * 请求消息类型:音频 */ public static final String REQ_MESSAGE_TYPE_VOICE = "voice" ; /** * 请求消息类型:推送 */ public static final String REQ_MESSAGE_TYPE_EVENT = "event" ; /** * 事件类型:subscribe(订阅) */ public static final String EVENT_TYPE_SUBSCRIBE = "subscribe" ; /** * 事件类型:unsubscribe(取消订阅) */ public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe" ; /** * 事件类型:CLICK(自定义菜单点击事件) */ public static final String EVENT_TYPE_CLICK = "CLICK" ; /** * 解析微信发来的请求(XML) * * @param request * @return * @throws Exception */ @SuppressWarnings ( "unchecked" ) public static Map throws Exception { // 将解析结果存储在HashMap中 Map new HashMap // 从request中取得输入流 InputStream inputStream = request.getInputStream(); // 读取输入流 SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); // 得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子节点 List // 遍历所有子节点 for (Element e : elementList) { map.put(e.getName(), e.getText()); } // 释放资源 inputStream.close(); inputStream = null ; return map; } /** * 文本消息对象转换成xml * @param textMessage 文本消息对象 * @return xml */ public static String textMessageToXml(TextMessage textMessage) { xstream.alias( "xml" , textMessage.getClass()); return xstream.toXML(textMessage); } /** * 音乐消息对象转换成xml * @param musicMessage 音乐消息对象 * @return xml */ public static String musicMessageToXml(MusicMessage musicMessage) { xstream.alias( "xml" , musicMessage.getClass()); return xstream.toXML(musicMessage); } /** * 图文消息对象转换成xml * @param newsMessage 图文消息对象 * @return xml */ public static String newsMessageToXml(NewsMessage newsMessage) { xstream.alias( "xml" , newsMessage.getClass()); xstream.alias( "item" , new Article().getClass()); return xstream.toXML(newsMessage); } /** * 扩展xstream,使其支持CDATA块 * @date */ private static XStream xstream = new XStream( new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 对所有xml节点的转换都增加CDATA标记 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( " |
TextMessage代码
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package com.test.message.resp; /** * 文本消息 * v型知识库 www.vxzsk.com */ public class TextMessage extends BaseMessage { // 回复的消息内容 private String Content; public String getContent() { return Content; } public void setContent(String content) { Content = content; } } |
BaseMessage代码
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | package com.test.message.resp; /** * 消息基类(公众帐号 -> 普通用户) * V型知识库 www.vxzsk.com */ public class BaseMessage { // 接收方帐号(收到的OpenID) private String ToUserName; // 开发者微信号 private String FromUserName; // 消息创建时间 (整型) private long CreateTime; // 消息类型(text/music/news) private String MsgType; // 位0x0001被标志时,星标刚收到的消息 private int FuncFlag; public String getToUserName() { return ToUserName; } public void setToUserName(String toUserName) { ToUserName = toUserName; } public String getFromUserName() { return FromUserName; } public void setFromUserName(String fromUserName) { FromUserName = fromUserName; } public long getCreateTime() { return CreateTime; } public void setCreateTime( long createTime) { CreateTime = createTime; } public String getMsgType() { return MsgType; } public void setMsgType(String msgType) { MsgType = msgType; } public int getFuncFlag() { return FuncFlag; } public void setFuncFlag( int funcFlag) { FuncFlag = funcFlag; } } |
效果如下:




本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
