SpringCloud微服务实战——搭建企业级开发框架(五十三):微信小程序授权登录增加多租户可配置界面

这篇具有很好参考价值的文章主要介绍了SpringCloud微服务实战——搭建企业级开发框架(五十三):微信小程序授权登录增加多租户可配置界面。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  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是唯一的,在发布微信小程序后,微信小程序是唯一的。当然也有特殊的情况,比如,同一个小程序作为多个租户相同的商户管理端,那么在此时,需要让用户在前端选择输入租户标识以确定登录用户属于那个租户,即多个租户共用同一个微信小程序。

GitEgg-Cloud是一款基于SpringCloud整合搭建的企业级微服务应用开发框架,开源项目地址:

Gitee: https://gitee.com/wmz1930/GitEgg

GitHub: https://github.com/wmz1930/GitEgg文章来源地址https://www.toymoban.com/news/detail-617413.html

到了这里,关于SpringCloud微服务实战——搭建企业级开发框架(五十三):微信小程序授权登录增加多租户可配置界面的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 毕业设计So Easy:SpringCloud Hadoop Vue实现企业级网盘系统

    目录 1、项目背景 2、项目功能 3、项目机制 4、功能模块结构 5、数据库设计 6、项目实现 6.1、前台主要功能模块 6.2、后台主要功能模块 7、项目效果 7.1、主界面 7.2、后台界面 7.3、菜单管理界面 很多计算机专业大学生经常和我交流:毕业设计没思路、不会做、论文不会写、

    2024年02月13日
    浏览(59)
  • 毕业设计项目 基于SpringCloud+Hadoop+Vue的企业级网盘系统设计与实现

    基于SpringCloud+Hadoop+Vue的企业级网盘系统设计与实现 提示:适合用于课程设计或毕业设计,工作量达标,源码开放 前端:vue-projectManage 后台:mycloud-admin 提供前端服务:mycloud 文件在线预览服务:file-online-preview 编程语言:Java、Mybatis、Spring、SpringBoot、SpringCloud、Node、Vue 开发环

    2024年04月24日
    浏览(55)
  • java项目分享 - 基于SpringCloud+Hadoop+Vue的企业级网盘系统设计与实现

    基于SpringCloud+Hadoop+Vue的企业级网盘系统设计与实现 提示:适合用于课程设计或毕业设计,工作量达标,源码开放 前端:vue-projectManage 后台:mycloud-admin 提供前端服务:mycloud 文件在线预览服务:file-online-preview 编程语言:Java、Mybatis、Spring、SpringBoot、SpringCloud、Node、Vue 开发环

    2024年02月05日
    浏览(56)
  • 计算机毕设分享 基于SpringCloud+Hadoop+Vue的企业级网盘系统设计与实现

    基于SpringCloud+Hadoop+Vue的企业级网盘系统设计与实现 提示:适合用于课程设计或毕业设计,工作量达标,源码开放 前端:vue-projectManage 后台:mycloud-admin 提供前端服务:mycloud 文件在线预览服务:file-online-preview 编程语言:Java、Mybatis、Spring、SpringBoot、SpringCloud、Node、Vue 开发环

    2024年02月20日
    浏览(88)
  • Docker(四)搭建harbor企业级仓库

    虽然Docker官方也提供了公共的镜像仓库,但是从安全和效率等方面考虑,部署我们私有环境的Registry也是非常必要的。 所以Harbor孕育而生,Harbor是由VMware公司开源的企业级的Docker Registry管理项目,它包括权限管理(RBAC)、LDAP、日志审核、管理界面、自我注册、镜像复制和中文支

    2024年02月04日
    浏览(69)
  • 阿里云大数据——搭建企业级数据分析平台

    目录 1、数据分析介绍 1.1数据分析基本介绍 1.2数据分析目的 1.3数据分析平台组成部分 1.4数据分析平台对应的场景 2、阿里云数加介绍 2.1数加是什么? 2.2“数加”是阿里云大数据的核心能力 2.3数加平台发展历程 3、常用的数加产品 3.1数加产品框架图 3.2数据分析平台常用的数

    2023年04月11日
    浏览(92)
  • Git---企业级开发模型

    我们知道,一个软件从零开始到最终交付,大概包括一下几个阶段 : 规划、编码、构建、测试、发布、部署和维护. 最初程序比较简单,工作量也不大.程序猿一个人可以完成所有阶段的工作.但随着软件产业的日益发展壮大,软件的规模也在逐渐变得庞大.软件的复杂度不断攀升,一个

    2024年02月13日
    浏览(50)
  • CentOS7搭建Harbor企业级Docker仓库

    前言 Harbor是一个企业级的Docker Registry管理项目,主要用于存储和分发Docker镜像。它提供了一些企业必需的功能特性,如安全、标识和管理等,以扩展开源Docker Distribution。 作为企业级私有Registry服务器,Harbor提供了更好的性能和安全,并提升了用户使用Registry构建和运行环境传

    2024年01月22日
    浏览(45)
  • 拿来即用的企业级安全运维体系搭建指南

    **作者介绍****林伟壕,**SecDevOpsor,先后在中国电信和网易游戏从事数据网络、网络安全和游戏运维工作。对Linux运维、虚拟化和网络安全防护等研究颇多,目前专注于网络安全自动化检测、防御系统构建。 在上篇《99%的人会中招的运维安全陋习,请规避!》中,我们花了很大

    2024年02月03日
    浏览(52)
  • 【云原生-Harbor】企业级搭建镜像仓库Harbor最佳教程

    Harbor 是由 VMware 公司中国团队为企业用户设计的 Registry server 开源项目,包括了权限管理 (RBAC)、LDAP、审计、管理界面、自我注册、HA 等企业必需的功能,同时针对中国用户的特点,设计镜像复制和中文支持等功能。 官网:https://goharbor.io/ 开源地址:https://github.com/goharbor/har

    2024年02月02日
    浏览(72)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包