java微信分享朋友圈_java开发微信分享到朋友圈功能

微信分享功能开发

用了一天时间,把微信发送给朋友和分享到朋友圈功能开发出来,在这里给大家分享一下,避免大家走弯路。

一、服务器端程序

package com.wiimedia.controller;

import java.io.IOException;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Arrays;

import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import com.google.gson.Gson;

import com.wiimedia.model.Ticket;

import com.wiimedia.service.ArticleSolrService;

import com.wiimedia.service.TicketRepository;

import com.wiimedia.service.TicketRepositorySolr;

import com.wiimedia.utils.GetRandomStr;

import com.wiimedia.utils.SignatureBean;

import com.wiimedia.utils.weixin.WeixinUtil;

/**

*

*

*

Project:mryl_phone_v2

*

*

Package:com.wiimedia.controller

*

*

Description:微信分享Controller

*

*

Company:Wiimedia

*

*@Athor:SongJia

*

*@Date:2016-7-15 上午09:34:10

*

*/

@Controller

@RequestMapping("/WeixinshareController/Api/Inteface")

public class WeixinshareController {

@Autowired

private TicketRepositorySolr ticketRepositorySolr;

@RequestMapping("/getSignature")

public String getSignature( HttpServletRequest request,

HttpServletResponse response) throws IOException, ParseException{

//获取签名页面链接

String url = request.getParameter("url");

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

//从数据库中获取标签,并检查标签是否过期

Ticket oldticket = ticketRepositorySolr.getTicketById("20160114wiimediamrylsong1152");

if(oldticket==null){//第一次访问,标签不存在。

executeTicket(response,"1",url,format);

return null;

}else{//标签存在,判断标签是否超时

String oldAcquiretime = oldticket.getAcquiretime();

long difference=format.parse(format.format(new Date())).getTime()-format.parse(oldAcquiretime).getTime();

if(difference>7100000){//标签超时,重新到微信服务器请求标签超时时间为7200秒(7200000毫秒)

executeTicket(response,"2",url,format);

return null;

}else{//标签未超时

/**

* 注意事项

* 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。

* 2.签名用的url必须是调用JS接口页面的完整URL。

* 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。

*

****根据第1点要求 signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端***

*/

String signature = signature(oldticket.getTicket(),oldticket.getTimestamp(),oldticket.getNoncestr(),url);

SignatureBean signatureBean = new SignatureBean();

signatureBean.setNoncestr(oldticket.getNoncestr());

signatureBean.setSignature(signature);

signatureBean.setTimestamp(oldticket.getTimestamp());

signatureBean.setUrl(url);

response.setContentType("text/html;charset=UTF-8");

response.getWriter().print(new Gson().toJson(signatureBean));

return null;

}

}

}

/**

*

*

Project:mryl_phone_v2

*

*

:mryl_phone_v2

*

*

Description:更新和获取ticket的方法,因为用的solr所以更新和新增是一样的ID无则添加,有责更新

*

*

Company:Wiimedia

*

*@Athor:SongJia

*

*@Date:2016-7-15 上午09:45:00

*

*/

public void executeTicket(HttpServletResponse response,String flag,String url,SimpleDateFormat format) throws IOException{

//获取签名随即字符串

GetRandomStr randomStr = new GetRandomStr();

String noncestr = randomStr.getRandomString(15);

//获取签名时间戳

String timestamp = Long.toString(System.currentTimeMillis());

//请求accessToken

String accessTokenUrl ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=您的APPID&secret=您的密匙";

String tokenJson = WeixinUtil.httpRequest(accessTokenUrl, "GET", null);

Gson gson = new Gson();

ShareAccess_Token token = gson.fromJson(tokenJson, ShareAccess_Token.class);

String to= token.getAccess_token();

//获取标签

String urlTicket ="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+to+"&type=jsapi";

String ticketJson = WeixinUtil.httpRequest(urlTicket, "GET", null);

Ticket ticket = gson.fromJson(ticketJson, Ticket.class);

String t = ticket.getTicket();

//String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");

//我的Ticket ID是写死的

String acquiretime = format.format(new Date());

ticket.setTid("20160114wiimediamrylsong1152");

ticket.setAcquiretime(acquiretime);

ticket.setTimestamp(timestamp);

ticket.setNoncestr(noncestr);

//因为用的SOLR所以更新和添加的方法是一样的,可以根据自己具体需求进行修改,本文不再贴出代码.

if(flag.equals("2")){

ticketRepositorySolr.addTicketToSolr(ticket);

}else{

ticketRepositorySolr.addTicketToSolr(ticket);

}

/**

* 注意事项

* 1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。

* 2.签名用的url必须是调用JS接口页面的完整URL。

* 3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。

*

*根据第1点要求 signature 配置的时候很容易出错,需要把生成 Ticket的 noncestr和 timestamp传给客户端*

*/

String signature = signature(t,timestamp,noncestr,url);

SignatureBean signatureBean = new SignatureBean();

signatureBean.setNoncestr(noncestr);

signatureBean.setSignature(signature);

signatureBean.setTimestamp(timestamp);

signatureBean.setUrl(url);

response.setContentType("text/html;charset=UTF-8");

response.getWriter().print(new Gson().toJson(signatureBean));

}

/**

*

*

Project:mryl_phone_v2

*

*

:mryl_phone_v2

*

*

Description:根据标签,时间戳,密匙,URL进行签名

*

*

Company:Wiimedia

*

*@Athor:SongJia

*

*@Date:2016-7-15 上午09:37:13

*

*/

private String signature(String jsapi_ticket, String timestamp, String noncestr, String url) {

jsapi_ticket = "jsapi_ticket=" + jsapi_ticket;

timestamp = "timestamp=" + timestamp;

noncestr = "noncestr=" + noncestr;

url = "url=" + url;

String[] arr = new String[] { jsapi_ticket, timestamp, noncestr, url };

// 将token、timestamp、nonce,url参数进行字典序排序

Arrays.sort(arr);

StringBuilder content = new StringBuilder();

for (int i = 0; i < arr.length; i++) {

content.append(arr[i]);

if (i != arr.length - 1) {

content.append("&");

}

}

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;

return tmpStr;

}

/**

* 将字节转换为十六进制字符串

*

* @param mByte

* @return

*/

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;

}

/**

* 将字节数组转换为十六进制字符串

*

* @param byteArray

* @return

*/

private static String byteToStr(byte[] byteArray) {

String strDigest = "";

for (int i = 0; i < byteArray.length; i++) {

strDigest += byteToHexStr(byteArray[i]);

}

return strDigest;

}

class ShareAccess_Token{

private String access_token;

private String expires_in;

public String getAccess_token() {

return access_token;

}

public void setAccess_token(String accessToken) {

access_token = accessToken;

}

public String getExpires_in() {

return expires_in;

}

public void setExpires_in(String expiresIn) {

expires_in = expiresIn;

}

}

}

二、客户端代码.

var url = window.location.href;

var articleId = "";

var shareTitle="明日医疗资讯";

var shareImgUrl="";

var userinfo = localStorage.getItem("_userinfo");

var timestamp;

var noncestr;

var signature;

//获取签名

$.ajax({

type: "GET",

url: "WeixinshareController/Api/Inteface/getSignature",

//data:{timestamp:timestamp,noncestr:noncestr,url:url},

data:{url:url},

success: function(data){

var objData=JSON.parse(data);

timestamp=objData.timestamp;

noncestr=objData.noncestr;

signature=objData.signature;

console.log(objData);

wxShare();

}

});

function wxShare(){

wx.config({

debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。

appId: '您的appid', // 和获取Ticke的必须一样------必填,公众号的唯一标识

timestamp:timestamp, // 必填,生成签名的时间戳

nonceStr: noncestr, // 必填,生成签名的随机串

signature: signature,// 必填,签名,见附录1

jsApiList: [

'onMenuShareAppMessage'

] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2

});

}

wx.ready(function(){

//config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,

//config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关

//接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。

//----------“分享给朋友”

wx.onMenuShareAppMessage({

title: "明日医疗资讯", // 分享标题

desc: shareTitle, // 分享描述

link: url, // 分享链接

imgUrl: shareImgUrl, // 分享图标

type: '', // 分享类型,music、video或link,不填默认为link

dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空

success: function () {

// 用户确认分享后执行的回调函数、

},

cancel: function () {

// 用户取消分享后执行的回调函数

}

});

//------------"分享到朋友圈"

wx.onMenuShareTimeline({

title: '明日医疗资讯', // 分享标题

link: '', // 分享链接

imgUrl: shareImgUrl, // 分享图标

success: function () {

// 用户确认分享后执行的回调函数

},

cancel: function () {

// 用户取消分享后执行的回调函数

}

});

//-------------分享到QQ

wx.onMenuShareQQ({

title: '明日医疗资讯', // 分享标题

desc: shareTitle, // 分享描述

link: '', // 分享链接

imgUrl: shareImgUrl, // 分享图标

success: function () {

// 用户确认分享后执行的回调函数

},

cancel: function () {

// 用户取消分享后执行的回调函数

}

});

//-------------分享到QQ空间

wx.onMenuShareQZone({

title: '明日医疗资讯', // 分享标题

desc: shareTitle, // 分享描述

link: '', // 分享链接

imgUrl: shareImgUrl, // 分享图标

success: function () {

// 用户确认分享后执行的回调函数

},

cancel: function () {

// 用户取消分享后执行的回调函数

}

});

});

三、服务器需要的工具类和Model

① Ticket

package com.wiimedia.model;

public class Ticket{

private String tid;

private String ticket;

private String errcode;

private String errmsg;

private String expires_in;

private String acquiretime;

private String noncestr;

private String timestamp;

public Ticket(String tid, String ticket, String errcode, String errmsg,

String expiresIn, String acquiretime, String noncestr,

String timestamp) {

super();

this.tid = tid;

this.ticket = ticket;

this.errcode = errcode;

this.errmsg = errmsg;

expires_in = expiresIn;

this.acquiretime = acquiretime;

this.noncestr = noncestr;

this.timestamp = timestamp;

}

public String getTid() {

return tid;

}

public void setTid(String tid) {

this.tid = tid;

}

public String getTicket() {

return ticket;

}

public void setTicket(String ticket) {

this.ticket = ticket;

}

public String getErrcode() {

return errcode;

}

public void setErrcode(String errcode) {

this.errcode = errcode;

}

public String getErrmsg() {

return errmsg;

}

public void setErrmsg(String errmsg) {

this.errmsg = errmsg;

}

public String getExpires_in() {

return expires_in;

}

public void setExpires_in(String expiresIn) {

expires_in = expiresIn;

}

public String getAcquiretime() {

return acquiretime;

}

public void setAcquiretime(String acquiretime) {

this.acquiretime = acquiretime;

}

public String getNoncestr() {

return noncestr;

}

public void setNoncestr(String noncestr) {

this.noncestr = noncestr;

}

public String getTimestamp() {

return timestamp;

}

public void setTimestamp(String timestamp) {

this.timestamp = timestamp;

}

}

② 添加到数据库的业务根据自己需要进行实现.

③ GetRandomStr

package com.wiimedia.utils;

import java.util.Random;

public class GetRandomStr {

/**

*

*

Project:mryl_phone_v2

*

*

:mryl_phone_v2

*

*

Description:生成随即字符串

*

*

Company:Wiimedia

*

*@Athor:SongJia

*

*@Date:2016-7-14 上午11:14:46

*

*/

public String getRandomString(int length) {

String base = "abcdefghijklmnopqrstuvwxyz0123456789";

Random random = new Random();

StringBuffer sb = new StringBuffer();

for (int i = 0; i < length; i++) {

int number = random.nextInt(base.length());

sb.append(base.charAt(number));

}

return sb.toString();

}

}

④ SignatureBean

package com.wiimedia.utils;

public class SignatureBean {

private String noncestr;

private String url;

private String timestamp;

private String signature;

public String getNoncestr() {

return noncestr;

}

public void setNoncestr(String noncestr) {

this.noncestr = noncestr;

}

public String getUrl() {

return url;

}

public void setUrl(String url) {

this.url = url;

}

public String getTimestamp() {

return timestamp;

}

public void setTimestamp(String timestamp) {

this.timestamp = timestamp;

}

public String getSignature() {

return signature;

}

public void setSignature(String signature) {

this.signature = signature;

}

}

⑤ WeixinUtil

package com.wiimedia.utils.weixin;

import java.io.BufferedReader;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.net.ConnectException;

import java.net.URL;

import javax.net.ssl.HttpsURLConnection;

import javax.net.ssl.SSLContext;

import javax.net.ssl.SSLSocketFactory;

import javax.net.ssl.TrustManager;

/**

*

*

Project:mryl_phone_v2

*

*

:mryl_phone_v2

*

*

Description:公众平台接口工具类

*

*

Company:Wiimedia

*

*@Athor:SongJia

*

*@Date:2016-7-15 上午09:37:13

*

*/

public class WeixinUtil {

/**

* 发起https请求并获取结果

*

* @param requestUrl 请求地址

* @param requestMethod 请求方式(GET、POST)

* @param outputStr 提交的数据

* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)

*/

public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {

StringBuffer buffer = new StringBuffer();

try {

// 创建SSLContext对象,并使用我们指定的信任管理器初始化

TrustManager[] tm = { new MyX509TrustManager() };

SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");

sslContext.init(null, tm, new java.security.SecureRandom());

// 从上述SSLContext对象中得到SSLSocketFactory对象

SSLSocketFactory ssf = sslContext.getSocketFactory();

URL url = new URL(requestUrl);

HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();

httpUrlConn.setSSLSocketFactory(ssf);

httpUrlConn.setDoOutput(true);

httpUrlConn.setDoInput(true);

httpUrlConn.setUseCaches(false);

// 设置请求方式(GET/POST)

httpUrlConn.setRequestMethod(requestMethod);

if ("GET".equalsIgnoreCase(requestMethod))

httpUrlConn.connect();

// 当有数据需要提交时

if (null != outputStr) {

OutputStream outputStream = httpUrlConn.getOutputStream();

// 注意编码格式,防止中文乱码

outputStream.write(outputStr.getBytes("UTF-8"));

outputStream.close();

}

// 将返回的输入流转换成字符串

InputStream inputStream = httpUrlConn.getInputStream();

InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");

BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

String str = null;

while ((str = bufferedReader.readLine()) != null) {

buffer.append(str);

}

bufferedReader.close();

inputStreamReader.close();

// 释放资源

inputStream.close();

inputStream = null;

httpUrlConn.disconnect();

return buffer.toString();

} catch (ConnectException ce) {

ce.printStackTrace();

} catch (Exception e) {

e.printStackTrace();

}

return "";

}

}

四、至此,分享功能已经开发完成,但是,在生成signature的时候会遇到很多问题,这里提供一些wx.config失败的排错方法。

① 确认自己的生成的signature是否正确

在微信提供的http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign进行校验

② wx.config中使用的noncestr, timestamp与用以签名中的对应noncestr, timestamp是否一致一致…如上面(一.服务器代码)

(有可能因为JS页面加载顺序问题,服务器生成的signature,noncestr,timestamp在wx.config中没有获取到)。

③ 确认url是页面完整的url,包括GET参数部分

需要去掉#后面的部分

④ config 中的 appid 与用来获取 jsapi_ticket 的 appid 是否一致

⑤ 报错{errmsg:config:ok}是debug的正常返回把调试模式关掉就OK

wx.config debug: false,

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
继“Java开发微信朋友圈PC版系统-架构1.0”之后,debug这段时间日撸夜撸,终于赶在春节放假前给诸位带来了这一系统的架构2.0版本,特此分享给诸位进行学习,以掌握、巩固更多的技术栈以及项目和产品开发经验,同时也为即将到来的金三银四跳槽季做准备! 言归正传,下面仍然以问答的方式介绍下本门课程的相关内容! (1)问题一:这是一门什么样的课程? 很明显,本门课程是建立在架构1.0,即 第1门课程 的基础上发布的,包含了架构1.0的内容,即它仍然是一门项目、产品实战课,基于Spring Boot2.X + 分布式中间件开发的一款类似“新浪微博”、“QQ空间”、“微信朋友圈”PC版的互联网社交软件,包含完整的门户网前端 以及 后台系统管理端,可以说是一套相当完整的系统! (2)问题二:架构2.0融入了哪些新技术以及各自有什么作用? 本课程对应着系统架构2.0,即第2阶段,主要目标:基于架构1.0,优化系统的整体性能,实现一个真正的互联网社交产品;其中,可以学习到的技术干货非常多,包括:系统架构设计、Spring Boot2.X、缓存Redis、多线程并发编程、消息中间件RabbitMQ、全文搜索引擎Elastic Search、前后端消息实时通知WebSocket、分布式任务调度中间件Elastic Job、Http Restful编程、Http通信OKHttp3、分布式全局唯一ID、雪花算法SnowFlake、注册中心ZooKeeper、Shiro+Redis 集群Session共享、敏感词自动过滤、Java8 等等; A.  基于Elastic Search实现首页列表数据的初始化加载、首页全文检索;B.  基于缓存Redis缓存首页朋友圈“是否已点赞、收藏、关注、评论、转发”等统计数据;整合Shiro实现集群部署模式下Session共享;C.  多线程并发编程并发处理系统产生的废弃图片、文件数据;D.  基于Elastic Job切片作业调度分布式多线程清理系统产生的废弃图片;E.  基于RabbitMQ解耦同步调用的服务模块,实现服务模块之间异步通信;F.  基于WebSocket实现系统后端 与 首页前端 当前登录用户实时消息通知;G.  基于OKHttp3、Restful风格的Rest API实现ES文档、分词数据存储与检索;H.  分布式全局唯一ID 雪花算法SnowFlake实现朋友圈图片的唯一命名;I.  ZooKeeper充当Elastic Job创建的系统作业的注册中心;J.  为塑造一个健康的网络环境,对用户发的朋友圈、评论、回复内容进行敏感词过滤;K.  大量优雅的Java8  Lambda编程、Stream编程;  (3)问题三:系统运行起来有效果图看吗?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值