实现微信订阅号强制关注

众所周知,微信订阅号是没有网页授权接口权限的,因此不可以直接判断用户的关注状态,但并不意味没有其他方法可以实现,下面就是一种还不错的方法。

首先你需要一个服务号,并通过微信认证,然后在微信开放平台http://open.weixin.qq.com注册一个帐号,将你的服务号和订阅号都绑定到开放平台,绑定后如下图:


绑定后,订阅号和服务号将通过unionid关联用户信息,也就是说同个openid调用服务号拉取用户信息接口和订阅号获取用户信息接口都将返回相同的unionid

具体实现步骤和原理:

1.同步订阅号用户列表和用户信息(如果您的订阅号已经在运营),保存到数据库表(包括unionid、关注状态等)

2.用服务号做网页授权,拉取用户信息,获取unionid

3.根据unionid在数据表查询用户关注状态

4.开发订阅号事件接口,通过取消关注事件更新数据表用户关注状态


同步订阅号用户列表和用户信息代码

@RequestMapping(value="/syncOpenid")
	@ResponseBody
	public String syncWxOpenid(final HttpServletRequest request){
		if(WeixinUtil.syncOpenidStatus){
			return JSON.toJSONString(new DwzAjaxResult("300", "同步已在进行", "", "", ""));
		}
		final WxToken token = wxTokenService.getWxTokenByKey(WX_TOKEN_KEY.NORMAL_ACCESS_TOKEN.getKey());
		if(token == null || !token.isActive() || token.getToken_value() == null || "".equals(token.getToken_value())){
			return JSON.toJSONString(new DwzAjaxResult("300", "获取AccessToken失败", "", "", ""));
		}
		//标识Openid已经在同步状态
		WeixinUtil.syncOpenidStatus = true;
		//获取最后一个Openid,从最后一个开始
		//final WxOpenid lastOpenid = wxOpenidService.getLatestOpenid();
		Runnable orun = new Runnable() {
			private String nextOpenId;
			private WxToken tk;
			private boolean flag = true;
			@Override
			public void run() {
				if(tk == null){
					//初始化token
					tk = token;
				}
//				if(nextOpenId == null && lastOpenid != null && lastOpenid.getOpen_id() != null){
//					nextOpenId = lastOpenid.getOpen_id();
//				}
				System.out.println("= Sync openid start =");
				while(flag){
					//调用微信用户列表接口
					try {
						JSONObject jo = getUserList(nextOpenId);
						if(jo != null && !jo.containsKey("errcode")){
							System.out.println("GET OPENID LIST,total="+jo.getLongValue("total")+",count="+jo.getIntValue("count")+",next_openid="+jo.getString("next_openid"));
							//设置下一个查询起始Openid
							nextOpenId = jo.getString("next_openid");
							JSONObject data = jo.getJSONObject("data");
							if(data != null){
								//获取open id列表
								JSONArray openidArray = data.getJSONArray("openid");
								wxOpenidService.addWxOpenidList(openidArray);
							}
							if(jo.getIntValue("count") == 0 || nextOpenId == null || "".equals(nextOpenId)){
								shutdown();
							}
						}
					} catch (Exception e) {
						e.printStackTrace();
						request.getServletContext().setAttribute("openid_sync", "0");
					}
				}
			}
			public void shutdown(){
				WeixinUtil.syncOpenidStatus = false;
				System.out.println("= Sync openid complete =");
				flag = false;
			}
		};
		Thread th = new Thread(orun);
		th.start();
		DwzAjaxResult dwzResult = new DwzAjaxResult("200", "同步已开始至后台进行,请耐心等待!", "wxOpenidList", "", "");
		return JSON.toJSONString(dwzResult);
	}
	
	private JSONObject getUserList(String nextOpenid){
		JSONObject json = WeixinUtil.getUserList(nextOpenid, getWxAccessToken());
		if(json.containsKey("errcode")){
			if(json.getString("errmsg").indexOf("access_token is invalid")!=-1){
				//access token过期,则需要刷新access token
				JSONObject jo = WeixinUtil.getAccessToken();
				if(!jo.containsKey("errcode")){
					WxToken token = new WxToken();
					token.setToken_key(WX_TOKEN_KEY.NORMAL_ACCESS_TOKEN.getKey());
					token.setCreate_time(System.currentTimeMillis());
					token.setToken_value(jo.getString("access_token"));
					token.setExpire_seconds(jo.getInteger("expires_in"));
					wxTokenService.addNewWxTokenOrUpdate(token);
					
					json = WeixinUtil.getUserList(nextOpenid, token.getToken_value());
				}else{
					System.out.println("REGET ACESS TOKEN ERROR:" + JSON.toJSONString(jo));
				}
			}
		}
		return json;
	}
	
	@RequestMapping(value="/syncUserInfo")
	@ResponseBody
	public String syncUserInfo(final HttpServletRequest request){
		if(WeixinUtil.syncUserInfoStatus){
			return JSON.toJSONString(new DwzAjaxResult("300", "同步已在进行", "", "", ""));
		}
		final WxToken token = wxTokenService.getWxTokenByKey(WX_TOKEN_KEY.NORMAL_ACCESS_TOKEN.getKey());
		if(token == null || !token.isActive() || token.getToken_value() == null || "".equals(token.getToken_value())){
			return JSON.toJSONString(new DwzAjaxResult("300", "获取AccessToken失败", "", "", ""));
		}
		//标识Openid已经在同步状态
		WeixinUtil.syncUserInfoStatus = true;
		Runnable orun = new Runnable() {
			private WxToken tk;
			private boolean flag = true;
			private long page = 1;
			private int psize = 100;
			
			@Override
			public void run() {
				if(tk == null){
					//初始化token
					tk = token;
				}
				System.out.println("= Sync userinfo start =");
				
				while(flag){
					try {
						List<WxOpenid> list = wxOpenidService.getWxOpenidsWithoutUnionid(page, psize);
						if(list.size() == 0 || list.size() < psize){
							shutdown();
						}
						for(WxOpenid wo : list){
							//调用微信用户基本信息接口
							WxOpenid userInfo = getUserInfo(wo.getOpenid());
							wxOpenidService.addNewWxOpenidOrUpdate(userInfo);
						}
						page += 1;
					} catch (Exception e) {
						e.printStackTrace();
						request.getServletContext().setAttribute("userinfo_sync", "0");
					}
				}
			}
			public void shutdown(){
				flag = false;
				WeixinUtil.syncUserInfoStatus = false;
				System.out.println("= Sync userinfo complete =");
				
			}
		};
		Thread th = new Thread(orun);
		th.start();
		DwzAjaxResult dwzResult = new DwzAjaxResult("200", "同步已开始至后台进行,请耐心等待!", "wxOpenidList", "", "");
		return JSON.toJSONString(dwzResult);
	}
	
	private WxOpenid getUserInfo(String openid){
		JSONObject json = WeixinUtil.getUserInfo(openid, getWxAccessToken());
		if(json.containsKey("errcode")){
			if(json.getString("errmsg").indexOf("access_token is invalid")!=-1){
				//access token过期,则需要刷新access token
				JSONObject jo = WeixinUtil.getAccessToken();
				if(!jo.containsKey("errcode")){
					WxToken token = new WxToken();
					token.setToken_key(WX_TOKEN_KEY.NORMAL_ACCESS_TOKEN.getKey());
					token.setCreate_time(System.currentTimeMillis());
					token.setToken_value(jo.getString("access_token"));
					token.setExpire_seconds(jo.getInteger("expires_in"));
					wxTokenService.addNewWxTokenOrUpdate(token);
					
					json = WeixinUtil.getUserInfo(openid, token.getToken_value());
				}else{
					System.out.println("REGET ACESS TOKEN ERROR:" + JSON.toJSONString(jo));
				}
			}
		}
		return JSONObject.parseObject(json.toJSONString(), WxOpenid.class);
	}
	
	private String getWxAccessToken(){
		WxToken token = wxTokenService.getWxTokenByKey(WX_TOKEN_KEY.NORMAL_ACCESS_TOKEN.getKey());
		if(token != null){
			return token.getToken_value();
		}
		return "";
	}
获取用户列表
public void addWxOpenidList(JSONArray openidArray) {
		WxOpenid wxo = new WxOpenid();
		wxo.setSubscribe(1);	//只添加openid列表,默认为关注状态为:已关注
		for(int i=0; i<openidArray.size(); i++){
			Object obj = openidArray.get(i);
			if(obj == null) continue;
			wxo.setOpenid(obj.toString());
			List<WxOpenid> list = wxOpenidMapper.selectByCondition(wxo);
			if(list.size() > 0){
				//union id 已经存在, 若果Open id不存在,则添加open id
				wxo = list.get(0);
				if(wxo.getSubscribe() != 1){
					wxo.setSubscribe(1);
					wxOpenidMapper.update(wxo);
				}
			}else{
				wxOpenidMapper.save(wxo);
			}
		}
	}

public static JSONObject getUserInfo(String openid, String accessToken){
		String resp = CommonUtil.httpsRequest("https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openid, "GET", null);
		JSONObject jo = JSONObject.parseObject(resp);
		if(jo.containsKey("errcode")){
			log.error("[WAP] GET USER INFO ERROR: " + jo.getString("errcode") + ":" + jo.getString("errmsg"));	
		}
		return jo;
	}

保存或更新token

public WxToken addNewWxTokenOrUpdate(WxToken wxToken) {
		WxToken token = wxTokenMapper.selectByKey(wxToken.getToken_key());
		if(token != null){
			token.setCreate_time(System.currentTimeMillis());
			//token已存在,则更新token
			wxToken.setId(token.getId());
			wxTokenMapper.update(wxToken);
			//如果更新的是noral_access_token,则需要删除失效的js ticket
			if(WX_TOKEN_KEY.NORMAL_ACCESS_TOKEN.getKey().equals(wxToken.getToken_key())){
				WxToken ticket = wxTokenMapper.selectByKey(WX_TOKEN_KEY.JSSDK_TICKET.getKey());
				if(ticket != null){
					wxTokenMapper.delete(ticket.getId());
				}
			}
		}else{
			token = new WxToken();
			token.setExpire_seconds(wxToken.getExpire_seconds());
			token.setToken_key(wxToken.getToken_key());
			token.setToken_value(wxToken.getToken_value());
			token.setCreate_time(System.currentTimeMillis());
			wxTokenMapper.save(wxToken);
		}
		return wxToken;
	}


public List<WxOpenid> getWxOpenidsWithoutUnionid(long page, int psize) {
		QueryPager pager = new QueryPager();
		pager.setPageNum(page);
		pager.setNumPerPage((long)psize);
		WxOpenid wxo = new WxOpenid();
		wxo.setPager(pager);
		return wxOpenidMapper.selectWithoutUnionid(wxo);
	}

public WxToken getWxTokenByKey(String key) {
		WxToken token = wxTokenMapper.selectByKey(key);
		boolean isSave = false;
		if(token == null || !token.isActive()){
			JSONObject tokenJo = WeixinUtil.getAccessToken();
			if(tokenJo != null && tokenJo.containsKey("access_token")){
				if(token == null){
					//保存新token
					token = new WxToken();
					isSave = true;
				}
				token.setExpire_seconds(tokenJo.getInteger("expires_in"));
				token.setToken_value(tokenJo.getString("access_token"));
				token.setToken_key(key);
				token.setCreate_time(System.currentTimeMillis());
				if(isSave){
					wxTokenMapper.save(token);
				}else{
					wxTokenMapper.update(token);
				}
			}
		}
		return token;
	}

微信订阅号事件、消息处理

@RequestMapping("/weixinConnector" + GlobalConstant.STATIC_SUFFIX)
	public void weixinConnector(HttpServletRequest request, HttpServletResponse response){
		if(request.getMethod().equalsIgnoreCase(RequestMethod.GET.toString())){
			//GET处理签名验证
			String signature = request.getParameter("signature");
			String nonce = request.getParameter("nonce");
			String timestamp = request.getParameter("timestamp");
			String echostr = request.getParameter("echostr");
			WxSignature sign = new WxSignature(signature, nonce, timestamp);
			if(WeixinUtil.verifySignature(sign)){
				printJson(echostr, response);
				return;
			}
		}else{
			//POST处理菜单创建、消息回复等
			WxNotify notify = WeixinUtil.parseNotify(request);
			String msgType = notify.getMsgType();
			String fromOpenid = notify.getFromUserName();
			
			//处理事件推送
			if("event".equals(msgType)){
				if("subscribe".equals(notify.getEvent())){
					//关注自动回复消息(暂无实现回复图片、视频、语音等)
					WxConfig config = wxConfigService.getWxConfigByKey(WX_CONFIG_KEY.SUBSCRIBE_REPLY_KEY.getKey());
					if(config != null && config.getValue() != null){
						String keyValue = config.getValue();
						if(WX_CONFIG_KEY.SUBSCRIBE_REPLY_TEXT.getKey().equals(keyValue)){
							//回复文本信息
							doReplyText(notify.getToUserName(), fromOpenid, WX_CONFIG_KEY.SUBSCRIBE_REPLY_TEXT, response);
						}
						else if(WX_CONFIG_KEY.SUBSCRIBE_REPLY_ARTICLE.getKey().equals(keyValue)){
							//回复图文信息
							doReplyArticles(notify.getToUserName(), fromOpenid, response);
						}
					}
				}
				if("subscribe".equals(notify.getEvent()) || "unsubscribe".equals(notify.getEvent())){
					//关注和取消关注事件,需要更新数据库Open id状态
					//获取用户信息(UionID)机制
					try {
						WxOpenid userInfo = getUserInfo(fromOpenid);
						//保存open id 和 union id信息
						wxOpenidService.addNewWxOpenidOrUpdate(userInfo);
					} catch (Exception e) {
						e.printStackTrace();
						//获取AccessToken失败,则按接口文档说明返回空字符串
						printJson("", response);
						return;
					}
					
				}
				if("TEMPLATESENDJOBFINISH".equals(notify.getEvent())) {
					//模版消息发送任务完成事件
					printJson("", response);
					return;
				}
			}else if("text".equals(msgType) || "image".equals(msgType) || "voice".equals(msgType) || "video".equals(msgType) || "shortvideo".equals(msgType) || "location".equals(msgType) || "link".equals(msgType)){
				fromOpenid = notify.getFromUserName();
				//处理消息回复(暂无实现回复图片、视频、语音等)
				WxConfig config = wxConfigService.getWxConfigByKey(WX_CONFIG_KEY.COMMON_REPLY_KEY.getKey());
				if(config != null && config.getValue() != null){
					String keyValue = config.getValue();
					if(WX_CONFIG_KEY.COMMON_REPLY_TEXT.getKey().equals(keyValue)){
						//回复文本信息
						doReplyText(notify.getToUserName(), fromOpenid, WX_CONFIG_KEY.COMMON_REPLY_TEXT, response);
					}
					else if(WX_CONFIG_KEY.COMMON_REPLY_ARTICLE.getKey().equals(keyValue)){
						//回复图文信息
						doReplyArticles(notify.getToUserName(), fromOpenid, response);
					}
				}
			}
		}
	}

其他实时服务号授权等代码就省略了。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dyyaries

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值