微信登录并不复杂,其核心逻辑是每个用户对应每个应用都有唯一openId,根据openId进行用户的校验和处理。
//初始化方法
public static String getCodeUrl(String username) throws ClientProtocolException, IOException {
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
url = url.replace("APPID", appid);
url = url.replace("STATE", "1");
url = url.replace("REDIRECT_URI", URLEncoder.encode(redictUri, "utf-8"));
//System.out.println("----------微信取用户url="+url+",redictUri="+redictUri);
return url;
}
访问该url执行初始化,在微信上设置回调函数,将秘钥文件放在目录结构下,就可以回调了。(微信公众号必须是服务号,订阅号不支持)
//微信获取openId方法
//获取access_token,openId
public static Map getOpendId(String code) {
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=AppId&secret=AppSecret&code=CODE&grant_type=authorization_code";
url = url.replace("AppId", appid).replace("AppSecret", appsecret).replace("CODE", code);
Map headersParams = new HashMap<>();
//获取access_token,openId等
String result = HttpRequestUtil.sendPost(url, headersParams,"UTF-8");
System.out.println("获取accessToken和openId:url="+url+",返回结果:"+result);
JSONObject obj = JSONObject.parseObject(result);
//把返回参数(4个)返回
JSONObject resultObj = new JSONObject();
headersParams.put("openid",obj.getString("openid"));
headersParams.put("accessToken",obj.getString("access_token"));
headersParams.put("appid",appid);
headersParams.put("appname","应用名称");
return headersParams;
}
//获取用户信息
public static JSONObject getUserInfo(String openid,String accessToken){
//拉取用户信息
String url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
url = url.replace("ACCESS_TOKEN",accessToken);
url = url.replace("OPENID",openid);
String result = HttpRequestUtil.sendGet(url, "");
System.out.println("拉取用户信息:url="+url+",返回结果:"+result);
JSONObject obj = JSONObject.parseObject(result);
return obj;
}
-------------------------------------------微信分享相关-----------------------------------微信分享相对稍微复杂,主要是对签名的计算,首先需要引入微信的js文件,然后根据appid,随机数,时间戳和url进行加密运算,必须匹配才可分享
前端引入
前端需要引入的自定义js文件需要引入的内容:
function wx_ready_fenxiang(title, desc, imgUrl,url){
$.post("/weixin/share",{"url":url},function(result){
var appid,timestamp,nonceStr,signature;
appid = result.appId;
timestamp = result.timestamp;
nonceStr = result.nonceStr;
signature = result.signature;
wx.config({
debug: false,//debug模式会打印日志,微信端会弹框
appId: appid,
timestamp:timestamp,
nonceStr: nonceStr,
signature: signature,
jsApiList: [
'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone'
]
});
_wx(title, desc, imgUrl, 'link',url);
},"json")
}
function _wx(title, desc, imgUrl, type,url){
wx.ready(function() {
//朋友圈
wx.onMenuShareTimeline({
title: title, // 分享标题
link: url, // 分享链接
imgUrl: imgUrl, // 分享图标
success: function() {
// alert('谢谢分享');
// 用户确认分享后执行的回调函数
},
cancel: function() {
// alert('谢谢分享');
// 用户取消分享后执行的回调函数
}
});
//微信好友
wx.onMenuShareAppMessage({
title: title, // 分享标题
desc: desc, // 分享描述
link: url, // 分享链接
imgUrl: imgUrl, // 分享图标
type: type, // 分享类型,music、video或link,不填默认为link
// dataUrl: dataurl, // 如果type是music或video,则要提供数据链接,默认为空
success: function() {
// alert('谢谢分享');
},
cancel: function() {
// alert('分享失败');
// 用户取消分享后执行的回调函数
}
});
//qq好友
wx.onMenuShareQQ({
title: title, // 分享标题
desc: desc, // 分享描述
link: url, // 分享链接
imgUrl: imgUrl, // 分享图标
success: function() {
// 用户确认分享后执行的回调函数
},
cancel: function() {
// 用户取消分享后执行的回调函数
}
});
//腾讯微博
wx.onMenuShareWeibo({
title: title, // 分享标题
desc: desc, // 分享描述
link: url, // 分享链接
imgUrl: imgUrl, // 分享图标
success: function() {
// 用户确认分享后执行的回调函数
},
cancel: function() {
// 用户取消分享后执行的回调函数
}
});
//qq空间
wx.onMenuShareQZone({
title: title, // 分享标题
desc: desc, // 分享描述
link: url, // 分享链接
imgUrl: imgUrl, // 分享图标
success: function() {
// 用户确认分享后执行的回调函数
},
cancel: function() {
// 用户取消分享后执行的回调函数
}
});
// alert("wx ready 哈哈哈");
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
wx.error(function(res) {
// alert(res + "wx-error");
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
}
//调用方法
wx_ready_fenxiang("名称", "详情", '图片url',location.href)
JAVA后台部分:
//获取分享的token
public static String getShareAccessToken() throws IOException {
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ appid+"&secret="+appsecret;
Map headersParams = new HashMap<>();
String resultStr = HttpUtils.get(url,headersParams);
// sendGet:用get方法获取数据 ,具体请参考之间的关于微信的文章 http://www.cnblogs.com/jiduoduo/p/5749363.html
System.out.println("------------微信分享share的accessToken:url="+url+",返回结果:"+resultStr);
//返回结果
JSONObject obj = JSONObject.parseObject(resultStr);
String access_token = obj.getString("access_token");
return access_token;
}
//获取jsapi_ticket
public static String getJsapiTicket(String accessToken) throws IOException {
String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=" + accessToken;
Map headersParams = new HashMap<>();
String resultStr = HttpUtils.get(url, headersParams);
System.out.println("Redpack.WeixinUtils.getJsapiTicket获取JsapiTicket:url="+url+",返回结果:"+resultStr);
//返回结果
JSONObject obj = JSONObject.parseObject(resultStr);
String ticket = obj.getString("ticket");
return ticket;
}
//生成签名
public static String getSignature(String jsapi_ticket, String noncestr, Long timeStamp, String url) {
String string1 = "jsapi_ticket=JSAPI_TICKET&noncestr=NONCESTR×tamp=TIMESTAMP&url=URL";
string1 = string1.replace("JSAPI_TICKET", jsapi_ticket);
string1 = string1.replace("NONCESTR", noncestr);
string1 = string1.replace("TIMESTAMP", timeStamp.toString());
string1 = string1.replace("URL",url);
String signature= DigestUtils.shaHex(string1);
System.out.println("------生成签名string1="+string1+",结果signature="+signature);
return signature;
}
Controller层,就是前台js引用的接口:
@RequestMapping("/share")
@ResponseBody
public JSONObject share(HttpServletRequest request,@RequestParam String url) throws IOException {
System.out.println("----------------------------微信分享url="+url);
//url = url.replace("sendfriend","redpack/get");
//时间戳(10位)
Long timeStamp = System.currentTimeMillis()/1000;
//随机数(可以任意指定)
String noncestr="12345678";
//获得jsapi_ticket
//1.拿到accessToken
String accessToken = WeixinUtils.getShareAccessToken();
//此处accessToken过期时间为2个小时,第一次取可以存入redis,两个小时之内有效,如果accessToken尚在有效期内,再次取是会报错的。
String jsapi_ticket = WeixinUtils.getJsapiTicket(accessToken);
//获得签名
String signature = WeixinUtils.getSignature(jsapi_ticket,noncestr,timeStamp,url);
//Map param = WeixinUtils.getOpendId(code);
JSONObject obj = new JSONObject();
obj.put("appId",PropertyConstants.getValue("com.tencent.weixin.appid"));
obj.put("timestamp",timeStamp);
obj.put("nonceStr",noncestr);
obj.put("signature",signature);
obj.put("jsapi_ticket",jsapi_ticket);
obj.put("url",url);
return obj;
}
注意:1.时间戳必须是10位,13位的时间戳是无法取到正确的值的。
2.随机数可以任意指定,不需要太复杂,我就设的12345678
3.可否分享就是看签名校验是否正确,可以在微信公众号开发者工具里的网址进行计算,匹配则可分享。
4.分享标题和内容是有敏感词的,比如红包,现金等,具体请查阅微信官方的说明。
5.分享只能分享当前页,不能分享其他页。