微信访问网页时判断微信用户是否关注公众号

目录

  • 一、开放平台与公众平台
  • 二、 微信公众平台配置
    • 1. 获取appid, appsecret,添加白名单
      • 正式帐号
      • 测试帐号
    • 2. 添加网页
      • 正式帐号
      • 测试帐号
  • 三、java后端实现
    • 源码
    • 注意事项

一、开放平台与公众平台

微信公众平台:
https://mp.weixin.qq.com/cgi-bin/loginpage
微信开放平台:
https://open.weixin.qq.com/
微信官方文档:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
两者的区别:
开放平台是网站或app使用的接口平台,利用开放平台可在自己的网站或app上开发微信帐户登录、微信分享等功能!
公众平台是微信号的一种,也具有开发功能,是在公众号中开发出更多功能,例如微网站等!
简单来讲,公众平台的开发功能是在微信平台的基础上的,而开放平台是在你自己的平台上开发的与微信相关的一些功能。

备注:本文开发内容是微信公众平台的内容,涉及官方文档如下:
获取全局token
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
网页授权
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
获取用户基本信息(UnionID机制)
https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId

二、 微信公众平台配置

个人学习时可申请测试帐号:
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Requesting_an_API_Test_Account.html
注意:公众平台接口调用仅支持80端口,且项目支持外网访问,个人学习时可在本地配置内网穿透,参考https://blog.csdn.net/qq_37718403/article/details/106093162

1. 获取appid, appsecret,添加白名单

正式帐号

前提:正式帐号接入微信公众平台的配置参考
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
官方描述:
微信访问网页时判断微信用户是否关注公众号_第1张图片

AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得(需要已经成为开发者,且帐号没有异常状态)(appSecret只展示一次,需保存下来,否则需要重置获取)。
获取access_token时需要添加IP白名单。
微信访问网页时判断微信用户是否关注公众号_第2张图片
点击查看
微信访问网页时判断微信用户是否关注公众号_第3张图片

点击修改微信访问网页时判断微信用户是否关注公众号_第4张图片

测试帐号

登录测试帐号
微信访问网页时判断微信用户是否关注公众号_第5张图片

2. 添加网页

正式帐号

官方描述:
微信访问网页时判断微信用户是否关注公众号_第6张图片
进入公众号设置=》功能设置=》网页授权域名
微信访问网页时判断微信用户是否关注公众号_第7张图片

点击设置,input框中输入授权回调页的域名参考第1点(只能填写一个),下载第3点中的txt文档,上传至服务器的根目录。
微信访问网页时判断微信用户是否关注公众号_第8张图片

测试帐号

点击【网页授权获取用户基本信息】的修改,将域名添加进去
微信访问网页时判断微信用户是否关注公众号_第9张图片
微信访问网页时判断微信用户是否关注公众号_第10张图片

三、java后端实现

微信开放接口全局返回码说明参考:https://mp.weixin.qq.com/wiki…

源码

WxController

package com.controller;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.ParseException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.util.WeiXinUtil;

import net.sf.json.JSONObject;

@Controller
@RequestMapping("/wx")
public class WxController {
	public static final String APPID="";
    public static final String APPSECRET ="";
    
    private static final Logger logger = Logger.getLogger(WxController.class);
	
	@RequestMapping(value = "/wxLogin", method = RequestMethod.GET)
	public String wxLogin(HttpServletRequest request, HttpServletResponse response)throws ParseException, UnsupportedEncodingException {
		//这个地址是成功后的回调地址,域名必须和公众号中配置的域名一致
		String backUrl="http://xiayehuimou.free.idcfengye.com/ssm_demo/wx/callBack";
		// 第一步:用户同意授权,获取code
		String url ="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+ APPID
					+ "&redirect_uri="
					+URLEncoder.encode(backUrl, "utf-8")
					+ "&response_type=code"
					+ "&scope=snsapi_userinfo"
					+ "&state=STATE#wechat_redirect";
		logger.info("forward重定向地址{" + url + "}");
		return "redirect:" + url ;
	}
	
	@RequestMapping(value = "/callBack", method = RequestMethod.GET)
	public String callBack(ModelMap modelMap,HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String code =req.getParameter("code");
		//第二步:通过code换取网页授权access_token
		String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+APPID
				+ "&secret="+APPSECRET
				+ "&code="+code
				+ "&grant_type=authorization_code";
		System.out.println("url:"+url);
		JSONObject jsonObject = WeiXinUtil.httpsRequest(url, "GET", null);
		String openid = jsonObject.getString("openid");
		String access_token = jsonObject.getString("access_token");
		String refresh_token = jsonObject.getString("refresh_token");
		//第五步验证access_token是否失效;展示都不需要
		String chickUrl="https://api.weixin.qq.com/sns/auth?access_token="+access_token+"&openid="+openid;
		JSONObject chickuserInfo =WeiXinUtil.httpsRequest(chickUrl, "GET", null);
		System.out.println(chickuserInfo.toString());
		if(!"0".equals(chickuserInfo.getString("errcode"))){
			// 第三步:刷新access_token(如果需要)-----暂时没有使用,参考文档https://mp.weixin.qq.com/wiki,
			String refreshTokenUrl="https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+openid+"&grant_type=refresh_token&refresh_token="+refresh_token;
			JSONObject refreshInfo =  WeiXinUtil.httpsRequest(refreshTokenUrl, "GET", null);
			System.out.println(refreshInfo.toString());
			access_token=refreshInfo.getString("access_token");
		}
		// 第四步:拉取用户信息(需scope为 snsapi_userinfo)
		// 这个url的token是网页授权token(获取的基本信息内容相对较少,没有是否关注公众号字段)
		//String infoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token="+access_token
		//		+ "&openid="+openid
		//		+ "&lang=zh_CN";
		// 此处的token是全局token
		String infoUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+WeiXinUtil.getToken(APPID,APPSECRET).getAccessToken()
				+ "&openid="+openid
				+ "&lang=zh_CN";
		System.out.println("infoUrl:"+infoUrl);
		JSONObject userInfo = WeiXinUtil.httpsRequest(infoUrl, "GET", null);
		System.out.println("JSON-----"+userInfo.toString());
		System.out.println("名字-----"+userInfo.getString("nickname"));
		System.out.println("头像-----"+userInfo.getString("headimgurl"));
		//获取到用户信息后就可以进行重定向,走自己的业务逻辑了。。。。。。
		//接来的逻辑就是你系统逻辑了,请自由发挥
		// 我此处是打开file.html页面
		return "file";
	}

}

WeiXinUtil

package com.util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.entity.Token;

/**
 * 类名: CommonUtil 
* 描述: 通用工具类
* 开发人员: howin
* 创建时间: 2016-08-19
* 发布版本:V1.0
*/
public class WeiXinUtil { private static Logger log = LoggerFactory.getLogger(WeiXinUtil.class); private static long tokenTime = 0; private static long jsTicketTime = 0; private static Token token = null; private static String ticket = null; // (全局token)凭证获取(GET)限2000(次/天) public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; /** * 发送https请求 * * @param requestUrl * 请求地址 * @param requestMethod * 请求方式(GET、POST) * @param outputStr * 提交的数据 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */ public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; 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 conn = (HttpsURLConnection) url.openConnection(); conn.setSSLSocketFactory(ssf); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 设置请求方式(GET/POST) conn.setRequestMethod(requestMethod); // 当outputStr不为null时向输出流写数据 if (null != outputStr) { OutputStream outputStream = conn.getOutputStream(); // 注意编码格式 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 从输入流读取返回内容 InputStream inputStream = conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; StringBuffer buffer = new StringBuffer(); while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } // 释放资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); inputStream = null; conn.disconnect(); jsonObject = JSONObject.fromObject(buffer.toString()); } catch (ConnectException ce) { log.error("连接超时:{}", ce); } catch (Exception e) { log.error("https请求异常:{}", e); } return jsonObject; } /** * 获取接口访问凭证 * * @param appid * 凭证 * @param appsecret * 密钥 * @return */ public static Token getToken(String appid, String appsecret) { long now = new Date().getTime(); if (tokenTime != 0 && now - tokenTime < 7000000) {// token有效时间 7e6 毫秒 return token; } String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", appsecret); // 发起GET请求获取凭证 JSONObject jsonObject = httpsRequest(requestUrl, "GET", null); if (null != jsonObject) { try { token = new Token(); token.setAccessToken(jsonObject.getString("access_token")); token.setExpiresIn(jsonObject.getInt("expires_in")); tokenTime = now; } catch (JSONException e) { token = null; // 获取token失败 log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return token; } /** * 获取jsapi_ticket访问凭证 */ public static String getJsTicket() { long now = new Date().getTime(); if (jsTicketTime != 0 && now - jsTicketTime < 7000000) {// token有效时间 7e6 毫秒 return ticket; } // 得到token String accessToken = getToken(null, null).getAccessToken(); // GET方法获得jsapi_ticket String requestUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi" .replace("ACCESS_TOKEN", accessToken); // 发起GET请求获取凭证 JSONObject jsonObject = httpsRequest(requestUrl, "GET", null); if (jsonObject != null) { ticket = jsonObject.getString("ticket"); jsTicketTime = now; } return ticket; } /** * URL编码(utf-8) * * @param source * @return */ public static String urlEncodeUTF8(String source) { String result = source; try { result = java.net.URLEncoder.encode(source, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return result; } /** * 根据内容类型判断文件扩展名 * * @param contentType * 内容类型 * @return */ public static String getFileExt(String contentType) { String fileExt = ""; if ("image/jpeg".equals(contentType)) fileExt = ".jpg"; else if ("audio/mpeg".equals(contentType)) fileExt = ".mp3"; else if ("audio/amr".equals(contentType)) fileExt = ".amr"; else if ("video/mp4".equals(contentType)) fileExt = ".mp4"; else if ("video/mpeg4".equals(contentType)) fileExt = ".mp4"; return fileExt; } // 菜单创建(POST) 限100(次/天) public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"; /** * 创建菜单 * * @param menu * 菜单实例 * @param accessToken * 有效的access_token * @return 0表示成功,其他值表示失败 */ /* * public static int createMenu(Menu menu, String accessToken) { int result = 0; * * // 拼装创建菜单的url String url = menu_create_url.replace("ACCESS_TOKEN", * accessToken); // 将菜单对象转换成json字符串 String jsonMenu = * JSONObject.fromObject(menu).toString(); // 调用接口创建菜单 JSONObject jsonObject = * httpsRequest(url, "POST", jsonMenu); System.out.println(jsonMenu); if (null * != jsonObject) { if (0 != jsonObject.getInt("errcode")) { result = * jsonObject.getInt("errcode"); log.error("创建菜单失败 errcode:{} errmsg:{}", * jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } * * return result; } */ /** * 对Map数组进行排序 */ public static String FormatQueryParaMap(HashMap<String, String> parameters) throws UnsupportedEncodingException { String buff = ""; List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(parameters.entrySet()); Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() { public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) { return (o1.getKey()).toString().compareTo(o2.getKey()); } }); for (int i = 0; i < infoIds.size(); i++) { Map.Entry<String, String> item = infoIds.get(i); if (item.getKey() != "") { // buff += item.getKey() + "="+ URLEncoder.encode(item.getValue(), "utf-8") + // "&"; buff += item.getKey() + "=" + item.getValue() + "&"; } } if (buff.isEmpty() == false) { buff = buff.substring(0, buff.length() - 1); } return buff; } /** * 生成32位随机字符串 */ public static String CreateNoncestr() { String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String res = ""; for (int i = 0; i < 16; i++) { Random rd = new Random(); res += chars.charAt(rd.nextInt(chars.length() - 1)); } return res; } /** * SHA1加密 */ public final static String Sha1(String s) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; try { byte[] btInput = s.getBytes(); // 获得MD5摘要算法的 MessageDigest 对象 MessageDigest mdInst = MessageDigest.getInstance("sha-1"); // 使用指定的字节更新摘要 mdInst.update(btInput); // 获得密文 byte[] md = mdInst.digest(); // 把密文转换成十六进制的字符串形式 int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } } }

MyX509TrustManager

package com.util;
 
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* 类名: MyX509TrustManager 
* 描述:信任管理器
* 开发人员: howin
* 创建时间: 2016-08-19
* 发布版本:V1.0
*/
/* * 证书管理器的作用是让它新人我们指定的证书, * 此类中的代码意味着信任所有的证书,不管是不是权威机构颁发的。 */ public class MyX509TrustManager implements X509TrustManager { // 检查客户端证书 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 检查服务器端证书 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 返回受信任的X509证书数组 public X509Certificate[] getAcceptedIssuers() { return null; } }

注意事项

如果使用测试号,用户必须要先关注测试号,否则获取code时不会执行回调方法
微信公众号测试账号获取授权须关注

你可能感兴趣的:(微信开发)