企业级微信小程序服务号授权关联机制

本文介绍对于同一主体(企业)如何在其小程序内部进行服务号授权,从而获取服务号的 openid,实现小程序端与服务号的关联。

openid 与 UnionID 机制

在关注者与服务号产生消息交互后,服务号可获得关注者的OpenID(加密后的微信号,每个用户对每个服务号的OpenID是唯一的。对于不同服务号,同一用户的openid不同)。服务号可通过获取用户基本信息来根据OpenID获取用户基本信息,包括语言和关注时间。

请注意,如果开发者有在多个服务号,或在服务号、移动应用、小程序、小店、带货助手、网站应用之间统一用户账号的需求,需要前往微信开放平台(open.weixin.qq.com)绑定服务号后,才可利用 UnionID 机制来满足上述需求。

openid,用户的身份标记,关注服务号后产生(取消关注后依然存在),只要服务号不变,openid 就不会变。

unionid,用户(主要针对开发者)在开放平台注册后产生,不是所有的用户都有 unionid 。

所以,对于一般用户而言,如果想在小程序内使用服务号的功能(比如发送模板消息),就需要知道这个用户的 openid

服务号网页授权

获取 openid 的前提是必须得到用户的授权。好在服务号提供了微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

网页授权流程分为四步:

  1. 引导用户进入授权页面;
  2. 接受用户授权同意,在回调页面中获取 code;
  3. 通过code换取用户授权 access_token(与服务端接口 access_token 不同);
  4. 通过用户授权 access_token 和 openid 获取用户基本信息(支持 UnionID 机制);

访问授权链接

用户在小程序中使用 web-view 访问授权链接,链接格式如下:

open.weixin.qq.com/connect/oau...

具体参数配置说明,可以直接查看 👉️跳转授权链接

其中有几点需要注意:

第一,链接的参数顺序不要更改,因为授权操作安全等级较高,微信会对授权链接做正则强匹配校验;

第二,这里提及到一个回调页面 ,这个页面需要我们自己提供,在我们引导用户进入授权页面时,需要提前将回调页面的网址作为 redirect_uri 参数一并发送给服务号的授权接口。这样,code 才会返回至 redirect_uri 页面,供开发者使用。

接下来,我们就先部署一下回调页面。

部署回调中转网页

这个中转页面其实内容很简单,就是获取地址后面拼接的 code 参数,然后根据自己的实际业务需求跳转到对应的小程序页面。

处理 code

我们可以通过以下方法快速获取查询参数中的 code 字符串。

js 复制代码
// 获取查询参数
getUrlSearchParams() {
    const obj = {}
    const params = new URLSearchParams(window.location.search);
    params.forEach((value, key) => {
        obj[key] = value;
    });
    return obj;
},

返回小程序

基于 web-view 的能力,我们可以在网页中可使用 JSSDK 1.3.2提供的接口返回小程序页面。

js 复制代码
// 处理 code
getCode() {
    const { code /* 也可以有其他参数 platform */ } = this.getUrlSearchParams();

    switch (platform) {
        // 1. 返回小程序中的 aa 页面
        case 'aa':
            wx.miniProgram.redirectTo({ url: '/pages/common/aa/index?code=' + code });
            break;
        // 2. 返回小程序中的 bb 页面
        case 'bb':
            wx.miniProgram.redirectTo({ url: '/pages/common/bb/index?code=' + code });
            break;
        // 3. 也可以跳转到 web 页面
        case 'cc':
            window.location.href = 'https://your.web.com/login?code=' + code;
            break;
        default:
            break;
    }
}

完整代码详见 👉️github | 服务号网页授权

网页授权回调域名

在部署网页时,需要遵守服务号的一些配置要求。比如,必须通过 ICP 备案,使用 https,安装校验文件等。

之后,我们便可在服务号中进行功能配置。

至此,前端部分的工作就完成了,我们接下来要做的就是将获取到的 code 发送给服务端,让服务端去调用 API 获取用户信息。

服务端获取 openid

换取用户授权凭证

服务端接受到 code 后,调用服务端 API 换取用户授权凭证,即可获取 access_token、openid、unionid 等信息。

java 复制代码
@Override
public String wechatQueryOpenid(String code) {
    String authStr = restTemplate.exchange(
            "https://api.weixin.qq.com/sns/oauth2/access_token?appid=appid&secret=secret&code=" + code + "&grant_type=authorization_code",
            HttpMethod.GET,
            null,
            new ParameterizedTypeReference<String>() {}
        ).getBody();
    Map authMap = (Map) JSON.parse(authStr);
    if (authMap == null || authMap.get("openid") == null) {
        return null;
    }
    String openid = (String) authMap.get("openid");
    
    // todo: 保存到用户的信息中......
    
    return openid;
}

发送模板消息

我们以发送服务号模板消息为例,来演示一下获取服务号 openid 之后的业务场景。

java 复制代码
@Override
public void sendModelMessage(String openId, Map wechatNoticeVO) {
    try {
        //获取微信公众号 token
        String serviceAccessToken = getServiceAccessToken();

        //封装请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        wechatNoticeVO.put("touser", openId);
        HttpEntity<HashMap> requestEntity = new HttpEntity(wechatNoticeVO, headers);

        //调用请求
        HashMap result = restTemplate.exchange(
                "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + serviceAccessToken,
                HttpMethod.POST,
                requestEntity,
                new ParameterizedTypeReference<HashMap>() {}
        ).getBody();
        Integer successode = new Integer("0");
        if (result == null || !successode.equals((Integer) result.get("errcode"))) {
            log.info("发送消息异常" + (Integer) result.get("errcode") + ":" + (String) result.get("errmsg"));
            Integer tokenExpired = new Integer("42001");
            if (tokenExpired.equals((Integer) result.get("errcode"))) {
                redisTemplate.delete(RedisKeyConstants.WECHAT_AUTHTOKEN);
            }
        }
    } catch (Exception e) {
        log.info("发送服务号模板消息异常!", e);
    }

}

private String getServiceAccessToken() {
    // 尝试先从 redis 中获取 token
    String authToken = (String) redisTemplate.opsForValue().get(RedisKeyConstants.WECHAT_AUTHTOKEN);

    // 当没有获取到 token 的时候重新获取 token
    if (StringUtils.isBlank(authToken)) {
        Map authMap = restTemplate.exchange(
            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + secret,
            HttpMethod.POST,
            null,
            new ParameterizedTypeReference<HashMap>() {}
        ).getBody();
        if (authMap != null && authMap.get("access_token") != null) {
            authToken = (String) authMap.get("access_token");
            redisTemplate.opsForValue().set(RedisKeyConstants.WECHAT_AUTHTOKEN, authToken, 1000, TimeUnit.SECONDS);
        } else {
            authToken = getServiceAccessToken();
        }
    }
    return authToken;
}

完整流程

当然,获取 openid 的方法不止一种,欢迎大家可以留言讨论~ 🤗

参考资料

相关推荐
软件聚导航12 小时前
「聚小软 AI 助手」技术升级:从数据库检索到 RAG 知识库问答
前端·数据库·mysql·微信小程序·小程序·ai编程·rag
一叶星殇16 小时前
微信小程序 Vant Popup 滚动穿透:页面级与组件级解决方案详解
微信小程序·小程序
2601_9638699521 小时前
【计算机毕业设计】基于微信小程序的点餐传菜系统设计与实现
微信小程序·小程序·课程设计
北漂燕郊杨哥1 天前
VS Code 安装微信小程序 MCP 介绍
vscode·微信小程序·小程序·mcp
维双云2 天前
2026微信小程序开店用哪个平台?长期稳定运营的系统选择方法
微信小程序·小程序
辛迪聊物业数字化2 天前
【智能楼宇系统有什么用?赋能园区高效 节能 安全】
经验分享·安全·微信小程序·php
Coodor2 天前
使用web也可以写NFC微信小程序拉取
前端·微信小程序·小程序·nfc拉起小程序
太阳之神aboluo3 天前
微信小程序 (1) 基础用法
微信小程序·小程序
黑马程序员毕设3 天前
基于B/S架构的“指尖乡味”助农电商小程序系统设计与实现
spring boot·微信小程序·小程序·架构·课程设计·毕设
编程小橘子4 天前
基于微信小程序健康心理测试、设计与实现 (源码+lw+参考文档+核心代码讲解等)
微信小程序·小程序·毕业设计·毕设