微信公众号对接获取用户openid预约项目心路全历程

news2024/12/25 2:39:42

公众号对接获取openid全历程

    • 一、背景
    • 二、选型
    • 三、开始修改若依框架
    • 四、自己搭后端框架
    • 五、前端框架uni-app修改
    • 六、对接获取公众号登录用户openId
    • 七、总结

一、背景

老板接了朋友的一个公众号需求,要求做一个简单的疫苗预约系统。功能是获取当前登录用户,记录用户选择的疫苗,针次,时间,并且用户可以查看自己预约的记录。我刚接到这个需求感觉也简单,给的时间呢也只有一周,包括程序的开发,服务的部署,公众号相关信息的配置。虽然是个很小的功能,没想到期间碰到过很多坑,公众号也是我第一次全流程的做,记录一下这期间的坎坷。

二、选型

一般接到这种小功能的开发,我们一般会在开源框架市场找几个相似的项目然后修修改改,达到甲方想要的使用预期。前三天基本都是浪费在选型这一块了,本来也想着,选好开源项目,改起来也快,最后敲定选用若依uni-app这款,可没想到,最后改了半天,最后忍无可忍,自己手写了一套后端,前端代码,框架当然用的还是springboot+uni-app。。。

三、开始修改若依框架

若依这套框架也是典型的租户型框架,uniapp启动后是一个登录界面,而公众号要做默认登录,是不要这个登录界面的,那怎么跳过这个登录界面,直接登录进去呢,一开始想的是默认获取用户openid直接授权进去,这就要对应的修改后端生成token的方式,然后每一个页面访问的header还要携带这个token。

除此之外,登录进去后才展示的业务界面还需要画一下,那就先把页面画出来,一共做了四个页面,用户首页(展示预约疫苗入口和用户预约记录),用户信息维护(点击预约疫苗后先维护自己的信息,包括姓名,身份证号,手机号),预约界面(三个相互隐藏的栏位,疫苗栏位,针次栏位,时间栏位),疫苗说明界面。这里说一下微信公众号静默获取的信息只能获取到openId,unionid应该也可以获取,我没试过。获取openId的过程后面会说明。这里让用户自己维护手机号和身份证号是因为可能会给长辈预约疫苗。

页面画完之后,数据都是用的前端代码模拟的值,流程无碍之后,准备后端添加接口,业务表是甲方直接提供,使用的是sqlServer数据库,若依用的数据库版本是mysql,最后又找了一下,找到了若依sqlserver版本的后端框架,发现没法改,主要还是认证这一个部分,如果要改造认证这一部分,又要花费很多工作时间,改造过程中可能还会产生新的问题或者bug都是没法预估的,不是说若依不好用,只能说没必要用这种shiro认证框架,太冗余了。

四、自己搭后端框架

不需要认证,获取到当前登录用户的openID进行数据隔离和查询也可以保证数据的安全性。

  • 新建一个springboot项目

  • 删除多余的文件夹

  • 修改pom.xml的一些配置和添加我所需要的maven依赖,sqlSerer驱动,mybatisplus,springboot启动的依赖自带的,fastjson,druid数据池,多数据源,lombok,当然hutool也要

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <!-- 阿里JSON解析器 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.75</version>
            </dependency>
            <!-- druid -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.2.4</version>
            </dependency>
            <!-- 动态数据源 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
                <version>3.5.2</version>
            </dependency>
            <!-- SqlServer 数据库连接包 -->
            <dependency>
                <groupId>com.microsoft.sqlserver</groupId>
                <artifactId>sqljdbc4</artifactId>
                <version>4.0</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.1</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-core</artifactId>
                <version>3.4.1</version>
            </dependency>
            <!-- Lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.18</version>
            </dependency>
        </dependencies>
    
  • 感觉又回到了刚学spring框架的那个时候了,做项目基本用开源框架或者公司自己的产品框架开发,很少自己搭spring项目了。

  • 然后修改pom.xml的打包配置选项

        <build>
            <finalName>yuyue</finalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <includeSystemScope>true</includeSystemScope>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>copy</id>
                            <phase>package</phase>
                            <goals>
                                <goal>copy-dependencies</goal>
                            </goals>
                            <configuration>
                                <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <!--指定JDK编译版本 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>2.3.2</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>3.1.2</version>
                    <configuration>
                        <skipTests>true</skipTests>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <configuration>
                        <nonFilteredFileExtensions>
                            <nonFilteredFileExtension>woff</nonFilteredFileExtension>
                            <nonFilteredFileExtension>woff2</nonFilteredFileExtension>
                            <nonFilteredFileExtension>eot</nonFilteredFileExtension>
                            <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                            <nonFilteredFileExtension>svg</nonFilteredFileExtension>
                        </nonFilteredFileExtensions>
                    </configuration>
                    <version>3.1.0</version>
                </plugin>
            </plugins>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <filtering>true</filtering>
                </resource>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                        <include>**/*.json</include>
                        <include>**/*.ftl</include>
                    </includes>
                </resource>
            </resources>
        </build>
    
  • 使用yaml配置文件,配置内容如下

    server:
      port: 8080
      servlet:
        context-path: /
      compression:
        enabled: true
        min-response-size: 1024
        mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*
    
    spring:
      datasource:
        druid:
          stat-view-servlet:
            enabled: true
            loginUsername: admin
            loginPassword: 123456
            allow:
          web-stat-filter:
            enabled: true
        dynamic:
          datasource:
            master:
              # 本地
              url: jdbc:sqlserver://localhost:1433;DatabaseName=Lanshan
              username: sa
              password: root
    
    # mybatis plus 设置
    mybatis-plus:
      mapper-locations: classpath*:com/yuyue/**/xml/*Mapper.xml
      #  configuration:
      #关闭二级缓存,默认开启
      #    cache-enabled: false
      #    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      global-config:
        # 关闭MP3.0自带的banner
        banner: false
        db-config:
          # 主键类型  0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)";
          id-type: 4
          # 默认数据库表下划线命名
          table-underline: true
          # 配置逻辑删除
          logic-delete-field: deleted
          logic-delete-value: 1
          logic-not-delete-value: 0
      #  这个配置会将执行的sql打印出来,在开发或测试的时候可以用
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      configuration-properties:
        # 配置流程引擎参数,详情可见 DatabaseConfiguration
        blobType: BLOB
        boolValue: TRUE
        # 不要设置库名,否则会出现双库名 bug
        prefix: ''
    # Mybatis输出sql日志
    logging:
      level:
        com.yuyue: debug
    
  • 然后就写相关的实体类,mapper,service,controller。

  • 写完到这里就基本没问题了,可以正常启动。

五、前端框架uni-app修改

前端框架虽然没有从0开始做,还是用的若依的uniapp,但是做了其他额外的工作量,打开首页的页面修改成我的首页,我所新加的页面都要在permission.js中排除掉,要不然会重定向到首页。去掉若依下面的固定栏位。剩下的就是添加接口js文件,然后在页面中将数据的获取方式从模拟值修改为访问后端接口返回。

这些都还是比较简单,过程有点繁琐而已,当前面都改完之后,这个项目最大的难点来了,就是openid的获取。根据微信官方文档所写,获取openid分为两部分,根据appid获取到微信返回回来的code,然后根据code和appid和app密钥获取该用户的openId。需要公众号提供的参数如下:

  • appId
  • app密钥secret

以下介绍对接获取微信公众号登录用户openId的过程

六、对接获取公众号登录用户openId

前提:以上程序开发完成并且配置到服务里面,服务证书访问并申请开启域名访问。

首先需要公众号进行如下的一些配置和操作

  • 配置网页授权域名
    在这里插入图片描述 配置网页授权域名的时候,保存的时候微信会校验一个文件,如下图
    在这里插入图片描述

    下载上图提到的文件,开启域名后需要同步开启80端口访问,然后将文件上传到服务器80端口的根目录,如果不知道根目录位置,可以找一下nginx或者网站的配置项。域名填写的内容如下示例:www.xxx.com,不用加前缀,不用加端口号。然后点击保存,如果提示域名活路径格式不正确说明你上传的文件放错位置了,文件放上去之后,确保访问www.xxx.com/上传文件名可以访问到。

  • 公众号添加IP访问白名单
    在这里插入图片描述

    将服务器的外网ip填写进去即可。

  • 提供公众号的appid和secret

  • 绑定微信公众号的开发者微信号

    作为开发人员,如果不绑定这个东西就属于是隧洞摸黑前行,永远也想不到有多少个坑在等你。
    在这里插入图片描述

    需要开发人员先关注该公众号,然后将你的微信号提供出来,在公众号中进行开发者绑定。

以上,公众号的配置暂告一段落。

接下来,程序开始修改获取openId。

程序修改后,如何验证正确性以及中间过程访问的参数是否正确,这个时候我们需要用到微信开发者工具

在这里插入图片描述

打开这个软件,然后用绑定公众号开发者的微信号登录,访问服务器部署的url,就能看到调用的接口了(做这种对接就不要想着本地调试了)。唯一麻烦的只有每次修改完代码后,需要将代码打包放在服务器然后解压。

  • 根据appId获取微信返回回来的CODE

    访问url:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

    需要修改的参数是:APPID,REDIRECT_URI

    APPID就是以上公众号提供出来的appId,REDIRECT_URI是重定向结果的url,这个重定向是微信官方那边做的,我们不用管如何重定向,需要注意的是重定向的url需要使用javaScript函数encodeURIComponent()进行编码

    示例:

    redirectToAuth() {
        const redirectUri = encodeURIComponent('https://www.xxx.cn')
        const authUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='+this.appid+'&redirect_uri='+redirectUri+'&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect'
        window.location.href = authUrl;
    			}
    
    // 访问后页面会刷新重定向为
    // https://www.xxx.cn?CODE=xxxxxxxxx
    
  • 处理微信返回来的CODE

    接下来要根据CODE和appid和secret获取到用户的openid,记住,这个过程一定要在后端完成,不可在前端代码完成,这个地方有坑,差点被坑哭了(>_<)。使用前端返回openid,接口会提示跨域异常,没有返回数据。

    首先获取url中的CODE参数

    const code = (new URLSearchParams(window.location.search)).get('code')
    

    接下来发起后端调用,我这里使用hutool的http工具访问,如下示例

    访问url:https://api.weixin.qq.com/sns/oauth2/access_token

    String resultOpen = HttpUtil.get("https://api.weixin.qq.com/sns/oauth2/access_token?" +
                    "appid=" + appid +//公众号ID
                    "&secret=" + secret//公众号密钥
                    "&code=" + code + //前端 传入 code
                    "&grant_type=authorization_code");
    JSONObject jsonObject = JSONObject.parseObject(resultOpen);
    if (jsonObject.containsKey("errcode")) {
        String s = codeMap.get(code);
        if(StrUtil.isBlank(s)) {
            return Result.error(resultOpen);
        }else{
            return Result.ok(s);
        }
    }
    openId = jsonObject.get("openid").toString();
    

    正常返回的参数如下

    {
      "access_token":"ACCESS_TOKEN",
      "expires_in":7200,
      "refresh_token":"REFRESH_TOKEN",
      "openid":"OPENID",
      "scope":"SCOPE",
      "is_snapshotuser": 1,
      "unionid": "UNIONID"
    }
    

    错误返回的参数如下

    {"errcode":40029,"errmsg":"invalid code"}
    
  • 当我们正确拿到这个openid就可以为所欲为了(不是),就可以正常进行后续的业务操作了。

  • 最后让客户将我们项目的访问链接配置在公众号的菜单就可以啦

七、总结

真正开发的时间其实是从周五下午开始,期间我还在忙另一个正在上线的项目。这个功能虽然要求不多,但是涉及到对接,期间的坑属实不少,看着网上一篇有一篇的攻略,却总是抓不住要点,就openid要后端才能获取这个,很多网上教程说直接用前端代码获取,可能之前可以,但是现在不行了。客户下周一验收此功能,可以算得上3天搞定一个公众号对接案例,涉及到对接微信的东西不多,只有一个获取用户openid,这一个小东西就能碰到很多坑。

经过这次项目,可以看得出制定方案选型有多么重要,真正干活其实方向对了很快就出活了,如果方向不对,比如这次公众号开发就是要改造若依框架,可能2周都出不来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1250901.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VRRP的交换机VRRP主备配置例子

拓朴如下&#xff1a; 主要配置如下&#xff1a; [S1] vlan batch 10 20 # interface Vlanif10ip address 10.1.1.1 255.255.255.0vrrp vrid 1 virtual-ip 10.1.1.254vrrp vrid 1 priority 200vrrp vrid 1 preempt-mode timer delay 20 # interface Vlanif20ip address 13.1.1…

【pandas】数据透视表【pivot_table】

pivot_table pandas的pivot_table函数是一个非常有用的工具&#xff0c;用于创建一个数据透视表&#xff0c;这是一种用于数据总结和分析的表格形式。 以下是pivot_table的基本语法&#xff1a; pandas.pivot_table(data, valuesNone, indexNone, columnsNone, aggfuncmean,…

【LLM_04】自然语言处理基础_2

一、神经网络1、循环神经网络&#xff08;RNN&#xff09;2、门控循环单元&#xff08;GRU&#xff09;3、长短期记忆网络&#xff08;LSTM&#xff09;4、双向RNN5、卷积神经网络&#xff08;CNN&#xff09; 二、注意力机制1、注意力机制原理介绍2、注意力机制的各种变式3、注…

十大排序之堆排序(详解)

文章目录 &#x1f412;个人主页&#x1f3c5;算法思维框架&#x1f4d6;前言&#xff1a; &#x1f380;堆排序 时间复杂度O(n*logn)&#x1f387;1. 算法步骤思想&#x1f387;2、动画演示&#x1f387;3.代码实现 &#x1f412;个人主页 &#x1f3c5;算法思维框架 &#x1…

Redis集群(新)

1.什么是集群 Redis集群实现了对Redis的水平扩容&#xff0c;可实现并发写操作&#xff0c;启动n个redis节点&#xff0c;将数据分别存储在不同的节点中&#xff0c;每块节点负责不同区域的插槽&#xff0c;所以Redis集群通过分区来提供一定程度的可用性。 Redis集群现采用的是…

mac测试远程端口是否可连接

打开命令行工具&#xff0c;使用命令nc -z ip port即可 &#xff0c;如果成功&#xff0c;则会返回如下信息&#xff1a; 。

算法基础之表达式求值

算法基础之表达式求值 中序表达式求值 用栈 将字符和数字分别用栈存储 由下往上计算 左子树算完再算右子树 判断方法&#xff1a;当前符号优先级<前一个符号优先级 则左右子树已遍历完 #include<iostream>#include<cstring>#include<stack>#include&l…

单文件组件MVVM

单文件组件&MVVM 所谓组件化开发&#xff0c;就是创建一个个组件。 Vue是一个大类&#xff0c;渲染一切从new Vue开始。 指定视图&#xff1a;el template render:jsx语法 $mount[数学公式] 编译App.vue&#xff0c;作为视图入口 单个组件&#xff1a;结构 样式 data compu…

彩纸屋在线少儿编程源码/scratch在线编程系统/培训管理系统源码/在线培训系统源码PHP

源码简介&#xff1a; 彩纸屋在线少儿编程源码&#xff0c;它是scratch在线编程系统&#xff0c;作为培训管理系统源码/在线培训系统源码&#xff0c;采用PHP源码。 彩纸屋是全国首家提供scratch开源定制和少儿编程培训管理系统源代码的服务商&#xff0c;彩纸屋提供的scratc…

MIT6.824-Raft笔记:Raft初探、副本间log时序

从宏观角度说明raft在程序中的作用&#xff0c;和客户端的关系&#xff0c;以及多个副本之间的关系&#xff1b;从微观角度说明多个副本之间raft对日志处理的流程。 1. Raft 初探 宏观角度说明raft在程序中的作用&#xff0c;和客户端的关系&#xff0c;以及多个副本之间的关…

激活函数与非线性化:探索神经网络中的关键元素

随着人工智能领域的迅猛发展&#xff0c;神经网络成为实现各种复杂任务的有力工具。其中&#xff0c;激活函数及其非线性化特性扮演着至关重要的角色。本文将深入探讨激活函数的基本概念、作用原理以及常见的几种激活函数&#xff0c;并介绍它们在神经网络中发挥的重要作用。 …

Web3 进入“殖民时代”

最近在 AI 和 Web3 领域发生了两件“大”事&#xff0c;两件事都具有指标意义&#xff0c;但在媒体上其意义都被大量的八卦细节给掩埋了。 其实看待任何重大事件&#xff0c;都可以有两种不同的视角。第一是娱乐的视角&#xff0c;在新闻事件中找乐子。如果是本着这个目的&…

STM32-使用固件库新建工程

参考链接: 【入门篇】11-新建工程—固件库版本&#xff08;初学者必须认认真真看&#xff09;_哔哩哔哩_bilibili 使用的MCU是STM32F103ZET6 。 这篇参考的是野火的资料&#xff0c;可以在“野火大学堂”或者它的论坛上下载。&#xff08;我通常是野火和正点原子的资料混着看的…

【DDS】OpenDDS配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍OpenDDS配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更…

能让PDF看起来像是扫描件的Look Scanned

什么是 Look Scanned ? Look Scanned 是一个能够让 PDF 看起来就像是扫描件一样的纯前端网站。你再也不需要麻烦地打印之后扫描了&#xff0c;你所需要的就是鼠标点几下。 这是个挺有意思的软件&#xff0c;但是老苏不确定什么场景下会用到这个软件&#xff0c;如果不想自己搭…

89. 打家劫舍【动态规划】

题目 题解 class Solution:def rob(self, nums: List[int]) -> int:N len(nums)# 定义状态: dp[i]表示从第i间房子开始抢劫&#xff0c;最多能抢到的金额dp [0 for i in range(N)]for i in range(N-1, -1, -1):if i N-1:dp[i] nums[i]elif i N-2:dp[i] max(nums[i], …

C语言—一维数组在内存中的存放

1、先看代码&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int arr[]{1,2,3,4,5,6,7,8,9,10}; int szsizeof(arr)/sizeof(arr[0]);int i0;for(i0;i<sz;i){printf("&arr[%d] %p\n",i,&arr[i]);}return 0; } 2、定…

【Mybatis】Mybatis操作数据库详解

Mybatis操作数据库 什么是MybatisMybatis入门准备工作创建Springboot工程 建表 创建实体类 配置数据库连接字符串编写持久层代码单元测试 Mybatis的基础操作打印日志参数传递增(insert)返回主键 删(delete)改(update)查(select) Mybatis XML配置文件配置连接字符串和Mybatis写持…

Jenkins CI/CD

1、 Jenkins CI/CD 流程图 说明&#xff1a;这张图稍微更形象一点&#xff0c;上线之前先把代码git到版本仓库&#xff0c;然后通过Jenkins 如Java项目通过maven去构建&#xff0c;这是在非容器之前&#xff0c;典型的自动化的一个版本上线流程。那它有哪些问题呢&#xff1f; …

Docker Swarm总结+基础、集群搭建维护、安全以及集群容灾(1/3)

博主介绍&#xff1a;Java领域优质创作者,博客之星城市赛道TOP20、专注于前端流行技术框架、Java后端技术领域、项目实战运维以及GIS地理信息领域。 &#x1f345;文末获取源码下载地址&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb;…