封装通用第三方平台用户表(微信开放平台)

news2024/11/28 8:37:07

文章目录

    • 一. 注册微信开放平台
      • 1.1 开发者资质认证
      • 1.2 应用申请
      • 1.3 配置应用
    • 二.通用数据库表设计
    • 三.入库实体类
    • 四. 对接第三方平台
    • 4.1 微信开放平台VO对象
      • 4.2 通用方法


我们的系统可能要对接很多第三方系统,为了便利用户授权使用和对多平台账户的管理。有必要设计通用第三方平台表。用来统一用户管理,将以微信开放平台为例

一. 注册微信开放平台

首先强调的是 微信公众号平台和微信开放平台不是一个东西。 微信开放平台主要用于自己的系统对接微信。 而微信公众号平台主要使用微信公众号,小程序等微信内部程序对接微信开放接口。 而微信公众测试号不可用于微信开放平台。 微信开放平台必须通过企业认证注册应用实体,才能任意配置回调域使用测试域。

微信开放平台
微信开放平台文档
微信公众号平台
微信公众号平台测试号申请
微信公众号平台文档

1.1 开发者资质认证

在这里插入图片描述
主体必须以公司为认证实体
在这里插入图片描述

1.2 应用申请

扫描件内容必须与网站应用名称、网站应用简介、应用官网完全对应。 审查非常严格,不要得过且过。
在这里插入图片描述

1.3 配置应用

sercet只会出现一次,谨慎保管。如果我们需要登录或支付,其对应的接口状态必须为已获得
在这里插入图片描述
回调域配置,当认证通过后,我们可以使用内网穿透域名进行测试。或者使用公司公网,用nginx代理到个人电脑上进行测试开发
在这里插入图片描述
注意,授权回调域不要加http://或https:// 前缀,直接写域名即可,其中阅读开放平台开发文档时,让填写回调地址,那必须在接口请求时追加http:// 或https:// 前缀


二.通用数据库表设计

在这里插入图片描述

-- 第三方平台万能表  可以不绑定用户
-- access_token移除,作为token不入库
-- 移除 refresh_token ,让前端携带access_token同时携带refresh_token,保证可以刷新token
drop table if exists thirdauth_user;
create table thirdauth_user
(
    id          bigint unsigned                           not null comment '主键'
        primary key,
    user_id     bigint unsigned default 0 comment '用户id',
    tenant_id   varchar(32)     default '' comment '租户id 子平台再多,也不可能超过100个',
    nickname    varchar(32)     default ''                null comment '昵称',
    avator      varchar(255)    default ''                null comment '头像地址',
    scope       varchar(255)    default ''                null comment '授权范围',
    platform    varchar(32)                               not null comment '第三方平台名称',
    openid      varchar(64)                               not null comment '第三方平台用户id',
    unionid     varchar(64)     default ''                null comment '第三方内部跨平台id',
    create_time datetime        default CURRENT_TIMESTAMP null comment '创建时间',
    update_time datetime        default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
    is_delete   tinyint         default 0                 null comment '是否删除 0否 1是',
    unique `idx_platform_openid` (`platform`, `openid`)
) comment '用户第三方登录表';

三.入库实体类

/**
 * 用户第三方登录表
 * @TableName thirdauth_user
 */
@TableName(value ="thirdauth_user")
@Data
public class ThirdauthUser implements Serializable {
    /**
     * 主键
     */
    @TableId
    private Long id;

    /**
     * 用户id
     */
    @TableField(value = "user_id")
    private Long userId;

    /**
     * 租户id 内部系统哪个平台的用户id
     */
    @TableField(value = "tenant_id")
    private Long tenantId;

    /**
     * 昵称
     */
    @TableField(value = "nickname")
    private String nickname;

    /**
     * 头像地址
     */
    @TableField(value = "avator")
    private String avator;

    /**
     * 第三方平台名称
     */
    @TableField(value = "platform")
    private String platform;

    /**
     * 第三方平台用户id
     */
    @TableField(value = "openid")
    private String openid;

    /**
     * 第三方内部跨平台id
     */
    @TableField(value = "unionid")
    private String unionid;

    /**
     * scope
     */
    @TableField(value = "scope")
    private String scope;

    /**
     * 创建时间
     */
    @TableField(value = "create_time")
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    @TableField(value = "update_time")
    private LocalDateTime updateTime;

    /**
     * 是否删除 0否 1是
     */
    @TableField(value = "is_delete")
    @TableLogic(value = "0",delval = "1")
    private Integer isDelete;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

四. 对接第三方平台

每个平台返回的数据总有差异,我们需要各种vo来协调,最后都归于上述实体类入库。这里仅列出微信开放平台的封装。

4.1 微信开放平台VO对象

/**
 * @author YuanJie
 * @date 2024/8/20 下午2:32
 */
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class WeChatVO {
    /**
     * 用户授权唯一标识
     */
    private String openid;
    /**
     * 用户授权的access_token
     */
    private String access_token;
    /**
     * access_token接口调用凭证超时时间,单位(秒)
     */
    private Long expires_in;
    /**
     * 用户昵称
     */
    private String nickname;
    /**
     * 用户头像
     */
    private String headimgurl;
    /**
     * 用户刷新access_token  30天有效
     */
    private String refresh_token;
    /**
     * 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
     */
    private String unionid;
    /**
     * 授权资料
     */
    private String scope;
}

4.2 通用方法

/**
 * @author YuanJie
 * @date 2024/6/20 下午2:02
 */
@RestController
@RequestMapping("/oauth2/wx")
@Slf4j
@ApiOperation(value = "oauth2", tags = "oauth2")
public class WXController extends BaseController {

    @Value("${oauth2.wx.appid}")
    private String appid;
    @Value("${oauth2.wx.appSecret}")
    private String appSecret;

    @Resource
    private ThirdauthUserService thirdauthUserService;
    @Resource
    private RestTemplate restTemplate;

    private final List<String> states = Collections.synchronizedList(new ArrayList<>());
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final String WX_PREFIX = "wx";

    /**
     * 127.0.0.1:8080/oauth2/wx/code?backendUrl=http://xxxxo.com/oauth2/wx/callback
     *
     * @param request
     * @param response
     * @param backendUrl
     * @throws IOException
     */
    @ApiOperation("获取微信code")
    @GetMapping("/code")
    public void getWxCode(HttpServletRequest request,
                          HttpServletResponse response,
                          @RequestParam("backendUrl") String backendUrl) throws IOException {
        String state = UUID.randomUUID().toString().replace("-", "");
        this.states.add(state);
        // 回调地址直接填 下面的getCallback()方法。让微信直接回调下面的方法,这里回调地址必须加http https 前缀
        String url = "https://open.weixin.qq.com/connect/qrconnect?" +
                "appid=" + appid +
                "&redirect_uri=" + UriEncoder.encode(backendUrl) +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=" + state + "#wechat_redirect";
        response.sendRedirect(url);
    }

    /**
     * 供微信调用
     * 获取access_token
     *
     * @param code
     */
    @ApiOperation("获取access_token")
    @GetMapping("/callback")
    public AjaxResult getCallback(@RequestParam("code") String code, @RequestParam("state") String state) throws JsonProcessingException {
        // 校验state
        if (!this.states.contains(state)) {
            return error();
        }
        this.states.remove(state);
        // 获取access_token
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                "appid=" + appid +
                "&secret=" + appSecret +
                "&code=" + code +
                "&grant_type=authorization_code";
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        WeChatVO weChatVO = objectMapper.readValue(forEntity.getBody(), WeChatVO.class);
        if (weChatVO == null) {
            return error("获取微信信息异常");
        }
        // 获取用户信息准备入库
        WeChatVO userInfo = setUserInfo(weChatVO.getAccess_token(), weChatVO.getRefresh_token(), weChatVO.getOpenid());
        log.info("当前微信用户信息:{}", objectMapper.writeValueAsString(weChatVO));
        // 返回token
        return success(userInfo);
    }

    /**
     * 请求用户信息
     *
     * @param accessToken
     * @param refreshToken
     * @param openid
     */
    private WeChatVO setUserInfo(String accessToken, String refreshToken, String openid) throws JsonProcessingException {
        // 校验token是否有效 并刷新token
        accessToken = checkToken(accessToken, refreshToken, openid);
        // 请求用户信息
        String userInfo = "https://api.weixin.qq.com/sns/userinfo?" +
                "access_token=" + accessToken +
                "&openid=" + openid;
        ResponseEntity<String> forEntity = restTemplate.getForEntity(userInfo, String.class);
        WeChatVO weChatVO = objectMapper.readValue(forEntity.getBody(), WeChatVO.class);
        if (weChatVO == null) {
            throw new RuntimeException("获取微信信息异常");
        }
        weChatVO.setAccess_token(accessToken);
        weChatVO.setRefresh_token(refreshToken);
        // 保存到数据库
        ThirdauthUser thirdauthUser = new ThirdauthUser();
        thirdauthUser.setPlatform(WX_PREFIX);
        thirdauthUser.setAvator(weChatVO.getHeadimgurl());
        BeanUtils.copyProperties(weChatVO, thirdauthUser);
        thirdauthUserService.saveOrUpdate(thirdauthUser,
                Wrappers.lambdaQuery(ThirdauthUser.class)
                        .eq(ThirdauthUser::getOpenid, weChatVO.getOpenid())
                        .eq(ThirdauthUser::getPlatform, WX_PREFIX));
        return weChatVO;
    }

    /**
     * 校验token是否有效 并返回刷新的token
     *
     * @param accessToken
     * @param refreshToken
     * @param openid
     */
    private String checkToken(String accessToken, String refreshToken, String openid) throws JsonProcessingException {
        String checkToken = "https://api.weixin.qq.com/sns/auth?" +
                "access_token=" + accessToken +
                "&openid=" + openid;
        ResponseEntity<String> forEntity = restTemplate.getForEntity(checkToken, String.class);
        String body = forEntity.getBody();
        Map map = objectMapper.readValue(body, Map.class);
        if (map.get("errcode").equals(0)) {
            return refreshToken(refreshToken);
        }
        throw new RuntimeException("token失效,请重新登录");

    }

    /**
     * 刷新token 若refreshToken失效,则重新登录
     *
     * @param refreshToken
     * @return
     */
    private String refreshToken(String refreshToken) throws JsonProcessingException {
        String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?" +
                "appid=" + appid +
                "&grant_type=refresh_token" +
                "&refresh_token=" + refreshToken;
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        WeChatVO weChatVO = objectMapper.readValue(forEntity.getBody(), WeChatVO.class);
        if (weChatVO == null) {
            throw new RuntimeException("刷新token失败,refreshToken已过期,请重新登录");
        }
        return weChatVO.getAccess_token();
    }

在这里插入图片描述

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

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

相关文章

2024 江苏省第二届数据安全技术应用职业技能竞赛 初赛 部分wp

文章目录 一、前言二、参考文章三、题目&#xff08;解析&#xff09;数据安全解题赛1、ds_0602&#xff08;30分&#xff09;2、333.file&#xff08;45分&#xff09;3、pf文件分析&#xff08;35分&#xff09;4、丢失的资料&#xff08;45分&#xff09;5、greatphp&#x…

基于SpringBoot的来访管理系统的设计与实现-

TOC springboot600基于SpringBoot的来访管理系统的设计与实现---论文 绪 论 1.1项目研究的背景 随着科学技术发展&#xff0c;计算机已成为人们生活中必不可少的生活办公工具&#xff0c;在这样的背景下&#xff0c;网络技术被应用到各个方面&#xff0c;为了提高办公生活效…

Android类加载机制简介

一、前言 随着 Android 技术的不断发展&#xff0c;对其内部机制的探索也日益深入。类加载机制作为 Android 运行时环境的核心组成部分之一&#xff0c;影响着应用的性能、安全性以及可扩展性。通过对 Android 类加载机制的研究&#xff0c;开发者可以更好地优化代码结构、提高…

requests请求库入门使用

requests 库是一个功能强大且易于使用的 HTTP 请求库&#xff0c;适用于各种网络请求任务。它简化了 HTTP 请求的发送过程&#xff0c;并提供了丰富的功能来处理各种网络请求和响应。 1.安装 首先&#xff0c;你需要安装 requests 库。如果你还没有安装&#xff0c;可以使用 …

网优学习干货:2.6G仿真操作(1)

2.6G工程建立——整体仿真过程 针对覆盖仿真、速率仿真及蒙特卡洛仿真的说明 覆盖仿真&#xff1a;覆盖仿真主要用于评估网络覆盖性能。基于MassiveMIMO天线、射线传模或经验传模进行覆盖预测&#xff0c;计算链路损耗后&#xff0c;基于栅格分析估算小区覆盖预测的各项指标&am…

前端不同项目使用不同的node版本(Volta管理切换)

前端不同项目使用不同的node版本(Volta管理切换) 使用volta自动切换前端项目的node版本&#xff0c; 每个不同的前端项目&#xff0c;可以使用不同的node版本。Volta这个工具&#xff0c;它允许用户方便地安装、切换和管理不同版本的Node.js&#xff0c;避免了为每个项目手动配…

QT6聊天室项目 核心类与主窗口设计逻辑分析

核心类 核心类设计逻辑 数据结构设计&#xff08;data.h&#xff09; 用户信息 用户ID用户网名用户个人签名用户手机号码用户头像聊天会话信息 会话编号会话名称&#xff08;单聊则是对方网名&#xff0c;群聊则是群名&#xff09;最新消息会话图标&#xff08;单聊对方头像&a…

sortable中el-table拖拽及点击箭头上下移动row

效果 安装 npm install sortablejs --save 引入 import Sortable from "sortablejs"; <el-table:data"tableBody"borderref"tableRef":stripe"true":key"tableKey"><el-table-column type"index" la…

driver.find_element 找不到元素的大坑

前端使用element框架。 登录进去使用开发人员工具能看到元素&#xff0c;复制xpath使用find_element死活找不到。 其中一次复制的xpath 注意红色部分: #先点击一下输入框 driver.find_element(By.XPATH,/html/body/div[1]/section/section/section/main/div/div[1]/div/form/…

迎接 Apple Intelligence.一:首次啟用

年 Apple 各平台的重點新功能&#xff0c;相信非 Apple Intelligence 莫屬。Apple 將會將人工智能整合到 iOS 18、iPadOS 18 和 macOS Squoia。在機械學習和大型語言模型加持下&#xff0c;預計 Siri 的對答會更加自然流暢&#xff0c;而且能直接提供答案&#xff0c;而不像過去…

作业8/21

client cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), socket(new QTcpSocket(this)) {ui->setupUi(this); // 设置 UI 界面// 控件初始状态设置为禁用&#xff0c;防止未连接…

储能系统---德业、禾迈、昱能产品布局及对比

一、公司介绍 德业、昱能、禾迈作为微逆行业的佼佼者&#xff0c;同样代表了中国制造行业的三种技术形态。 德业&#xff1a;代表传统制造行业&#xff0c;转型电力电子领域的代表。 昱能&#xff1a;代表海归人员回国创业&#xff0c;致力于技术本土化的代表。 禾迈&#…

socket 验证字符串是否是有效ip地址

import socketdef is_valid_ip(ip):try:# 尝试将字符串解析为IP地址socket.inet_aton(ip)return Trueexcept socket.error:return False# 测试 print(is_valid_ip("192.168.1.1")) # True print(is_valid_ip("256.256.256.256")) # False print(is_valid…

计算机存储原理——基础

文章目录 存储介质简介1. 光介质存储2. 磁介质存储3. flash(闪存) 硬盘存储原理磁盘组件磁盘是如何存储文件的磁盘的性能 存储接口与存储协议接口类型SCSISCSI的寻址方式windows下查看SCSI寻址 接口与总线与协议的对应关系cpu是如何控制存储的 文件系统常见的文件系统文件系统中…

《数据结构》顺序表+算法代码+动画演示-C语言版

目录 顺序表概念 顺序表初始化 顺序表销毁 顺序表尾插 顺序表尾删 顺序表头删 顺序表头插 顺序表pos位置插入 顺序表pos位置删除 顺序表全部代码如下&#xff1a; 顺序表概念 顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下…

「OC」CAShapeLayer和UIBezierPath —— 抽屉视图之中圆角cell的实现

「OC」CAShapeLayer和UIBezierPath —— 抽屉视图之中圆角cell的实现 文章目录 「OC」CAShapeLayer和UIBezierPath —— 抽屉视图之中圆角cell的实现前言CALayer的子类CAShapeLayerpath 贝塞尔曲线应用实践参考资料 前言 上次我们学习了CALayer的相关知识 &#xff0c;但好像忘…

2024计算机软考报名流程(电脑报名)

1.24年下半年软考报名时间&#xff0c;各省报名时间不一样&#xff0c; 报名时间大概集中在&#xff1a;24年8月19日&#xff5e;24年9月15日&#xff1b; 报名网站&#xff1a;中国计算机技术职业资格网&#xff1b; 广东&#xff1a;2024年8月21日9:00至29日17:00 安徽&#…

全面调研:19类AI Agent框架的对比与分析

代理&#xff08;Agent&#xff09;指能自主感知环境并采取行动实现目标的智能体&#xff0c;即AI作为一个人或一个组织的代表&#xff0c;进行某种特定行为和交易&#xff0c;降低一个人或组织的工作复杂程度&#xff0c;减少工作量和沟通成本。 背景 目前&#xff0c;我们在…

监听页面滚动到某个元素 设置入场动画

mounted() {this.eventScroll()}, eventScroll() {// 选择你想要监听的元素const targetLogo document.getElementById(logoanimation);// 创建并配置IntersectionObserverconst observer new IntersectionObserver((entries) > {entries.forEach(entry > {if (entry.…

回收站的文件删除了怎么恢复?4个技巧轻松找回文件!

在日常使用电脑的过程中&#xff0c;回收站作为我们删除文件的临时存放地&#xff0c;扮演着重要的角色。然而&#xff0c;有时我们可能会不小心从回收站中删除了重要文件&#xff0c;导致数据丢失。面对这种情况&#xff0c;许多用户会感到焦虑和无助。但别担心&#xff0c;本…