jsp 微信公众平台 token验证(php、jsp)(转载)

时间:2023-03-08 17:01:30

微信公众平台现在推出自动回复消息接口,但是由于是接口内容用的是PHP语言写的,很多地方操作起来让本人这个对java比较熟悉的小伙很别扭,所以仿照PHP的接口代码做了一套jsp语言编写的接口。

首先先把整个接口代码贴出来做下比较,然后我们再分析代码:

PHP:

  1. <?php
  2. /**
  3. * wechat php test
  4. */
  5. //define your token
  6. define("TOKEN", "weixin");
  7. $wechatObj = new wechatCallbackapiTest();
  8. $wechatObj->valid();
  9. class wechatCallbackapiTest
  10. {
  11. public function valid()
  12. {
  13. $echoStr = $_GET["echostr"];
  14. //valid signature , option
  15. if($this->checkSignature()){
  16. echo $echoStr;
  17. exit;
  18. }
  19. }
  20. public function responseMsg()
  21. {
  22. //get post data, May be due to the different environments
  23. $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
  24. //extract post data
  25. if (!empty($postStr)){
  26. $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
  27. $fromUsername = $postObj->FromUserName;
  28. $toUsername = $postObj->ToUserName;
  29. $keyword = trim($postObj->Content);
  30. $time = time();
  31. $textTpl = "<xml>
  32. <ToUserName><![CDATA[%s]]></ToUserName>
  33. <FromUserName><![CDATA[%s]]></FromUserName>
  34. <CreateTime>%s</CreateTime>
  35. <MsgType><![CDATA[%s]]></MsgType>
  36. <Content><![CDATA[%s]]></Content>
  37. <FuncFlag>0</FuncFlag>
  38. </xml>";
  39. if(!empty( $keyword ))
  40. {
  41. $msgType = "text";
  42. $contentStr = "Welcome to wechat world!";
  43. $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
  44. echo $resultStr;
  45. }else{
  46. echo "Input something...";
  47. }
  48. }else {
  49. echo "";
  50. exit;
  51. }
  52. }
  53. private function checkSignature()
  54. {
  55. $signature = $_GET["signature"];
  56. $timestamp = $_GET["timestamp"];
  57. $nonce = $_GET["nonce"];
  58. $token = TOKEN;
  59. $tmpArr = array($token, $timestamp, $nonce);
  60. sort($tmpArr);
  61. $tmpStr = implode( $tmpArr );
  62. $tmpStr = sha1( $tmpStr );
  63. if( $tmpStr == $signature ){
  64. return true;
  65. }else{
  66. return false;
  67. }
  68. }
  69. }
  70. ?>

JAVA:

  1. <%@page import="java.util.Date"%>
  2. <%@page import="org.dom4j.Element"%>
  3. <%@page import="org.dom4j.DocumentHelper"%>
  4. <%@page import="org.dom4j.Document"%>
  5. <%@page import="java.io.IOException"%>
  6. <%@page import="java.io.InputStreamReader"%>
  7. <%@page import="java.io.BufferedReader"%>
  8. <%@page import="java.io.Reader"%>
  9. <%@page import="java.security.MessageDigest"%>
  10. <%@page import="java.util.Arrays"%>
  11. <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
  12. <%
  13. //WeiXinHandler为内部类不能使用非final类型的对象
  14. final String TOKEN="weixin";
  15. final HttpServletRequest final_request=request;
  16. final HttpServletResponse final_response=response;
  17. %>
  18. <%
  19. class WeiXinHandler{
  20. public void valid(){
  21. String echostr=final_request.getParameter("echostr");
  22. if(null==echostr||echostr.isEmpty()){
  23. responseMsg();
  24. }else{
  25. if(this.checkSignature()){
  26. this.print(echostr);
  27. }else{
  28. this.print("error");
  29. }
  30. }
  31. }
  32. //自动回复内容
  33. public void responseMsg(){
  34. String postStr=null;
  35. try{
  36. postStr=this.readStreamParameter(final_request.getInputStream());
  37. }catch(Exception e){
  38. e.printStackTrace();
  39. }
  40. //System.out.println(postStr);
  41. if (null!=postStr&&!postStr.isEmpty()){
  42. Document document=null;
  43. try{
  44. document = DocumentHelper.parseText(postStr);
  45. }catch(Exception e){
  46. e.printStackTrace();
  47. }
  48. if(null==document){
  49. this.print("");
  50. return;
  51. }
  52. Element root=document.getRootElement();
  53. String fromUsername = root.elementText("FromUserName");
  54. String toUsername = root.elementText("ToUserName");
  55. String keyword = root.elementTextTrim("Content");
  56. String time = new Date().getTime()+"";
  57. String textTpl = "<xml>"+
  58. "<ToUserName><![CDATA[%1$s]]></ToUserName>"+
  59. "<FromUserName><![CDATA[%2$s]]></FromUserName>"+
  60. "<CreateTime>%3$s</CreateTime>"+
  61. "<MsgType><![CDATA[%4$s]]></MsgType>"+
  62. "<Content><![CDATA[%5$s]]></Content>"+
  63. "<FuncFlag>0</FuncFlag>"+
  64. "</xml>";
  65. if(null!=keyword&&!keyword.equals(""))
  66. {
  67. String msgType = "text";
  68. String contentStr = "Welcome to wechat world!";
  69. String resultStr = textTpl.format(textTpl, fromUsername, toUsername, time, msgType, contentStr);
  70. this.print(resultStr);
  71. }else{
  72. this.print("Input something...");
  73. }
  74. }else {
  75. this.print("");
  76. }
  77. }
  78. //微信接口验证
  79. public boolean checkSignature(){
  80. String signature = final_request.getParameter("signature");
  81. String timestamp = final_request.getParameter("timestamp");
  82. String nonce = final_request.getParameter("nonce");
  83. String token=TOKEN;
  84. String[] tmpArr={token,timestamp,nonce};
  85. Arrays.sort(tmpArr);
  86. String tmpStr=this.ArrayToString(tmpArr);
  87. tmpStr=this.SHA1Encode(tmpStr);
  88. if(tmpStr.equalsIgnoreCase(signature)){
  89. return true;
  90. }else{
  91. return false;
  92. }
  93. }
  94. //向请求端发送返回数据
  95. public void print(String content){
  96. try{
  97. final_response.getWriter().print(content);
  98. final_response.getWriter().flush();
  99. final_response.getWriter().close();
  100. }catch(Exception e){
  101. }
  102. }
  103. //数组转字符串
  104. public String ArrayToString(String [] arr){
  105. StringBuffer bf = new StringBuffer();
  106. for(int i = 0; i < arr.length; i++){
  107. bf.append(arr[i]);
  108. }
  109. return bf.toString();
  110. }
  111. //sha1加密
  112. public String SHA1Encode(String sourceString) {
  113. String resultString = null;
  114. try {
  115. resultString = new String(sourceString);
  116. MessageDigest md = MessageDigest.getInstance("SHA-1");
  117. resultString = byte2hexString(md.digest(resultString.getBytes()));
  118. } catch (Exception ex) {
  119. }
  120. return resultString;
  121. }
  122. public final String byte2hexString(byte[] bytes) {
  123. StringBuffer buf = new StringBuffer(bytes.length * 2);
  124. for (int i = 0; i < bytes.length; i++) {
  125. if (((int) bytes[i] & 0xff) < 0x10) {
  126. buf.append("0");
  127. }
  128. buf.append(Long.toString((int) bytes[i] & 0xff, 16));
  129. }
  130. return buf.toString().toUpperCase();
  131. }
  132. //从输入流读取post参数
  133. public String readStreamParameter(ServletInputStream in){
  134. StringBuilder buffer = new StringBuilder();
  135. BufferedReader reader=null;
  136. try{
  137. reader = new BufferedReader(new InputStreamReader(in));
  138. String line=null;
  139. while((line = reader.readLine())!=null){
  140. buffer.append(line);
  141. }
  142. }catch(Exception e){
  143. e.printStackTrace();
  144. }finally{
  145. if(null!=reader){
  146. try {
  147. reader.close();
  148. } catch (IOException e) {
  149. e.printStackTrace();
  150. }
  151. }
  152. }
  153. return buffer.toString();
  154. }
  155. }
  156. %>
  157. <%
  158. WeiXinHandler handler=new WeiXinHandler();
  159. handler.valid();
  160. %>

以上就是PHP接口和JSP接口的所有代码,现在我们来对一些需要注意的地方做下分析:

首先的从总体看的话,jsp要比PHP繁琐一些,因为很多函数需要自己写,像sha1加密,解析xml字符串等都需要自己找第三方的库。

第一点,我们要获取微信公众平台给jsp发送的post或get参数,正常情况下都是用request.getParameter就可以获取到,但是在写的过程中发现PHP是这样获取

  1. $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

这时通过查询一些资料知道这样获取的是无法通过$_GET或$_POST函数得到的”未识别 MIME 类型的数据“,原始的 POST 数据

(参考:http://blog.****.net/china_skag/article/details/7284227

所以这里使用获取原始数据流的方式来解析post的xml数据

  1. String postStr=null;
  2. try{
  3. postStr=this.readStreamParameter(final_request.getInputStream());
  4. }catch(Exception e){
  5. e.printStackTrace();
  6. }
  1. //从输入流读取post参数
  2. public String readStreamParameter(ServletInputStream in){
  3. StringBuilder buffer = new StringBuilder();
  4. BufferedReader reader=null;
  5. try{
  6. reader = new BufferedReader(new InputStreamReader(in));
  7. String line=null;
  8. while((line = reader.readLine())!=null){
  9. buffer.append(line);
  10. }
  11. }catch(Exception e){
  12. e.printStackTrace();
  13. }finally{
  14. if(null!=reader){
  15. try {
  16. reader.close();
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }
  22. return buffer.toString();
  23. }

第二个,是response消息返回给微信平台,我尝试的用最一般的out.print去做,但是发现没反应,观察PHP的代码写法

  1. echo "";
  2. exit;

猜想可能需要有个刷新的操作才能把消息response回去,于是找了下response内的一些函数做出以下尝试

  1. //向请求端发送返回数据
  2. public void print(String content){
  3. try{
  4. final_response.getWriter().print(content);
  5. final_response.getWriter().flush();
  6. final_response.getWriter().close();
  7. }catch(Exception e){
  8. }
  9. }

发现以上做法是可以在微信发送端得到消息的;

第三个,接口描述上说目前只支持80端口的服务端地址,所以我这里的做法是用apache服务器路由到tomcat的jsp上

关于微信公众平台的消息接口的详细介绍,可以参看微信公众平台的官方文档,里面介绍了消息的xml的格式和消息的发送方式等。

转载地址:http://blog.****.net/wangqianjiao/article/details/8469780/