java微信消息加解密

今天心血来潮就信手拈来学了下微信消息加解密的知识,忽然觉得微信真的好强大。可能在大部分项目微信消息的加解密都用不上,但是仍然不排除有使用到的情况,如涉及金钱方面的微信应用包括商城类、金融类还有其他安全级别要求很高的微信应用。针对这些情况我觉得还是有必要分享下我的学习心得。


一   功能概述

公众号消息加解密是公众平台为了进一步加强公众号安全保障,提供的新机制。开发者需注意,公众账号主动调用API的情况将不受影响。只有被动回复用户的消息时,才需要进行消息加解密。具体包括:

1.新增消息体签名验证,用于公众平台和公众账号验证消息体的正确性
2.针对推送给微信公众账号的普通消息和事件消息,以及推送给设备公众账号的设备消息进行加密
3.公众账号对密文消息的回复也要求加密
这段文字描述摘抄至微信公众平台开发者文档,从上面的描述可以看出消息加密旨在提高安全性。


二  公众平台如何设置消息加解密

点击左侧的开发者中心,找到相关的配置项即可,如下所示



三  编程实现

消息加解密支持的开发语言还是挺多的如java、C++、C#、php等,鉴于本人只懂java,因此代码部分将会全部使用java

1  有关消息加解密的关键代码

代码微信公众平台提供了下载链接,但是由于java开发环境的复杂性,直接使用微信提供的jar包会出现问题,因此不建议使用jar包而是直接使用源码,下面是用于加密的代码下载链接

http://mp.weixin.qq.com/wiki/static/assets/a5a22f38cb60228cb32ab61d9e4c414b.zip

2  代码编写之前的注意事项

(1)替换JCE策略文件

这主要是因为采用AES加密时,如果密钥长度大于128,会抛异常:java.security.InvalidKeyException: Illegal key size。下面是JCE的下载地址:

Java版本 JCE无限制权限策略文件下载地址


1.8 http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
1.7 http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
1.6 http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html


下载后替换%JDK_HOME%\jre\lib\security目录下对应的文件。

(2)使用微信提供的源代码

首先把zip解压缩,之后复制commons-codec这个jar包到项目,之后复制src下面的源代码粘贴到src下面



3 微信消息加解密需调整的代码

(1) 新增处理用户请求消息的2组方法

 public static WXBizMsgCrypt getWxCrypt() {
      	WXBizMsgCrypt crypt=null;
  		try {
  			crypt = new WXBizMsgCrypt(SignUtil.token,"xxxxx","xxxxx");
  		} catch (AesException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
      	return crypt;
 }
这个方法用于取得微信消息加解密的实例


 public static Map<String, String> parseXmlCrypt(HttpServletRequest request) throws Exception {  
        // 将解析结果存储在HashMap中  
        Map<String, String> map = new HashMap<String, String>();  
  
        // 从request中取得输入流  
        InputStream inputStream = request.getInputStream();  
        
        BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream));
        String line;
        StringBuffer buf=new StringBuffer();
        while((line=reader.readLine())!=null){
        	buf.append(line);
        }
        reader.close();
        inputStream.close();
        
        WXBizMsgCrypt wxCeypt=MessageUtil.getWxCrypt();
        // 微信加密签名  
        String msgSignature = request.getParameter("msg_signature");  
        // 时间戳  
        String timestamp = request.getParameter("timestamp");  
        // 随机数  
        String nonce = request.getParameter("nonce");  
        
        String respXml=wxCeypt.decryptMsg(msgSignature, timestamp, nonce, buf.toString());
        
        //SAXReader reader = new SAXReader();  
        Document document =DocumentHelper.parseText(respXml);
        // 得到xml根元素  
        Element root = document.getRootElement();  
        // 得到根元素的所有子节点  
        List<Element> elementList = root.elements();  
  
        // 遍历所有子节点  
        for (Element e : elementList)  
            map.put(e.getName(), e.getText());  
  
        // 释放资源  
        //inputStream.close();  
        //inputStream = null;  
  
        return map;  
    }  

该方法主要用于把用户发来的消息进行解密处理


/* 
	 * 微信控制器(入口)
	 */
public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		 // 将请求、响应的编码均设置为UTF-8(防止中文乱码)  
        request.setCharacterEncoding("UTF-8");  
        response.setCharacterEncoding("UTF-8");  
        
        String encryptType = request.getParameter("encrypt_type");  
        // 微信加密签名  
        String signature = request.getParameter("signature");  
        // 时间戳  
        String timestamp = request.getParameter("timestamp"); 
        // 随机数  
        String nonce = request.getParameter("nonce");  
        // 响应消息  
       
        try{
        	PrintWriter out = response.getWriter(); 
        	if (SignUtil.checkSignature(signature, timestamp, nonce)) {  
        		Map<String, String> requestMap = null;  
                  if("aes".equals(encryptType)){
        			requestMap=MessageUtil.parseXmlCrypt(request);
        			String respXml=CoreService.processRequest(requestMap); 
        			respXml=MessageUtil.getWxCrypt().encryptMsg(respXml,timestamp,nonce);
        			System.out.println(respXml);
        			out.print(respXml);  
                  }else{
                	requestMap=MessageUtil.parseXml(request);
        			String respXml=CoreService.processRequest(requestMap); 
        			//respXml=MessageUtil.getWxCrypt().encryptMsg(respXml,timestamp,nonce);
        			out.print(respXml);
        			System.out.println(respXml);
                  }
          
                // 调用核心业务类接收消息、处理消息  
                //String respMessage = CoreService.processRequest(request);  
                
                
                out.close();  
            }  
        	
        }catch(Exception e){
        	e.printStackTrace();
        }
        
}

相比之前的代码加了一个encrypt_type,为了考虑程序的健壮性,程序根据encrypt_type的值进行了判断,使得程序同时支持消息的明文模式和安全模式,同时CoreService里的方法也进行了一定的调整(主要体现在参数上面)

package com.debug.weixin.service;

import java.util.Date;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.debug.weixin.message.res.CustomServiceMessage;
import com.debug.weixin.message.res.TextMessage;
import com.debug.weixin.pojo.TransInfo;
import com.debug.weixin.util.MessageUtil;

public class CoreService {
	 /** 
     * 处理微信发来的请求 
     *  
     * @param request 
     * @return 
     */  
    public static String processRequest( Map<String, String> requestMap) {  
        String respMessage = null;  
        TextMessage tm=null;
        try {  
            // 默认返回的文本消息内容  
            String respContent = "请求处理异常,请稍候尝试!";  
  
            // xml请求解析  
            //Map<String, String> requestMap = MessageUtil.parseXml(request);  
  
            // 发送方帐号(open_id)  
            String fromUserName = requestMap.get("FromUserName");  
            // 公众帐号  
            String toUserName = requestMap.get("ToUserName");  
            // 消息类型  
            String msgType = requestMap.get("MsgType");  
            
  
            // 回复文本消息  
            tm= new TextMessage();  
            tm.setToUserName(fromUserName);  
            tm.setFromUserName(toUserName);  
            tm.setCreateTime(new Date().getTime());  
            tm.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);  
           
            System.out.println("msgType:"+msgType);
            // 文本消息  
            if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { 
            	String content=requestMap.get("Content");
            	if(content.startsWith("人工客服")){
            		CustomServiceMessage cus=new CustomServiceMessage();
            		cus.setToUserName(fromUserName);  
            		cus.setFromUserName(toUserName);  
            		cus.setCreateTime(new Date().getTime());  
            		cus.setMsgType(MessageUtil.TRANSFER_CUSTOMER_SERVICE);  
            		TransInfo t=new TransInfo();
            		t.setKfAccount("debug@php-wechat-crm");
            		cus.setTransInfo(t);
            		
            		respMessage=MessageUtil.customMessageToXml(cus);
            		
            	}else{
            		 respContent = "您发送的是文本消息!"; 
            	}
                
            }  
            // 图片消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {  
                respContent = "您发送的是图片消息!";  
            }  
            // 地理位置消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {  
                respContent = "您发送的是地理位置消息!";  
            }  
            // 链接消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {  
                respContent = "您发送的是链接消息!";  
            }  
            // 音频消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {  
                respContent = "您发送的是音频消息!";  
            }  
            // 事件推送  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {  
                // 事件类型  
                String eventType = requestMap.get("Event");  
                // 订阅  
                if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) { 
                	String ticket=requestMap.get("Ticket");
                	if(ticket!=null&&!"".equals(ticket)){
                		respContent="谢谢您的关注!,你的ticket是:"+ticket;
                	}else{
                		respContent="谢谢您的关注!";
                	}
                	
                     
                }  
                // 取消订阅  
                else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {  
                    // TODO 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息  
                }  
                // 自定义菜单点击事件  
                else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {  
                    // TODO 自定义菜单权没有开放,暂不处理该类消息  
                	 String eventKey=requestMap.get("EventKey");
                     
                     if("language".equalsIgnoreCase(eventKey.trim())){
                    	 
                     }
                }else if(eventType.equals("SCAN")){
                	String eventKey=requestMap.get("EventKey");
                	String ticket=requestMap.get("Ticket");
                	respContent="你的ticket是:"+ticket;
                }  
            }  
  
            tm.setContent(respContent);  
            //respMessage = MessageUtil.textMessageToXml(textMessage);  
            
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        if(respMessage==null){
        	return MessageUtil.textMessageToXml(tm);
        }else{
        	return respMessage;
        }
        //return respMessage;  
    }  
}




评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值