判断用户是否已关注公众号

2023-11-04

背景
业务场景是:判断当前登录用户是否已经关注指定的官方微信公众号,没有就指引用户关注。

微信公众号官方文档:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html

详细步骤

一.公众号后台配置

  1. 获取appid, appsecret,添加白名单
    登录微信公众平台,进入基本配置。开发中需要用到两个参数,appId和appSecret(appSecret只展示一次,需保存下来,否则需要重置获取)。获取access_token时需要添加IP白名单。
  2. 添加网页授权

二.后台实现思路

  1. 获取公众号的access_token
    https请求方式: GET
    https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
参数 说明
grant_type $ 获取access_token填写client_credential
appid 第三方用户唯一凭证
secret 第三方用户唯一凭证密钥,即appsecret

返回说明
正常情况下,微信会返回下述JSON数据包给公众号:
{“access_token”:“ACCESS_TOKEN”,“expires_in”:7200}

参数说明

参数 说明
access_token 获取到的凭证
expires_in 凭证有效时间,单位:秒

错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
{“errcode”:40013,“errmsg”:“invalid appid”}

返回错误码参考官方文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html

  1. 获取用户的openid
    在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(说明:OpenID就是加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的openid不同)。
    用户同意授权,获取code通过code来获取openid
    https请求方式: GET
    https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数 说明
appid 第三方用户唯一凭证
secret 第三方用户唯一凭证密钥,即appsecret
code code
grant_type authorization_code
  1. 根据前两个步骤获得的信息(access_token和openId),调用微信接口获取用户基本信息
    https请求方式: GET
    https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

返回说明
正常情况下,微信会返回下述JSON数据包给公众号:

  {
        "subscribe": 1, 
        "openid": "xxxxxx", 
        "nickname": "Band", 
        "sex": 1, 
        "language": "zh_CN", 
        "city": "深圳", 
        "province": "广东", 
        "country": "中国",   "headimgurl":"",
        "subscribe_time": 1382694957,
        "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
        "remark": "",
        "groupid": 0,
        "tagid_list":[128,2],
        "subscribe_scene": "ADD_SCENE_QR_CODE",
        "qr_scene": 98765,
        "qr_scene_str": ""
    }
    返回参数说明:
        参数            说明
        subscribe       用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。
        openid          用户的标识,对当前公众号唯一
        nickname        用户的昵称
        sex             用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
        city            用户所在城市
        country         用户所在国家
        province        用户所在省份
        language        用户的语言,简体中文为zh_CN
        headimgurl      用户头像,最后一个数值代表正方形头像大小(有0466496132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
        subscribe_time  用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
        unionid         只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
        remark          公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注
        groupid         用户所在的分组ID(兼容旧的用户分组接口)
        tagid_list      用户被打上的标签ID列表
        subscribe_scene 返回用户关注的渠道来源,ADD_SCENE_SEARCH 公众号搜索,ADD_SCENE_ACCOUNT_MIGRATION 公众号迁移,ADD_SCENE_PROFILE_CARD 名片分享,ADD_SCENE_QR_CODE 扫描二维码,ADD_SCENEPROFILE LINK 图文页内名称点击,ADD_SCENE_PROFILE_ITEM 图文页右上角菜单,ADD_SCENE_PAID 支付后关注,ADD_SCENE_OTHERS 其他
        qr_scene        二维码扫码场景(开发者自定义)
        qr_scene_str    二维码扫码场景描述(开发者自定义)
 
错误结果:
    {"errcode":40013,"errmsg":"invalid appid"}

详情查看官方文档-获取用户基本信息 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839

三.java后台实现

获取openid参考:

实体
@Data
public class WeiXinOauth2Token {
	private String accessToken;
	private int expiresIn;
	private String refeshToken;
	private String openId;
	private String scope;
}

String userListUrl="https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
/**
	 * 通过网页授权code获取微信openid
	 * @param appId
	 * @param appSecret
	 * @param code
	 * @return
	 */
	public static WeiXinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
        WeiXinOauth2Token wat = new WeiXinOauth2Token();
        String requestUrl = oauth2WebUrl.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);
        JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
        if (null != jsonObject) {
                try {
                        wat = new WeiXinOauth2Token();
                        wat.setAccessToken(jsonObject.getString("access_token"));
                        wat.setExpiresIn(jsonObject.getInteger("expires_in"));
                        wat.setRefeshToken(jsonObject.getString("refresh_token"));
                        wat.setOpenId(jsonObject.getString("openid"));
                        wat.setScope(jsonObject.getString("scope"));
                } catch (Exception e) {
                        wat = null;
                        String errorCode = jsonObject.getString("errcode");
                        String errorMsg = jsonObject.getString("errmsg");
                        log.error("获取网页授权凭证失败 errcode:"+errorCode+",errMsg:"+errorMsg);
                }

        }
        return wat;
	}

工具类
/**
     * 发送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);
                    //conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); 
                    // 当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.parseObject(buffer.toString());
            } catch (ConnectException ce) {
                    log.error("连接超时:{}", ce);
            } catch (Exception e) {
                    log.error("https请求异常:{}", e);
            }
            return jsonObject;
	}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

判断用户是否已关注公众号 的相关文章

  • JDK 文档是语言规范的一部分吗?

    只有一名官员Java语言规范 https docs oracle com javase specs jls se8 html index html所有 Java 实现都必须遵守它 API文档怎么样 所有Java实现都需要遵守吗这个版本 ht
  • Java Runtime.getRuntime().freeMemory() 问题

    我搜索并看到了一些线程 但没有一个能够解决我遇到的具体问题 我正在尝试使用以下方式监视我的内存使用情况Runtime getRuntime freeMemory Runtime getRuntime maxMemory and Runtim
  • Java:无法从同一包中的不同类访问静态变量

    这很奇怪 因为我有一个可以访问 Frame dimension getWidth 的 Character 类 及其伙伴 getHeight 但是当我想在 Map 类中使用它时 Eclipse 强调了它并且无法给我反馈 运行该程序最终会出现
  • 通过SOCKS代理连接Kafka

    我有一个在 AWS 上运行的 Kafka 集群 我想用标准连接到集群卡夫卡控制台消费者从我的应用程序服务器 应用程序服务器可以通过 SOCKS 代理访问互联网 无需身份验证 如何告诉 Kafka 客户端通过代理进行连接 我尝试了很多事情 包
  • 如何在 Firebase 远程配置中从 JSON 获取值

    我是 Android 应用开发和 Firebase 的新手 我想知道如何获取存储在 Firebase 远程配置中的 JSONArray 文件中的值 String 和 Int 我使用 Firebase Remote Config 的最终目标是
  • Spring Data JPA 选择不同

    我有一个情况 我需要建立一个select distinct a address from Person a 其中地址是 Person 内的地址实体 类型的查询 我正在使用规范动态构建我的 where 子句并使用findAll Specifi
  • 通往楼梯顶部的可能路径

    这是一个非常经典的问题 我听说谷歌在他们的面试中使用过这个问题 问题 制定一个递归方法 打印从楼梯底部到楼梯顶部的所有可能的独特路径 有 n 个楼梯 您一次只能走 1 步或 2 步 示例输出 如果它是一个有 3 级楼梯的楼梯 1 1 1 2
  • Kotlin 未解决的参考:CLI 上 gradle 的 println

    放一个printlnkotlin 函数返回之前的语句会崩溃 堆栈跟踪 thufir dur NetBeansProjects kotlin thufir dur NetBeansProjects kotlin gradle clean bu
  • 来自十六进制代码的 Apache POI XSSFColor

    我想将单元格的前景色设置为十六进制代码中的给定颜色 例如 当我尝试将其设置为红色时 style setFillForegroundColor new XSSFColor Color decode FF0000 getIndexed 无论我在
  • 什么时候可以在 Java 中使用 Thead.stop() ?

    Thread stop 的 Java 文档听起来好像如果您调用 Thread stop 世界就会终结 已弃用 这种方法本质上是不安全的 停止线程 Thread stop 导致它解锁所有已锁定的监视器 作为未经检查的 ThreadDeath
  • 是否可以通过编程方式查找 logback 日志文件?

    自动附加日志文件以支持电子邮件会很有用 我可以以编程方式设置路径 如以编程方式设置 Logback Appender 路径 https stackoverflow com questions 3803184 setting logback
  • 从 GitHub 上托管的 Spring Cloud Config Server 访问存储库的身份验证问题

    我在 GitHub 上的存储库中托管配置 如果我将回购公开 一切都好 但如果我将其设为私有 我将面临 org eclipse jgit errors TransportException https github com my user m
  • Java:如何为山区时间创建 TimeZone 对象?

    必须不禁用夏令时 嗯 在这个清单 http en wikipedia org wiki List of tz database time zones在 zoneinfo 时区名称中 有很多声称是 山地时间 找到最适合您想要的那个 然后使用它
  • 了解joda时间PeriodFormatter

    我以为我明白了 但显然我不明白 你能帮我通过这些单元测试吗 Test public void second assertEquals 00 00 01 OurDateTimeFormatter format 1000 Test public
  • 使用 Mockito 模拟某些方法,但不模拟其他方法

    有没有办法使用 Mockito 模拟类中的某些方法 而不模拟其他方法 例如 在这个 诚然是人为的 Stock我想嘲笑的班级getPrice and getQuantity 返回值 如下面的测试片段所示 但我想要getValue 执行乘法 如
  • Freemarker 和 Struts 2,有时它计算为序列+扩展哈希

    首先我要说的是 使用 Struts2 Freemarker 真是太棒了 然而有些事情让我发疯 因为我不明白为什么会发生这种情况 我在这里问是因为也许其他人有一个想法可以分享 我有一个动作 有一个属性 说 private String myT
  • 使用架构注册表对 avro 消息进行 Spring 云合约测试

    我正在查看 spring 文档和 spring github 我可以看到一些非常基本的内容examples https github com spring cloud samples spring cloud contract sample
  • 使用布尔值进行冒泡排序以确定数组是否已排序

    我有以下用于冒泡排序的代码 但它根本不排序 如果我删除布尔值那么它工作正常 我知道 由于我的 a 0 小于所有其他元素 因此没有执行交换 任何人都可以帮助我解决这个问题 package com sample public class Bub
  • Java Swing:需要一个高质量的带有复选框的开发 JTree

    我一直在寻找一个 Tree 实现 其中包含复选框 其中 当您选择一个节点时 树中的所有后继节点都会被自动选择 当您取消选择一个节点时 树中其所有后继节点都会自动取消选择 当已经选择了父节点 并且从其后继之一中删除了选择时 节点颜色将发生变化
  • Spring表单ModelAttribute字段验证避免400 Bad Request错误

    我有一个ArticleFormModel包含正常发送的数据html form由 Spring 使用注入 ModelAttribute注释 即 RequestMapping value edit method RequestMethod PO

随机推荐

  • 2024届IC秋招兆易创新数字IC后端笔试面试题

    数字IC后端实现PR阶段设计导入需要哪些文件 设计导入需要的文件如下图所示 这个必须熟练掌握 只要做过后端训练营项目的 对这个肯定是比较熟悉的 大家还要知道每个input文件的作用是什么 在吾爱IC后端训练营Cortexa7core项目中
  • python读音-原来Python应该这么念,怪不得总被嘲笑~

    原标题 原来Python应该这么念 怪不得总被嘲笑 Python具有简单 易学 免费 开源 可扩展 可嵌入 面向对象等优点 它的面向对象甚至比java和c net更彻底 可以与C 完美融合 两者堪称 万金油 相信很多人都不知道Python这
  • 常见(MySQL)面试题(含答案)

    常见 MySQL 面试题 含答案 本文的面试题如下 MyisAM和innodb的有关索引的疑问 innodb为什么要用自增id作为主键 MySql索引是如何实现的 说说分库与分表设计 面试过 聚集索引与非聚集索引的区别 事务四大特性 ACI
  • DataSource数据源

    本文详细的介绍了 JAVA中的数据源 认真看下来 你一定会收获满满 文章目录 一 DataSource接口 二 SpringBoot中的DataSource相关 1 默认提供一个数据源 2 自定义数据源 与 默认数据源 优先级 3 myba
  • win7中vscode配置jupyter

    一 内容简介 win7下用vscode写python程序 二 主要流程 3 1 安装环境 3 2 使用vscode运行调试python代码 3 3 vscode调用jupyter运行调试python代码 三 具体步骤 4 1 安装环境 vs
  • jsp Request原理

    jsp Request原理 1 Tomcat 服务器会根据请求url中的资源路径 创建对应的ServletDemo1的对象 2 tomcat服务器 会创建request和response对象 request对象封装请求消息数据 3 tomc
  • fijkplayer的使用

    1 配置 pubspec yaml 加入 fijkplayer 0 10 1 2 直接使用 import dart math import package flutter material dart import package fijkp
  • python面试题总结_2019(有解释答案) --3

    目录 50 Python 交换两个变量的值 51 在读文件操作的时候会使用 read readline 或者 readlines 简述它们各自的作用 52 json序列化时 可以处理的数据类型有哪些 如何定制支持 datetime 类型 5
  • 前端如何使用微信支付

    一 h5页面支付 h5页面接入微信支付分为两种情况 一种微信内置浏览器调用支付 另一种是在外置浏览器调用支付 内置浏览器支付 内置浏览器支付使用JSAPI支付 前端调用代码 wx chooseWXPay timestamp 0 支付签名时间
  • MySQL 服务器进程 mysqld的组成

    有连接层 SQL 层 存储层组成 连接层 为每个连接维护一个线程 进行身份验证 处理查询执行 SQL 层 包括了解析 授权分析 优化 执行 查询日志记录 存储层 管理表数据 常见的存储引擎有 InnoDB MyISAM MEMORY 和 N
  • python爬虫接口_爬虫与API(上)

    本系列两篇文章讲API的概念 以及它在爬虫中的使用 分为如下部分 API概念 库的API 数据API Github API httpbin 其他API 由于篇幅限制 本文只展示API概念 库的API 数据API API概念 API Appl
  • 程序员必知的23种设计模式之策略模式

    文章目录 1 模式引入 鸭子项目 2 传统方案解决鸭子问题 3 传统方式解决鸭子问题分析和解决方案 4 策略模式基本介绍 4 1 策略模式的原理类图 4 2 说明 5 策略模式解决鸭子问题 6 策略模式的注意事项和细节 1 模式引入 鸭子项
  • 比较两个版本字符串version1和version2

    时间限制 1秒 空间限制 32768K 如果version1 gt version2 返回1 如果 version1 lt version2 返回 1 不然返回0 输入的version字符串非空 只包含数字和字符 字符不代表通常意义上的小数
  • 23.7.14——23.7.16周末作业

    思维导图 2 有1 2 3 4个数字 能组成多少互不相同且无重复的三位 都是多少 3 终端输入一个数 输出他的二进制 二进制输出不可用格式符 4 定义一个一维整型数组 使用for循环实现数组中元素的输入输出 5 终端输入两个数 求这两个数的
  • windows下PL/SQL Developer连接Orcale

    2 使用PL SQL Developer连接Orcale 这是第三方开发的工具 用于编写PL SQL 代码 调试 查看执行计划等等功能 比较方便 但是是付费软件 Oracle 没有收购这家公司 大家只能用低版本的绿色版 性能有很大的问题 而
  • JavaScript数据结构之队列

    class Queue constructor this count 0 this list this lowestCount 0 往队列添加元素 enqueue element this list this count element t
  • 补充:tensor之间进行矩阵相乘的方法总结

    利用 进行简单的矩阵乘 符号在tensor中就表示矩阵相乘 符号的矩阵相乘性质在numpy中依然适用 首先矩阵相乘的双方必须满足可以矩阵相乘的条件 只会关注两个矩阵最里面的两个维度是否符合条件 外面的维度都只表示矩阵运算的次数 甚至两个矩阵
  • PAT (Basic Level) Practice 1018 锤子剪刀布

    大家应该都会玩 锤子剪刀布 的游戏 两人同时给出手势 胜负规则如图所示 现给出两人的交锋记录 请统计双方的胜 平 负次数 并且给出双方分别出什么手势的胜算最大 输入格式 输入第 1 行给出正整数 N 10 5 即双方交锋的次数 随后 N 行
  • 解决appium-inspector连接后在Appium中报错:No route found for /sessions

    完整报错 Appium inspector 连接点击start session后在Appium中报错 HTTP lt GET sessions 404 6 ms 211 HTTP HTTP gt GET sessions HTTP HTTP
  • 判断用户是否已关注公众号

    背景 业务场景是 判断当前登录用户是否已经关注指定的官方微信公众号 没有就指引用户关注 微信公众号官方文档 https developers weixin qq com doc offiaccount Getting Started Ove