【微信公众号开发】【2】注册搭建属于自己的公众号

前言:

1,正式的公众号需要在微信公众平台申请https://mp.weixin.qq.com/);

     也可以先申请测试号进行开发,测试帐号申请https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

    我这边是先申请的测试号

2,公众号有两种模式,两种模式不能并存

      编辑模式:针对非编程人员及信息发布类公众帐号使用。公众平台就相当于该公众号的管理后台。

      开发模式:针对具备开发能力的人使用。相关的功能是通过调用微信的接口来实现公众平台就像是负责授权的接口中心

3,我的开发环境为:Eclipse + Java + Spring

正文:

1,测试号管理

     进入上面的测试号网址,可得到测试号相关的信息,

      其中的较为重要的是:appID,appsecret

      其中的需要我们处理的有:

     (1)接口配置信息

             URL :外部能访问到的处理微信交互的地址

             Token:自己指定,保持程序中与该页面一致即可

      (2)JS接口安全域名

             域名 :在该域名下通过验证方可调用JSJDK

     (3)OAuth2.0网页授权

               体验接口权限表-网页服务-网页帐号-网页授权获取用户基本信息

               授权回调页面域名:与上面一致即可(使用OAuth2.0的url时带的参数要和这里一致)

2,基本要素

         公众号调用接口并不是无限制的!(认证帐号可以对实时调用量清零;测试账号次数少很多)

         接口调用视情况会需要两个参数(access_token,jsapi_ticket)有效时间为7200秒,且新获取的会使旧的失效)

         解决方案:

        (1)将参数存到properties文件中,使用时直接读取,重启服务及每两小时更新一次文件

         更新:

//重启后加载
	@PostConstruct  
	public void init() {
		refreshProperties();  
	} 

        //更新access_token及jsapi_ticket
	@Scheduled(cron = "0 0 0/2 * * ?")	//每两小时
	public void refreshProperties() { 	
        try {
			writeProperties(Config.crcTokenFileName, Config.AppId(), Config.AppSecret());
		} catch (Exception e) {
			logger.info("更新access_token及jsapi_ticket失败:" + e);
		}
	}

         更新properties文件中的数据

public static void writeProperties(String fileName, String appId, String appSecret) throws Exception 
	{
		String url = "https://api.weixin.qq.com/cgi-bin/token";
		String ticketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";

		String jsonStrToken = HttpRequest.sendGet(url, "grant_type=client_credential&appid="+ appId + "&secret=" + appSecret);       
		JSONObject json = JSONObject.fromObject(jsonStrToken);
		String access_token = (String) json.getString("access_token");
		logger.info("access_token:"+access_token);

		String jsonStrTicket = HttpRequest.sendGet(ticketUrl, "access_token=" + access_token + "&type=jsapi");
		JSONObject ticketJson = JSONObject.fromObject(jsonStrTicket);
		String ticket = (String) ticketJson.get("ticket"); 
		logger.info("ticket:"+ticket);      

		PropertiesUtil.writeData(fileName, ACCESS_TOKEN, access_token);        
		PropertiesUtil.writeData(fileName, JSAPI_TICKET, ticket);     
	}

         上文中的HttpRequest.sendGet方法
//向指定URL发送GET方法的请求
    public static String sendGet(String url, String param) {
		String result = "";
		BufferedReader in = null;
		try {
			String urlNameString = url + "?" + param;
			URL realUrl = new URL(urlNameString);
			// 打开和URL之间的连接
			URLConnection connection = realUrl.openConnection();
			// 设置通用的请求属性
			connection.setRequestProperty("accept", "*/*");
			connection.setRequestProperty("connection", "Keep-Alive");
			// 建立实际的连接
			connection.connect();
			// 获取所有响应头字段
			Map<String, List<String>> map = connection.getHeaderFields();
			// 遍历所有的响应头字段
			for (String key : map.keySet()) {
				logger.info(key + "--->" + map.get(key));
			}
			// 定义 BufferedReader输入流来读取URL的响应
			in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				result += line;
			}

        } catch (Exception e) {
			logger.info("发送GET请求出现异常!" + e);
			e.printStackTrace();
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {in.close();}
           } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
       return result;
 	}

          读取: fileName为properties文件的名称(如:tt.properties) 

public static String getAccessToken(String fileName, String appId, String appSecret) 
	{
		String access_token = PropertiesUtil.readData(fileName, ACCESS_TOKEN);

		return access_token;
	}

	public static String getWeiXinTicket(String fileName, String appId,String appSecret)
	{
		String ticket = PropertiesUtil.readData(fileName, JSAPI_TICKET);

		return ticket;
	}

           从properties文件中读取数据

public static String readData(String fileName, String key) 
	{  
        	filePath = PropertiesUtil.class.getResource("/" + fileName).toString();  //获取绝对路径           
        	filePath = filePath.substring(6);  //截掉路径的"file:"前缀 
        	Properties props = new Properties();
		
        	try {  
            		InputStream in = new BufferedInputStream(new FileInputStream(fileName));  
            		props.load(in);  
            		in.close();  
            		String value = props.getProperty(key);  
            		return value;  
        	} catch (Exception e) {  
            		e.printStackTrace();  
            		return null;  
        	}  
  }

3,代码(Java)

          (1)接口配置信息  URL  需经过微信验证

@ResponseBody
	@RequestMapping(value = "coreServlet", method = RequestMethod.GET)
	public void coreServletGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{	
	    //微信验证地址
	    String signature = request.getParameter("signature"); //加密签名 
        String timestamp = request.getParameter("timestamp"); // 时间戳            
        String nonce = request.getParameter("nonce"); // 随机数          
        String echostr = request.getParameter("echostr"); // 随机字符串  
  
        PrintWriter out = response.getWriter();  
        // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败  
        if (Sign.checkSignature(Config.token(), signature, timestamp, nonce)) {  
            out.print(echostr);  
        }  
        out.close();  
        out = null;
	}

            验证签名  上文中的Sign.checkSignature

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);  
		StringBuilder content = new StringBuilder();  
		for (int i = 0; i < arr.length; i++) {  
			content.append(arr[i]);  
		}  
		MessageDigest md = null;  
		String tmpStr = null;  
  
		try {  
			md = MessageDigest.getInstance("SHA-1");  
			// 将三个参数字符串拼接成一个字符串进行sha1加密  
			byte[] digest = md.digest(content.toString().getBytes());  
			tmpStr = byteToStr(digest);  
		} catch (NoSuchAlgorithmException e) {  
			e.printStackTrace();  
		}  
  
		content = null;  
		// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信  
		return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;  
	}
	//将字节数组转换为十六进制字符串 
	private static String byteToStr(byte[] byteArray) 
	{  
           String strDigest = "";  
        for (int i = 0; i < byteArray.length; i++) {  
            strDigest += byteToHexStr(byteArray[i]);  
        }  
        return strDigest;  
    }  


    private static String byteToHexStr(byte mByte) 
	{  
        char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };  
        char[] tempArr = new char[2];  
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];  
        tempArr[1] = Digit[mByte & 0X0F];  
  
        String s = new String(tempArr);  
        return s;  
    }

(2)JS接口安全域名 JS发送请求到后台进行验证

           前台JS

$.ajax({
        type : "post",
        url : "/getTicket",
        data : {
        	"url":wxUrl
        },
        dataType : "json",
        success : function(data){
            var obj = data;
            wx.config({
                debug: false, //开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                appId: obj.appId, //必填,公众号的唯一标识
                timestamp: obj.timestamp, //必填,生成签名的时间戳
                nonceStr: obj.nonceStr, //必填,生成签名的随机串
                signature: obj.signature,//必填,签名,见微信开发文档附录1
                jsApiList: ['chooseImage','previewImage','uploadImage','downloadImage'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
            });
        }


     });

           后台验证:  

//调用JSJDK前的验证
	@ResponseBody
	@RequestMapping(value = "getTicket", method = RequestMethod.POST)
	public Map<String, String> getTicket(String url) throws Exception 
	{	
	     //获取jsapi_ticket,是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。	
	     String jsapi_ticket = WeixinUtil.getWeiXinTicket(Config.joinTokenFileName, Config.appId(), Config.appSecret());
         //URL为要调用接口的页面路径
	     Map<String, String> ret = Sign.sign(Config.appId(), jsapi_ticket, url);

	     return ret;
	}

           WeixinUtil.getWeiXinTicket即为2中的获取jsapi_ticket方法

           Sign.sign

public static Map<String, String> sign(String appId, String jsapi_ticket, String url) 
	{
        Map<String, String> ret = new HashMap<String, String>();
        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;

        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);
        ret.put("appId", appId);
        
        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);
    }









     

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值