精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

SpringCloud— 微信小程序授權登錄增加多租戶配置

開發 架構
現在我們需要結合Weixin-Java-Miniapp的多租戶實現整合到我們的框架中,使多租戶可通過系統配置界面來新增多租戶小程序。

GitEgg框架集成weixin-java-miniapp工具包以實現微信小程序相關接口調用功能,weixin-java-miniapp底層支持多租戶擴展。每個小程序都有唯一的appid,weixin-java-miniapp的多租戶實現并不是以租戶標識TenantId來區分的,而是在接口調用時,傳入appid,動態切換ThreadLocal的appid來實現多租戶的。并且其多個微信小程序的配置,都是在配置yml文件中的,在實際業務運營過程中,如果需要新增多租戶小程序就修改配置文件顯然是不合適的。

現在我們需要結合weixin-java-miniapp的多租戶實現整合到我們的框架中,使多租戶可通過系統配置界面來新增多租戶小程序。前面我們講了如何集成以及如何使用weixin-java-miniapp實現微信授權登錄及賬號綁定等,現在只需要在原來的基礎上增加數據配置存儲,在服務啟動時由原先的讀取配置文件加載相應的微信小程序接口實例,修改為可以通過讀取配置文件和讀取緩存配置來生成相應的微信小程序接口實例。

一、新增微信小程序配置界面

1、微信小程序配置數據庫設計

在數據庫設計的時候,我們需要知道微信小程序授權時,哪些字段需要配置,是可選字段還是必填字段,這里我們通過weixin-java-miniapp的springboot工程配置文件可知,所需字段有:

# 公眾號配置(必填)
wx.miniapp.appid = appId
wx.miniapp.secret = @secret
wx.miniapp.token = @token
wx.miniapp.aesKey = @aesKey
wx.miniapp.msgDataFormat = @msgDataFormat                  # 消息格式,XML或者JSON.
# 存儲配置redis(可選)
# 注意: 指定redis.host值后不會使用容器注入的redis連接(JedisPool)
wx.miniapp.config-storage.type = Jedis                     # 配置類型: Memory(默認), Jedis, RedisTemplate
wx.miniapp.config-storage.key-prefix = wa                  # 相關redis前綴配置: wa(默認)
wx.miniapp.config-storage.redis.host = 127.0.0.1
wx.miniapp.config-storage.redis.port = 6379
# http客戶端配置
wx.miniapp.config-storage.http-client-type=HttpClient      # http客戶端類型: HttpClient(默認), OkHttp, JoddHttp
wx.miniapp.config-storage.http-proxy-host=
wx.miniapp.config-storage.http-proxy-port=
wx.miniapp.config-storage.http-proxy-username=
wx.miniapp.config-storage.http-proxy-password=

根據我們的設計,配置文件中需要增加租戶字段,我們需要兼容即使用配置文件來配置微信小程序,又可以使用配置界面將微信小程序配置信息配置到數據庫中,同時,增加md5字段配置,用于在讀取配置時比較配置信息是否有更改。所以,保存微信小程序配置的數據庫設計如下:

CREATE TABLE `t_wechat_miniapp`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租戶id',
  `miniapp_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序名稱',
  `appid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序appid',
  `secret` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序secret',
  `token` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序token',
  `aes_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '微信小程序aesKey',
  `msg_data_format` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '消息格式,XML或者JSON',
  `storage_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '配置類型: Memory(默認), Jedis, RedisTemplate',
  `key_prefix` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '相關redis前綴配置: wa(默認)',
  `redis_host` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'Redis服務器地址',
  `redis_port` int(11) NULL DEFAULT NULL COMMENT 'Redis服務器端口',
  `http_client_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http客戶端類型: HttpClient(默認), OkHttp, JoddHttp',
  `http_proxy_host` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http_proxy_host',
  `http_proxy_port` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http_proxy_port',
  `http_proxy_username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http_proxy_username',
  `http_proxy_password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'http_proxy_password',
  `status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1' COMMENT '狀態 1有效 0禁用',
  `md5` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'MD5',
  `comments` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '創建時間',
  `creator` bigint(20) NULL DEFAULT NULL COMMENT '創建者',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新時間',
  `operator` bigint(20) NULL DEFAULT NULL COMMENT '更新者',
  `del_flag` tinyint(2) NOT NULL DEFAULT 0 COMMENT '是否刪除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信小程序配置' ROW_FORMAT = DYNAMIC;

2、通過代碼生成器生成微信小程序配置的增刪查改代碼

通過代碼生成器根據新設計的表進行CRUD代碼生成,詳細步驟不再贅述,前面有詳細講解如何根據數據庫表設計生成前后端代碼。只是這里需要增加業務邏輯處理,以及更新到緩存配置。

  • 新增時,將配置信息添加到Redis,并且需要根據系統是否開啟多租戶來判斷生成緩存key
/**
    * 創建微信小程序配置
    * @param miniapp
    * @return
    */
    @Override
    public boolean createMiniapp(CreateMiniappDTO miniapp) {
        Miniapp miniappEntity = BeanCopierUtils.copyByClass(miniapp, Miniapp.class);
        try {
            String miniappEntityStr = JsonUtils.objToJson(miniappEntity);
            miniappEntity.setMd5(SecureUtil.md5(miniappEntityStr));
        } catch (Exception e) {
            log.error("創建微信小程序配置時,md5加密失敗:{}", e);
            throw new BusinessException("創建微信小程序配置時,md5加密失敗:" + e);
        }
        boolean result = this.save(miniappEntity);
        if (result)
        {
            // 更新到緩存
            Miniapp miniappEntityLocal = this.getById(miniappEntity.getId());
            MiniappDTO miniappDTO = BeanCopierUtils.copyByClass(miniappEntityLocal, MiniappDTO.class);
            this.addOrUpdateMiniappCache(miniappDTO);
        }
        return result;
    }
  • 編輯時,需要更新Redis配置信息,因為會有key也同時修改的情況,所以,需要先刪除舊的配置信息,再新增新的配置信息
/**
    * 更新微信小程序配置
    * @param miniapp
    * @return
    */
    @Override
    public boolean updateMiniapp(UpdateMiniappDTO miniapp) {
        Miniapp miniappEntity = BeanCopierUtils.copyByClass(miniapp, Miniapp.class);
        Miniapp miniappEntityOld = this.getById(miniappEntity.getId());
        try {
            String miniappEntityStr = JsonUtils.objToJson(miniappEntity);
            miniappEntity.setMd5(SecureUtil.md5(miniappEntityStr));
        } catch (Exception e) {
            log.error("創建微信小程序配置時,md5加密失敗:{}", e);
            throw new BusinessException("創建微信小程序配置時,md5加密失敗:" + e);
        }
        boolean result = this.updateById(miniappEntity);
        if (result)
        {
            // 把舊的刪掉
            MiniappDTO miniappDTOOld = BeanCopierUtils.copyByClass(miniappEntityOld, MiniappDTO.class);
            this.deleteMiniappCache(miniappDTOOld);
            // 更新到緩存
            Miniapp miniappEntityLocal = this.getById(miniappEntity.getId());
            MiniappDTO miniappDTO = BeanCopierUtils.copyByClass(miniappEntityLocal, MiniappDTO.class);
            this.addOrUpdateMiniappCache(miniappDTO);
        }
        return result;
    }
  • 刪除時,直接根據條件生成緩存key,然后進行刪除即可
/**
    * 刪除微信小程序配置
    * @param miniappId
    * @return
    */
    @Override
    public boolean deleteMiniapp(Long miniappId) {
        // 從緩存刪除
        Miniapp miniappEntity = this.getById(miniappId);
        MiniappDTO miniappDTO = BeanCopierUtils.copyByClass(miniappEntity, MiniappDTO.class);
        this.deleteMiniappCache(miniappDTO);
        // 從數據庫中刪除
        boolean result = this.removeById(miniappId);
        return result;
    }
  • 新增/更新緩存的公共方法
private void addOrUpdateMiniappCache(MiniappDTO miniappDTO) {
        try {

            String redisKey = MiniappConstant.WX_MINIAPP_CONFIG_KEY;
            if (enable) {
                redisKey = MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + miniappDTO.getAppid();
            }
            redisTemplate.opsForHash().put(redisKey, miniappDTO.getTenantId().toString(), JsonUtils.objToJson(miniappDTO));

            // wxMaService增加config
            this.addConfig(miniappDTO);

        } catch (Exception e) {
            log.error("初始化微信小程序配置失敗:{}" , e);
        }
    }
  • 刪除緩存的公共方法
private void deleteMiniappCache(MiniappDTO miniappDTO) {
        try {

            String redisKey = MiniappConstant.WX_MINIAPP_CONFIG_KEY;
            if (enable) {
                redisKey = MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + miniappDTO.getAppid();
            }
            redisTemplate.opsForHash().delete(redisKey, miniappDTO.getTenantId().toString(), JsonUtils.objToJson(miniappDTO));
            // wxMaService刪除config
            this.removeConfig(miniappDTO);
        } catch (Exception e) {
            log.error("初始化微信小程序配置失敗:{}" , e);
        }
    }

3、修改相關配置文件,使微信小程序配置既支持配置文件又支持數據庫配置

  • 新增GitEggWxMaRedissonConfigImpl類繼承自WxMaRedissonConfigImpl,增加我們需要的租戶配置字段:configKey、tenantId、md5。
/**
 * @author GitEgg
 * @date 2023/7/21
 */
public class GitEggWxMaRedissonConfigImpl extends WxMaRedissonConfigImpl {

    protected String configKey;

    protected String tenantId;

    protected String md5;

    public GitEggWxMaRedissonConfigImpl(@NonNull RedissonClient redissonClient, String keyPrefix) {
        super(redissonClient, keyPrefix);
    }

    public GitEggWxMaRedissonConfigImpl(@NonNull RedissonClient redissonClient) {
        super(redissonClient);
    }
......
}
  • 修改WxMaProperties類,增加我們需要的字段tenantId,因為configKey和md5是系統生成的,只有動態可配置時才會用到這幾個字段去判斷,這里如果是配置文件配置,修改后生效必須重啟系統,所以這里不需要這幾個字段。
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {

    private List<Config> configs;

    @Data
    public static class Config {

        /**
         * 租戶
         */
        private Long tenantId;

        /**
         * 設置微信小程序的appid
         */
        private String appid;

        /**
         * 設置微信小程序的Secret
         */
        private String secret;

        /**
         * 設置微信小程序消息服務器配置的token
         */
        private String token;

        /**
         * 設置微信小程序消息服務器配置的EncodingAESKey
         */
        private String aesKey;

        /**
         * 消息格式,XML或者JSON
         */
        private String msgDataFormat;
    }

}
  • 修改WxMaConfiguration類,我們使用自己定義的緩存方式和Config類進行相關操作,這里的緩存我們使用Redisson。
@Bean
    public WxMaService wxMaService() {
        List<WxMaProperties.Config> configs = this.properties.getConfigs();
        //已添加緩存配置,如果配置文件沒有,那么在緩存新增時,仍然可以setConfigs
//        if (configs == null) {
//            throw new WxRuntimeException("大哥,拜托先看下項目首頁的說明(readme文件),添加下相關配置,注意別配錯了!");
//        }
        WxMaService maService = new WxMaServiceImpl();
        if (null != configs)
        {
            maService.setMultiConfigs(
                    configs.stream()
                            .map(a -> {
//                    WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
//                    WxMaDefaultConfigImpl config = new WxMaRedisConfigImpl(new JedisPool());
                                GitEggWxMaRedissonConfigImpl config = new GitEggWxMaRedissonConfigImpl(redissonClient);
                                // 使用上面的配置時,需要同時引入jedis-lock的依賴,否則會報類無法找到的異常
                                config.setTenantId(null != a.getTenantId() ? a.getTenantId().toString() : AuthConstant.DEFAULT_TENANT_ID.toString());
                                config.setConfigKey(config.getTenantId() + StrPool.UNDERLINE + a.getAppid());
                                config.setAppid(a.getAppid());
                                config.setSecret(a.getSecret());
                                config.setToken(a.getToken());
                                config.setAesKey(a.getAesKey());
                                config.setMsgDataFormat(a.getMsgDataFormat());
                                return config;
                            }).collect(Collectors.toMap( GitEggWxMaRedissonConfigImpl::getConfigKey, a -> a, (o, n) -> o)));
        }
        return maService;
    }

二、服務啟動時加載微信小程序配置信息

1、新增加載方法,服務啟動時加載配置數據需要排除多租戶插件,之所以在啟動時加載配置信息到緩存,是因為配置的微服務和小程序相關功能的服務不是同一個服務,所以將緩存作為相關配置的存儲

  • MiniappMapper中新增initMiniappList數據庫查詢方法,一定要加@InterceptorIgnore(tenantLine = “true”)注解,表示此查詢不需要多租戶控制,在服務啟動時不區分租戶加載所有配置。
/**
     * 排除多租戶插件查詢微信配置列表
     * @param miniappDTO
     * @return
     */
    @InterceptorIgnore(tenantLine = "true")
    List<MiniappDTO> initMiniappList(@Param("miniapp") QueryMiniappDTO miniappDTO);
<!-- 不區分租戶查詢微信小程序配置信息 -->
    <select id="getMiniapp" resultType="com.gitegg.boot.extension.wx.miniapp.dto.MiniappDTO" parameterType="com.gitegg.boot.extension.wx.miniapp.dto.QueryMiniappDTO">
        SELECT
        <include refid="Base_Column_List"/>
        FROM t_wechat_miniapp
        WHERE del_flag = 0
        <if test="miniapp.miniappName != null and miniapp.miniappName != ''">
            AND miniapp_name = #{miniapp.miniappName}
        </if>
        <if test="miniapp.appid != null and miniapp.appid != ''">
            AND appid = #{miniapp.appid}
        </if>
        <if test="miniapp.secret != null and miniapp.secret != ''">
            AND secret = #{miniapp.secret}
        </if>
        <if test="miniapp.status != null and miniapp.status != ''">
            AND status = #{miniapp.status}
        </if>
        ORDER BY id DESC
    </select>
  • MiniappServiceImpl中新增initMiniappList接口的實現方法,緩存配置的增刪查改方法也在此類中實現,這里不再贅述,更多了解可以查看框架代碼。
/**
     * 初始化微信小程序配置表列表
     * @return
     */
    @Override
    public void initMiniappList() {
        QueryMiniappDTO miniappDTO = new QueryMiniappDTO();
        miniappDTO.setStatus(String.valueOf(GitEggConstant.ENABLE));
        // 這里初始化所有的配置,不再只初始化已啟用的配置
        List<MiniappDTO> miniappInfoList = miniappMapper.initMiniappList(miniappDTO);

        // 判斷是否開啟了租戶模式,如果開啟了,那么需要按租戶進行分類存儲
        if (enable) {
            Map<String, List<MiniappDTO>> miniappListMap =
                    miniappInfoList.stream().collect(Collectors.groupingBy(MiniappDTO::getAppid));
            miniappListMap.forEach((key, value) -> {
                String redisKey = MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + key;
                redisTemplate.delete(redisKey);
                addMiniapp(redisKey, value);
            });
        } else {
            redisTemplate.delete(MiniappConstant.WX_MINIAPP_CONFIG_KEY);
            addMiniapp(MiniappConstant.WX_MINIAPP_CONFIG_KEY, miniappInfoList);
        }
    }
  • 在InitExtensionCacheRunner系統配置加載類中新增initMiniappList的調用
/**
 * 容器啟動完成加載擴展信息數據到緩存
 * @author GitEgg
 */
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component
public class InitExtensionCacheRunner implements CommandLineRunner {
    
    private final IJustAuthConfigService justAuthConfigService;
    
    private final IJustAuthSourceService justAuthSourceService;

    private final IMailChannelService mailChannelService;

    private final IMiniappService miniappService;

    @Override
    public void run(String... args) {

        log.info("InitExtensionCacheRunner running");
    
    
        // 初始化第三方登錄主配置
        justAuthConfigService.initJustAuthConfigList();

        // 初始化第三方登錄 第三方配置
        justAuthSourceService.initJustAuthSourceList();

        // 初始化郵件配置信息
        mailChannelService.initMailChannelList();

        // 初始化微信配置信息
        miniappService.initMiniappList();

    }
}

2、實現動態選擇某個租戶微信小程序接口的方法,我們需要在保證原先讀取配置文件的方式仍然可用的基礎上擴展讀取數據庫緩存配置信息,所以,在接口實現時需要充分考慮原先配置方式可用

  • 通過appid獲取組裝后的key值,在WxMaService中存儲著默認以appid為key值的配置configMap,這里我們將key值修改為tenantId_appid的格式,作為多租戶的擴展,同時在獲取配置信息時,也需要傳入多租戶信息。
  • 如果前端傳了租戶,那么先使用前端的租戶,如果沒有傳租戶,那么從系統中查詢租戶。
  • 從緩存獲取配置對象,如果md5配置和系統配置不一樣,那么需要重新add。
  • 緩存配置中沒有也需要直接返回,因為有可能是配置文件配置的。
  • 取緩存中所有appid的配置租戶,如果存在多個租戶,那么提示錯誤,讓前端選擇租戶;如果只有一個租戶,那么返回。
/**
     * 通過appid獲取appid,忽略租戶插件
     * @param miniappId
     * @return
     */
    @Override
    public String getMiniappId(String miniappId) {
        if (enable) {
            // 如果前端傳了租戶,那么先使用前端的租戶,如果沒有傳租戶,那么從系統中查詢租戶
            String tenantId = GitEggAuthUtils.getTenantId();
            if (!StringUtils.isEmpty(tenantId))
            {
                String miniappStr = (String) redisTemplate.opsForHash().get(MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + miniappId, tenantId);
                if (!StringUtils.isEmpty(miniappStr))
                {
                    // 轉為系統配置對象
                    try {
                        // 從緩存獲取配置對象,如果md5配置和系統配置不一樣,那么需要重新add
                        MiniappDTO miniappDTO = JsonUtils.jsonToPojo(miniappStr, MiniappDTO.class);
                        return this.ifConfig(miniappDTO);
                    } catch (Exception e) {
                        log.error("獲取微信小程序配置時發生異常:{}", e);
                        throw new BusinessException("獲取微信小程序配置時發生異常。");
                    }
                }
                // 緩存配置中沒有也需要直接返回,因為有可能是配置文件配置的
                return tenantId + StrPool.UNDERLINE + miniappId;
            } else {
                String redisKey = MiniappConstant.WX_MINIAPP_TENANT_CONFIG_KEY + miniappId;
                // 取緩存中所有appid的配置租戶,如果存在多個租戶,那么提示錯誤,讓前端選擇租戶;如果只有一個租戶,那么返回
                List<Object> values = redisTemplate.opsForHash().values(redisKey);
                if (!CollectionUtils.isEmpty(values))
                {
                    if (values.size() > GitEggConstant.Number.ONE)
                    {
                        throw new BusinessException("此小程序配置在多個租戶下,請選擇所需要操作的租戶。");
                    }

                    String miniappConfig = (String) values.stream().findFirst().orElse(null);
                    try {
                        MiniappDTO miniappConfigDTO = JsonUtils.jsonToPojo(miniappConfig, MiniappDTO.class);
                        return this.ifConfig(miniappConfigDTO);
                    } catch (Exception e) {
                        log.error("獲取緩存小程序配置失敗:{}", e);
                        throw new BusinessException("小程序已被禁用,請聯系管理員");
                    }
                }
                else
                {
                    return AuthConstant.DEFAULT_TENANT_ID + StrPool.UNDERLINE + miniappId;
                }

            }
        } else {
            return AuthConstant.DEFAULT_TENANT_ID + StrPool.UNDERLINE + miniappId;
        }
    }

3、修改原先的切換租戶方法調用,在WxMaUserController中,將原來的wxMaService.switchover(appid)修改為我們自己的實現miniappService.switchover(appid),其它有切換租戶調用的都改為此方法

if (!miniappService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到對應appid=[%s]的配置,請核實!", appid));
        }

4、微信小程序前端代碼不需要修改,還是按照前面章節的說明進行調用即可。在請求中加入appid,在請求頭中加入租戶id

import request from '@/common/utils/request'

const wechatLoginApi = {
  Login: '/extension/wx/user/'
}

export default wechatLoginApi

/**
 * 微信登錄
 * @param {Object} appId
 * @param {Object} parameter
 */
export function wechatLogin (appId, parameter) {
  return request({
    url: wechatLoginApi.Login + appId + '/login',
    method: 'get',
    params: parameter
  })
}

/**
 * 獲取微信信息
 * @param {Object} appId
 * @param {Object} parameter
 */
export function wechatInfo (appId, parameter) {
  return request({
    url: wechatLoginApi.Login + appId + '/info',
    method: 'get',
    params: parameter
  })
}

/**
 * 獲取手機號
 * @param {Object} appId
 * @param {Object} parameter
 */
export function wechatPhone (appId, parameter) {
  return request({
    url: wechatLoginApi.Login + appId + '/phone',
    method: 'get',
    params: parameter
  })
}

在修改配置時,一定需要注意權限問題,一般情況下,在不同的租戶下不允許配置相同的微信小程序,因為appid是唯一的,在發布微信小程序后,微信小程序是唯一的。當然也有特殊的情況,比如,同一個小程序作為多個租戶相同的商戶管理端,那么在此時,需要讓用戶在前端選擇輸入租戶標識以確定登錄用戶屬于哪個租戶,即多個租戶共用同一個微信小程序。

責任編輯:姜華 來源: 今日頭條
相關推薦

2023-03-26 00:00:00

2022-06-10 06:55:21

JustAuthSpring

2021-10-26 00:25:14

程序登錄流程

2021-03-04 11:50:48

微信Spring Secu登錄

2017-05-08 15:03:07

微信小程序開發實戰

2021-12-30 19:00:09

微信小程序移動應用

2016-10-20 21:02:12

微信小程序javascript

2017-01-09 10:01:49

微信小程序

2017-06-09 10:40:00

微信小程序架構分析

2016-09-28 18:10:59

微信程序MINA

2017-06-09 12:58:20

微信小程序架構分析

2016-11-04 10:31:49

微信程序指南

2017-06-09 10:06:54

微信小程序架構分析

2016-11-22 11:23:52

微信小程序騰訊微信

2016-09-27 15:40:58

微信程序前端

2016-11-04 10:49:48

微信小程序

2021-06-10 10:51:27

程序基礎架構

2016-09-27 16:38:24

JavaScript微信Web

2016-11-07 10:30:07

微信小程序安裝配置

2021-02-25 10:01:19

鴻蒙HarmonyOS應用開發
點贊
收藏

51CTO技術棧公眾號

在线观看你懂的网站| 亚洲一二三四五| 黄色网页在线看| 丁香六月综合激情| 国产国语刺激对白av不卡| 免费看一级黄色| 狼人精品一区二区三区在线| 在线精品视频小说1| 日韩video| 黄色片免费在线| 国产成人综合在线观看| 国产99久久精品一区二区 夜夜躁日日躁 | 免费在线国产精品| 91麻豆视频在线观看| 国产日韩欧美三区| 欧美成人免费视频| 国产18无套直看片| 亚州国产精品| 精品国产自在久精品国产| 青青青在线视频免费观看| 毛片在线导航| 亚洲精品欧美专区| 亚州欧美一区三区三区在线| 欧美一区二区三区激情| 蜜桃av噜噜一区| 热久久这里只有精品| 国产精品7777| 亚洲综合专区| 日韩一区二区久久久| 一级性生活毛片| 久久亚州av| 精品国精品自拍自在线| 一级网站在线观看| 久久爱.com| 日本乱人伦aⅴ精品| 熟女少妇在线视频播放| 免费网站在线观看人| 中文字幕一区二区三区蜜月| 三区精品视频观看| 狠狠色伊人亚洲综合网站l| av资源站一区| 国产精品一区在线观看| 午夜精品一区二区三| 韩国女主播成人在线| 国产一区二区丝袜高跟鞋图片| 特级做a爱片免费69| 午夜亚洲伦理| 欧美又大又硬又粗bbbbb| 国产做受高潮漫动| 亚洲一区二区三区四区五区午夜| 久久久久久久久久婷婷| 麻豆视频在线观看| 精品成人免费| 欧美日韩成人在线视频| 久一视频在线观看| 亚洲看片一区| 久久欧美在线电影| 色网站在线播放| 国产欧美不卡| 日韩男女性生活视频| 懂色av中文字幕| 日本怡春院一区二区| 国产精品久久久久福利| 亚洲天堂一二三| 国产美女在线观看一区| 99re国产视频| 天堂av中文在线资源库| 国产亚洲成av人在线观看导航| 免费精品视频一区二区三区| 精华区一区二区三区| 国产人成亚洲第一网站在线播放| 亚洲a∨一区二区三区| 日本亚洲精品| 亚洲午夜一区二区| 成人观看免费完整观看| 成人一区视频| 精品精品国产高清a毛片牛牛| 182在线视频| 欧洲grand老妇人| 精品国产一区二区三区久久| 欧美另类视频在线观看| 一二三区精品| 成人av色在线观看| 隣の若妻さん波多野结衣| 97se狠狠狠综合亚洲狠狠| 日本一区二区在线视频| 成人国产免费电影| 欧美视频二区36p| 黄色小视频免费网站| 国产精品天天看天天狠| 国产亚洲精品美女久久久久| 亚洲色图综合区| 国产一区成人| 成人福利视频在线观看| 香蕉久久一区二区三区| 国产精品久久久久影院老司| 日本一级黄视频| 欧洲精品一区二区三区| 欧美大片一区二区三区| 偷拍夫妻性生活| 欧美日韩18| 欧美中文在线观看| 国产视频第一页| 久久精品视频一区二区三区| 国产精品igao激情视频| 国产成人免费9x9x人网站视频| 日韩视频一区在线观看| 日韩女同一区二区三区 | 欧美日韩精品在线一区二区 | 国产中文字幕久久| 亚洲片区在线| 亚洲一区中文字幕| 成人全视频高清免费观看| 亚洲一区二区黄色| 国产精欧美一区二区三区白种人| 欧美自拍一区| 欧美大片免费观看在线观看网站推荐| 午夜影院免费在线观看| 国产.欧美.日韩| 日本丰满少妇黄大片在线观看| 欧美午夜性生活| 韩国女主播一区二区| 欧美成人精品二区三区99精品| 人与嘼交av免费| 中文亚洲欧美| 国产精品对白刺激久久久| 一级日本在线| 欧美性猛片xxxx免费看久爱| 久久一区二区电影| 尹人成人综合网| 99超碰麻豆| av网址在线免费观看| 欧美专区日韩专区| 中文字幕av网址| 亚洲欧美日韩国产综合精品二区| 国产精品一区二区免费看| 国产素人视频在线观看| 欧美日韩精品一区二区三区| 亚洲自拍偷拍图| 久久高清一区| 欧美一区免费视频| 欧美日韩123区| 亚洲片av在线| 亚洲色成人www永久网站| 91麻豆免费视频| 91视频最新入口| 亚洲视频分类| 国产精品久久久久久中文字| 电影在线高清| 欧美日韩一区不卡| 三级黄色录像视频| 激情欧美日韩一区二区| 在线精品日韩| 日韩影片在线观看| 欧美高清自拍一区| 全部免费毛片在线播放一个| 亚洲一区二区三区精品在线| 国产a级黄色片| 久久免费黄色| 亚洲欧美丝袜| 精品久久国产一区| 高清欧美一区二区三区| 日本黄色一区二区三区| 色综合久久综合网97色综合| 日韩在线免费观看av| 日本v片在线高清不卡在线观看| 日本一区二区三区精品视频| 日韩欧国产精品一区综合无码| 久久久国产精品亚洲一区| 国产视频在线观看免费| 亚洲综合色视频| avtt香蕉久久| 麻豆国产欧美日韩综合精品二区| 日本女人高潮视频| 韩国精品福利一区二区三区| 国产成人黄色av| 好了av在线| 亚洲激情小视频| 亚洲精品91天天久久人人| 亚洲视频在线一区观看| 精人妻一区二区三区| 美女尤物久久精品| 中文视频一区视频二区视频三区| 亚洲一区二区三区免费| 欧美一区二区三区图| 欧美黑人激情| 亚洲娇小xxxx欧美娇小| 中文字幕二区三区| 亚洲伊人色欲综合网| 免费黄色在线视频| 国产乱码精品一区二区三区av | 91人人爽人人爽人人精88v| 久久免费电影| 正在播放欧美视频| 国产 欧美 自拍| 欧美日韩精品综合在线| 国产精品美女毛片真酒店| 国产精品丝袜黑色高跟| 女性生殖扒开酷刑vk| 奇米777欧美一区二区| 日韩精品一区在线视频| 国产高清欧美| 欧美日韩在线一二三| 日本在线一区二区三区| 国产aaa精品| 91av久久| 欧美大片在线免费观看| 91看片在线观看| 日韩精品中文字幕在线播放| 国产免费av观看| 日本电影亚洲天堂一区| 国产亚洲精品久久777777| 国产精品国产精品国产专区不蜜| 国产精品久久不卡| 成人综合在线视频| 182午夜视频| 久久国产尿小便嘘嘘| 久章草在线视频| 99精品视频免费全部在线| 欧美a级免费视频| 91精品综合久久久久久久久久久 | 99久久99| 99热这里有精品| 国产精品成人免费视频| 日韩在线伦理| 久久久久亚洲精品成人网小说| 麻豆视频在线观看免费| 中文字幕亚洲欧美日韩高清| 九色在线观看视频| 亚洲视频在线观看免费| 欧美视频免费一区二区三区| 亚洲精品mp4| 天天操天天射天天舔| 欧美精品一区二区三区视频| www.久久成人| 日韩欧美国产综合一区 | 国产欧美日韩另类| 亚洲成人1区2区| 日本三级理论片| 性久久久久久久久| 日本熟妇成熟毛茸茸| 亚洲大型综合色站| 亚洲男人的天堂在线视频| 亚洲超碰97人人做人人爱| 久久精品欧美一区二区| 亚洲综合色噜噜狠狠| 国产第一页在线播放| 亚洲不卡一区二区三区| 亚洲男人的天堂在线视频| 欧美日韩性生活视频| 国产精品21p| 日本福利一区二区| 亚洲中文字幕在线观看| 欧美日韩国产成人在线免费| 国产一区二区在线视频观看| 欧美日韩一区国产| 国产美女主播在线观看| 欧美一级一级性生活免费录像| 99久久国产热无码精品免费| 欧美一区二区免费| 国精产品乱码一区一区三区四区| 日韩欧美一卡二卡| 可以免费观看的毛片| 日韩av在线最新| 狠狠v欧美ⅴ日韩v亚洲v大胸| 国产亚洲一区二区精品| 日本黄色片在线观看| 久久av中文字幕| 俺来俺也去www色在线观看| 97国产成人精品视频| 色婷婷综合久久久中字幕精品久久| 国产精品极品美女粉嫩高清在线| 另类一区二区| 99久久精品免费看国产一区二区三区 | 欧美tk—视频vk| 人成在线免费视频| 色噜噜久久综合伊人一本| 成视频免费观看在线看| 久久免费国产视频| 久久91导航| 97久久天天综合色天天综合色hd| 极品尤物一区| 亚洲国产精品一区二区第一页| 久久久久电影| 久久精品国产精品亚洲色婷婷| 日韩av在线免费观看不卡| 韩国三级与黑人| 久久先锋影音av| 成人免费视频国产免费观看| 亚洲成人免费观看| 一区二区国产欧美| 亚洲精品videossex少妇| 婷婷激情在线| 91av在线网站| 国产不卡精品在线| 欧美另类视频在线| 一区二区中文字| 中文字幕无码不卡免费视频| 国产另类ts人妖一区二区| 扒开jk护士狂揉免费| 亚洲综合色噜噜狠狠| 精品无码一区二区三区的天堂| 在线播放中文一区| 可以在线观看的av网站| 欧美人与性动交a欧美精品| 日本精品网站| 精品国产一区二区三| 亚洲一区二区| 午夜精品在线免费观看| aaa国产一区| 九九久久免费视频| 欧美日韩激情一区| 国产中文字幕在线播放| 久久久噜噜噜久久| 精品视频一区二区三区| 先锋影音亚洲资源| 美女久久网站| 五十路六十路七十路熟婆| 亚洲精品乱码久久久久久日本蜜臀| 伊人久久久久久久久久久久| 精品日本一线二线三线不卡| 色综合久久影院| 国产999视频| 免费成人高清在线视频theav| 屁屁影院ccyy国产第一页| 精品一区二区三区欧美| 精品人妻无码一区二区三区换脸| 黄色成人av在线| 韩国av免费在线| 欧美激情在线观看视频| 国产精品美女久久久久| 亚洲欧美一区二区原创| 日韩精品一二三| 中文字幕成人动漫| 91激情在线视频| 九色国产在线观看| 日本电影亚洲天堂| 精品一区亚洲| 黑鬼大战白妞高潮喷白浆| 91在线视频在线| 五月天婷婷综合网| 亚洲国内精品视频| 99re6在线精品视频免费播放| 成人午夜电影免费在线观看| 欧美深夜福利| www.美色吧.com| 欧美日韩国产影院| 激情福利在线| 国产精品青青在线观看爽香蕉 | 亚洲精品一区在线| 国产免费裸体视频| 成人免费毛片嘿嘿连载视频| 久久久久久久久久久久久久免费看 | 欧美日韩ab片| 精品成人自拍视频| 免费成人午夜视频| 久久久久久久久岛国免费| 亚洲精品成人在线视频| 亚洲最大在线视频| 国外成人福利视频| av中文字幕av| 99久久夜色精品国产网站| 国产精品男女视频| 夜夜嗨av色一区二区不卡| 亚洲最大的免费视频网站| 欧美美女黄色网| aa级大片欧美| 伊人网综合在线| 欧美成人免费视频| 日韩欧美中文字幕电影| 精品久久久久久中文字幕2017| 中文字幕欧美一| 动漫av一区二区三区| 欧美在线激情网| 欧美国产美女| jjzz黄色片| 在线观看亚洲成人| a视频在线观看| 就去色蜜桃综合| 精品一区二区三区免费| 精品亚洲永久免费| 亚洲天堂日韩电影| 精品国产一区二区三区2021| 久久综合九色综合88i| 中文av一区特黄| 女人18毛片水真多18精品| 国产精品白丝jk喷水视频一区 | 中文字幕一区在线| 天堂中文网在线| 国产综合视频在线观看| 99精品99| 一起操在线播放| 亚洲精品之草原avav久久| 蜜桃精品视频| 免费在线观看的毛片| 亚洲精品一二三| 大胆av不用播放器在线播放| 成人区精品一区二区| 免费观看在线综合色| 久久精品视频6|