JAVA 는 springMVC 방식 의 위 챗 접속 을 실현 하고 메시지 자동 답장 인 스 턴 스 를 실현 합 니 다.

얼마 전에 잠시 바 빴 습 니 다.위 챗 공식 번호 의 개발 은 0 부터 문 서 를 보고 많은 구 덩이 를 밟 았 습 니 다.이 겨 낸 셈 입 니 다.최근 에 정 리 를 하고 나중에 재 개발 할 때 돌 이 켜 보고 관련 프로젝트 를 하고 있 는 친구 들 에 게 참고 도 해 주 었 습 니 다.
1.사고방식
위 챗 접속:사용자 메시지 와 개발 자가 필요 로 하 는 이벤트 푸 시 는 위 챗 서버 를 통 해 요청 을 합 니 다.공공 플랫폼 에 설 치 된 서버 url 주 소 를 전달 합 니 다.위 챗 측은 signature,timestamp,nonce,echostr 네 개의 매개 변 수 를 가 져 옵 니 다.저 희 는 공공 플랫폼 에 설 치 된 token 과 전 달 된 timestamp 를 연결 합 니 다.nonce 는 SHA 1 암호 화 를 한 후 signature 와 일치 하 며,ture 설명 을 되 돌려 접속 에 성 공 했 습 니 다.

메시지 답장:사용자 가 공중 번호 로 메 시 지 를 보 낼 때 위 챗 서버 는 사용자 메 시 지 를 xml 형식 으로 POST 를 통 해 우리 가 설정 한 서버 에 대응 하 는 인터페이스 에 요청 합 니 다.우리 가 해 야 할 일 은 메시지 유형 등에 따라 해당 하 는 논리 적 처 리 를 하고 마지막 반환 결 과 를 xml 형식 으로 위 챗 서버 에 반환 하 는 것 입 니 다.위 챗 측 이 사용자 에 게 전달 하 는 이러한 과정. 

1.퍼 블 릭 플랫폼 설정

2.Controller

@Controller
@RequestMapping("/wechat")
publicclass WechatController {
  @Value("${DNBX_TOKEN}")
  private String DNBX_TOKEN;
  
  private static final Logger LOGGER = LoggerFactory.getLogger(WechatController.class);
  
  @Resource
  WechatService wechatService;
  
  /**
   *     
   * @param wc
   * @return
   * @throws IOException 
   */
  @RequestMapping(value="/connect",method = {RequestMethod.GET, RequestMethod.POST})
  @ResponseBody
  publicvoid connectWeixin(HttpServletRequest request, HttpServletResponse response) throws IOException{
    //    、         UTF-8(      ) 
    request.setCharacterEncoding("UTF-8"); //     POST      UTF-8  ,            ,       ;
    response.setCharacterEncoding("UTF-8"); //     (       ) ,         UTF-8,    ;boolean isGet = request.getMethod().toLowerCase().equals("get"); 
   
    PrintWriter out = response.getWriter();
     
    try {
      if (isGet) {
        String signature = request.getParameter("signature");//        
        String timestamp = request.getParameter("timestamp");//     
        String nonce = request.getParameter("nonce");//     
        String echostr = request.getParameter("echostr");//      
        
        //     signature       ,          echostr,      ,       if (SignUtil.checkSignature(DNBX_TOKEN, signature, timestamp, nonce)) { 
          LOGGER.info("Connect the weixin server is successful.");
          response.getWriter().write(echostr); 
        } else { 
          LOGGER.error("Failed to verify the signature!"); 
        }
      }else{
        String respMessage = "    !";
        
        try {
          respMessage = wechatService.weixinPost(request);
          out.write(respMessage);
          LOGGER.info("The request completed successfully");
          LOGGER.info("to weixin server "+respMessage);
        } catch (Exception e) {
          LOGGER.error("Failed to convert the message from weixin!"); 
        }
        
      }
    } catch (Exception e) {
      LOGGER.error("Connect the weixin server is error.");
    }finally{
      out.close();
    }
  }
}
3.서명 검증 검사 서명
위의 contrller 에서 볼 수 있 듯 이 저 는 도구 류 SignUtil 을 봉 하여 안에 있 는 checkSignature 라 는 이름 으로 네 개의 값 을 전 달 했 습 니 다.DNBXTOKEN, signature, timestamp, nonce。이 과정 은 매우 중요 하 다.사실 우 리 는 위 챗 이 보 내 온 값 을 복호화 하 는 과정 으로 이해 할 수 있다.많은 대형 프로젝트 의 모든 인 터 페 이 스 는 안전성 을 확보 하기 위해 이런 검증 과정 이 있 을 것 이다.DNBX_TOKEN 저희 가 위 챗 공식 플랫폼 에 설정 한 token 문자열 입 니 다.비밀 로 하 겠 습 니 다!다른 세 가 지 는 모두 위 챗 서버 에서 get 요청 을 보 내 온 매개 변수 입 니 다.저 희 는 sha 1 암호 화 를 진행 합 니 다.

public class SignUtil { 
 
  /** 
   *      
   * 
   * @param token      token, env.properties                     
   * @param signature         sha1       
   * @param timestamp    
   * @param nonce     
   * @return 
   */ 
  public static boolean checkSignature(String token,String signature, String timestamp, String nonce) { 
    String[] arr = new String[] { token, timestamp, nonce }; 
    //  token、timestamp、nonce            
    Arrays.sort(arr); 
    
    //                   sha1   
    String tmpStr = SHA1.encode(arr[0] + arr[1] + arr[2]); 
    
    //  sha1         signature  ,           
    return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false; 
  } 
  
}
SHA1:

/** 
 *       (JAVA) SDK 
 * 
 * SHA1  
 * 
 * @author helijun 2016/06/15 19:49
 */ 
public final class SHA1 { 
 
  private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', 
              '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 
 
  /** 
   * Takes the raw bytes from the digest and formats them correct. 
   * 
   * @param bytes the raw bytes from the digest. 
   * @return the formatted bytes. 
   */ 
  private static String getFormattedText(byte[] bytes) { 
    int len = bytes.length; 
    StringBuilder buf = new StringBuilder(len * 2); 
    //                  
    for (int j = 0; j < len; j++) { 
      buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); 
      buf.append(HEX_DIGITS[bytes[j] & 0x0f]); 
    } 
    return buf.toString(); 
  } 
 
  public static String encode(String str) { 
    if (str == null) { 
      return null; 
    } 
    try { 
      MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); 
      messageDigest.update(str.getBytes()); 
      return getFormattedText(messageDigest.digest()); 
    } catch (Exception e) { 
      throw new RuntimeException(e); 
    } 
  } 
}
공공 플랫폼 에 저장 을 제출 하고 녹색 힌트 인'접속 성공'을 본 후에 위 챗 접속 을 완료 한 것 을 축하합니다.이 과정 은 암호 화 알고리즘 의 대소 문 자 를 주의해 야 합 니 다.만약 에 접속 이 성공 하지 못 하면 대부분 암호 화 알고리즘 문제 이 므 로 검 사 를 많이 해 야 합 니 다.
4.메시지 자동 답장 서비스 구현

/**
   *          
   * 
   * @param request
   * @return
   */
  public String weixinPost(HttpServletRequest request) {
    String respMessage = null;
    try {

      // xml    
      Map<String, String> requestMap = MessageUtil.xmlToMap(request);

      //      (open_id)
      String fromUserName = requestMap.get("FromUserName");
      //     
      String toUserName = requestMap.get("ToUserName");
      //     
      String msgType = requestMap.get("MsgType");
      //     
      String content = requestMap.get("Content");
      
      LOGGER.info("FromUserName is:" + fromUserName + ", ToUserName is:" + toUserName + ", MsgType is:" + msgType);

      //     
      if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
        //              ,       ,      
        if(content.equals("xxx")){
          
        }
        
        //    
        TextMessage text = new TextMessage();
        text.setContent("the text is" + content);
        text.setToUserName(fromUserName);
        text.setFromUserName(toUserName);
        text.setCreateTime(new Date().getTime() + "");
        text.setMsgType(msgType);
        
        respMessage = MessageUtil.textMessageToXml(text);
        
      } /*else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {//     
        String eventType = requestMap.get("Event");//     
        
        if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {//   
          respContent = "    xxx   !";
          return MessageResponse.getTextMessage(fromUserName , toUserName , respContent);
        } else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {//          
          String eventKey = requestMap.get("EventKey");//   KEY ,            KEY   
          logger.info("eventKey is:" +eventKey);
          return xxx;
        }
      }
      //           2015-3-30
      else if(msgType.equals("voice"))
      {
        String recvMessage = requestMap.get("Recognition");
        //respContent = "         :"+recvMessage;
        if(recvMessage!=null){
          respContent = TulingApiProcess.getTulingResult(recvMessage);
        }else{
          respContent = "       ,        ?";
        }
        return MessageResponse.getTextMessage(fromUserName , toUserName , respContent); 
      }
      //    
      else if(msgType.equals("pic_sysphoto"))
      {
        
      }
      else
      {
        return MessageResponse.getTextMessage(fromUserName , toUserName , "    "); 
      }*/
      //     
      else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
        String eventType = requestMap.get("Event");//     
        //   
        if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
          
          TextMessage text = new TextMessage();
          text.setContent("    ,xxx");
          text.setToUserName(fromUserName);
          text.setFromUserName(toUserName);
          text.setCreateTime(new Date().getTime() + "");
          text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
          
          respMessage = MessageUtil.textMessageToXml(text);
        } 
        // TODO                    ,         
        else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {//     
          
          
        } 
        //          
        else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
          String eventKey = requestMap.get("EventKey");//   KEY ,            KEY   
          if (eventKey.equals("customer_telephone")) {
            TextMessage text = new TextMessage();
            text.setContent("0755-86671980");
            text.setToUserName(fromUserName);
            text.setFromUserName(toUserName);
            text.setCreateTime(new Date().getTime() + "");
            text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
            
            respMessage = MessageUtil.textMessageToXml(text);
          }
        }
      }
    }
    catch (Exception e) {
      Logger.error("error......")
    }
    return respMessage;
  }

먼저 코드 를 붙 이 고 위 와 같이 대부분 주석 이 있 으 며 기본 적 인 의 미 를 한 번 읽 어도 더 설명 할 필요 가 없다.
 특히 주의해 야 할 점 이 있 습 니 다.
위 에 빨간색 으로 표 시 된 from User Name 과 toUser Name 은 정반 대 입 니 다.이것 도 구덩이 중 하나 입 니 다.제 가 오랫동안 조정 한 것 을 기억 합 니 다.문제 가 없 으 면서 도 통 하지 않 았 습 니 다.마지막 에 이 두 개 를 바 꾸 면 소식 을 받 았 습 니 다!사실 뒤 돌아 보 니 맞 는 것 같 아 요.위 챗 서버 에 되 돌아 갈 때 그 자체 의 캐릭터 가 바 뀌 었 기 때문에 발송 과 수신 자 도 반대 일 거 예요.
5.MessageUtil

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

여 기 는 프로그램의 가 독성,확장 성 을 위해 서 저 는 패 키 지 를 만 들 었 습 니 다.몇 개의 상수 와 위 챗 에서 보 내 온 일부 매개 변 수 를 자바 bean 의 지속 적 인 대상 으로 포장 하고 핵심 코드 는 위 와 같 습 니 다.xml 와 map 사이 의 전환 에 중점 을 두 었 습 니 다.
사실 이 문 제 는 위 챗 이 xml 로 통신 한 탓 으로 돌 릴 수 있 습 니 다.우 리 는 평소에 json 을 사용 하기 때문에 짧 은 시간 안에 적응 하지 못 할 수도 있 습 니 다.
1.jar 패키지 도입

<!--   xml -->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    
    <dependency>
      <groupId>com.thoughtworks.xstream</groupId>
      <artifactId>xstream</artifactId>
      <version>1.4.9</version>
    </dependency>
2.xml 전 맵 집합 대상

/**
   * xml   map
   * @param request
   * @return
   * @throws IOException
   */
  @SuppressWarnings("unchecked")
  public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException{
    Map<String, String> map = new HashMap<String, String>();
    SAXReader reader = new SAXReader();
    
    InputStream ins = null;
    try {
      ins = request.getInputStream();
    } catch (IOException e1) {
      e1.printStackTrace();
    }
    Document doc = null;
    try {
      doc = reader.read(ins);
      Element root = doc.getRootElement();
      
      List<Element> list = root.elements();
      
      for (Element e : list) {
        map.put(e.getName(), e.getText());
      }
      
      return map;
    } catch (DocumentException e1) {
      e1.printStackTrace();
    }finally{
      ins.close();
    }
    
    return null;
  }
3.텍스트 메시지 대상 을 xml 로 변환

/** 
   *          xml 
   * 
   * @param textMessage        
   * @return xml 
   */ 
  public static String textMessageToXml(TextMessage textMessage){
    XStream xstream = new XStream();
    xstream.alias("xml", textMessage.getClass());
    return xstream.toXML(textMessage);
  }
지금까지 큰 성 과 를 거 두 었 습 니 다.이 럴 때 공중 번호 에서'테스트'를 보 내 보 세 요.당신 은 위 챗 으로 답장 하 는'the text is 테스트'를 받 을 수 있 습 니 다.이것 도 위 코드 에서 한 답장 처리 입 니 다.물론 당신 의 상상 을 발휘 하여 그 로 하여 금 당신 이 하고 싶 은 모든 일 을 할 수 있 습 니 다.예 를 들 어 날 씨 를 확인 하고 규정 을 위반 하 는 등..
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기