如何实现H5端对接钉钉登录并优雅扩展其他平台

如何实现H5端对接钉钉登录并优雅扩展其他平台

    • 钉钉H5登录逻辑
    • 后端代码如何实现?
    • 本次采用策略模式+工厂方式进行
      • 定义接口确定会使用的基本鉴权步骤
      • 具体逻辑类进行实现
      • 采用注册表模式(Registry Pattern)
      • 抽象工厂进行基本逻辑定义
      • 具体工厂进行对接口中的逻辑步骤具体----实例化----逻辑进行重写
    • 总结

钉钉H5登录逻辑

下图中需要说明的一点是,准确来说步骤3来说是钉钉API返回给前端,前端携带一次性校验码token给后端进行后续的鉴权。
还有一点需要注意获得权限之后,如果前端需要回调接口获取用户信息,则需要增加上下文中的用户信息存储

image-20250407212849539

后端代码如何实现?

具体的伪代码如下所述,下面细聊一下如何进行实现获取用户信息这一步。其中本次采用了设计模式进行实现。

    public Result<LoginResp> h5Login(LoginH5UserReq loginH5UserReq) throws ApiException {// 获取租户信息xxxxx// 查询三方鉴权配置信息xxxxx// 获取用户信息 这一步很关键后面细说如何实现H5AuthHandler H5AuthHandler = H5AuthHandlerRegistry.createHandler(loginH5UserReq.getTypePlatForm());String userUniqueIdentifier = H5AuthHandler.getUserDetail(loginH5UserReq);// 系统校验根据手机号查询用户信息SysUser sysUser = sysUserMapper.selectOne(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getTel, userUniqueIdentifier), false);// 使用断言进行优雅校验Assert.notNull(sysUser, () -> new BizException(ErrorCodeEnum.NOT_AVAILABLE));// 校验通过下发tokenString accessToken = StpUtil.getTokenInfo().getTokenValue();xxxxreturn Result.success(loginResult);}

本次我的思路是实现针对不同平台,例如对接钉钉、企业微信、飞书、三方,具体的逻辑是不一样的,使用设计模式中的工厂模式进行构建,实现不同的逻辑进行创建不同类进行完成。

简单罗列一下可以采用的设计模式的具体之间的区别

image-20250407214413483

本次采用策略模式+工厂方式进行

定义接口确定会使用的基本鉴权步骤

public interface AuthHandler {// 获取访问令牌(需处理OAuth2 code校验)String getAccessToken(String code) throws AuthException;// 使用令牌换取用户唯一标识(需处理令牌失效场景)String getUserId(String token) throws AuthException;// 获取用户详细信息(需处理多层级JSON解析)UserDetail getUserDetail(String userId) throws AuthException;
}

具体逻辑类进行实现

下面代码是大致思路展示,直接run是会出现问题。涉及公司保密协议不可以直接上我的源码望读者朋友见谅~

public class DingTalkAuthHandler implements AuthHandler {private static final String API_HOST = "https://oapi.dingtalk.com";private final String appKey;private final String appSecret;// 依赖配置注入(参考网页6的钉钉配置)public DingTalkAuthHandler(String appKey, String appSecret) {this.appKey = appKey;this.appSecret = appSecret;}@Overridepublic String getAccessToken(String code) {// 构建认证请求参数(参考网页7的code交换逻辑)Map<String, String> params = new HashMap<>();params.put("appkey", appKey);params.put("appsecret", appSecret);// 调用钉钉API(网页6的接口文档)String url = API_HOST + "/gettoken?" + buildQueryString(params);JsonNode response = HttpUtil.get(url);// 错误码校验(参考网页6的errcode处理)if(response.get("errcode").asInt() != 0) {throw new DingTalkAuthException(response.get("errmsg").asText());}return response.get("access_token").asText();}@Overridepublic String getUserId(String token) {// 安全域名验证(参考网页7的domain校验)String url = API_HOST + "/user/getuserinfo?access_token=" + token;JsonNode userInfo = HttpUtil.get(url);return userInfo.get("userid").asText();}@Overridepublic UserDetail getUserDetail(String userId) {// 多层级数据解析(参考网页6的JSON结构)String url = API_HOST + "/user/get?userid=" + userId;JsonNode data = HttpUtil.get(url).get("result");return UserDetail.builder().mobile(data.at("/mobile").asText()) // JSONPath定位.name(data.get("name").asText()).avatar(data.get("avatar").asText()).build();}// 私有方法封装请求构建private String buildQueryString(Map<String, String> params) {return params.entrySet().stream().map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8)).collect(Collectors.joining("&"));}
}

上述代码通过接口 + 实现类的方式进行大致逻辑的定义,具体逻辑的展开,不是本次的重点,主要想记录一下如何实现下述的调用:

// 需要实现根据loginH5UserReq.getTypePlatForm() 传入不同的类型,实现实例化对应的实体类进行处理对应逻辑
H5AuthHandler H5AuthHandler = H5AuthHandlerRegistry.createHandler(loginH5UserReq.getTypePlatForm());
// 得到具体逻辑类之后根据请求信息返回用户唯一的id进行后续鉴权
String userUniqueIdentifier = H5AuthHandler.getUserDetail(loginH5UserReq);

采用注册表模式(Registry Pattern)

集中管理平台与工厂映射关系,提供统一访问入口

@Component
// 为什么要采用ApplicationContextAware?文末解释
public class H5AuthHandlerRegistry implements ApplicationContextAware {private static final Map<String, H5AuthHandlerFactory<?>> REGISTRY = new ConcurrentHashMap<>();private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext context) {applicationContext = context;// Spring容器初始化完成后动态注册平台registerPlatforms();}// 具体平台注册private void registerPlatforms() {// 钉钉平台注册(依赖注入已生效)H5DingTalkAuthFactory dingTalkFactory = new H5DingTalkAuthFactory(applicationContext);REGISTRY.put(Platforms.DING_TALK.name(), dingTalkFactory);// 其他平台注册xxxxxxx}// 获取处理器工厂public static H5AuthHandlerFactory<?> getFactory(String platform) {return Optional.ofNullable(REGISTRY.get(platform)).orElseThrow(() -> new IllegalArgumentException("未注册的平台: " + platform));}// 全局同意访问入口public static H5AuthHandler createHandler(String platform) {return getFactory(platform).createHandler();}
}

抽象工厂进行基本逻辑定义

为什么这里要使用抽象类?

首先我想定义基本的创建逻辑,其次抽象类不能被实例化。还有抽象类一般用于设计模式中一种通用写法规范,为子类提供公共的代码实现(如非抽象方法)和强制约束(如抽象方法),子类继承并实现所有抽象方法后才能实例化。

public abstract class H5AuthHandlerFactory<T extends H5AuthHandler> {private final Class<T> handlerClass;protected H5AuthHandlerFactory(Class<T> handlerClass) {this.handlerClass = handlerClass;}// 定义基本创建逻辑,采用反射方式进行。支持反射创建(需无参构造)// PS:如果具体进行逻辑类不涉及采用spring容器管理类,可以使用直接newInstance。不然会出现创建失败,spring容器ioc和Java创建对象是割裂的两派public T createHandler() {try {return handlerClass.getDeclaredConstructor().newInstance();} catch (Exception e) {throw new RuntimeException("H5端登录逻辑抽象工厂---H5AuthHandlerFactory---处理器实例化失败", e);}}}

具体工厂进行对接口中的逻辑步骤具体----实例化----逻辑进行重写

public class H5DingTalkAuthFactory extends H5AuthHandlerFactory<H5DingTalkAuthHandler> {private final ApplicationContext context;// 这里是因为具体实例化处理钉钉H5登录逻辑类会使用到spring容器中的类,所以需要采用上下文的方式public H5DingTalkAuthFactory(ApplicationContext context) {super(H5DingTalkAuthHandler.class);this.context = context;}@Overridepublic H5DingTalkAuthHandler createHandler() {// 从Spring容器获取依赖项ThreePartyLoginRuleConfig ruleConfig = context.getBean(ThreePartyLoginRuleConfig.class);ObjectMapper objectMapper = context.getBean(ObjectMapper.class);// 通过构造器注入依赖return new H5DingTalkAuthHandler(ruleConfig, objectMapper);}
}

总结

总体来说,要实现其他平台的扩展。本次的使用中,由于对接不同平台,具体逻辑中涉及了配置文件配置不同平台JSON数据的解析,所以会使用sping中IOC功能,所以在工厂类中存在上下文部分。

扩展其他平台部分就需要创建两个类,一个类是集成抽象工厂实现其中的createHandler()方法,还有一个是实现接口中定义的三部曲。

H5xxxxxxxAuthFactory extends H5AuthHandlerFactory
H5xxxxxxxxAuthHandler implements H5AuthHandler

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

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

相关文章

STM32F103C8T6单片机开发:简单说说单片机的外部GPIO中断(标准库)

目录 前言 如何使用STM32F1系列的标准库完成外部中断的抽象 初始化我们的GPIO为输入的一个模式 初识GPIO复用&#xff0c;开启GPIO的复用功能时钟 GPIO_EXTILineConfig和EXTI_Init配置外部中断参数 插入一个小知识——如何正确的配置结构体&#xff1f; 初始化中断&#…

【自然语言处理】深度学习中文本分类实现

文本分类是NLP中最基础也是应用最广泛的任务之一&#xff0c;从无用的邮件过滤到情感分析&#xff0c;从新闻分类到智能客服&#xff0c;都离不开高效准确的文本分类技术。本文将带您全面了解文本分类的技术演进&#xff0c;从传统机器学习到深度学习&#xff0c;手把手实现一套…

Java Lambda与方法引用:函数式编程的颠覆性实践

在Java 8引入Lambda表达式和方法引用后&#xff0c;函数式编程范式彻底改变了Java开发者的编码习惯。本文将通过实战案例和深度性能分析&#xff0c;揭示如何在新项目中优雅运用这些特性&#xff0c;同时提供传统代码与函数式代码的对比优化方案。 文章目录 一、Lambda表达式&a…

剑指offer经典题目(三)

目录 动态规划入门 二进制运算 链表相关 动态规划入门 题目1&#xff1a;一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法&#xff08;先后次序不同算 不同的结果&#xff09;。OJ地址 简单图示如下。 题目分析&#…

【每日随笔】丛林法则 ( 弱肉强食 | 适者生存 | 资源有限稀缺 | 没有道德约束 | 自发性与无序性 | 丛林法则映射 - 资源分配 与 社会分层 )

文章目录 一、丛林法则1、弱肉强食2、适者生存3、资源有限稀缺4、没有道德约束5、自发性与无序性6、丛林法则映射 - 资源分配 与 社会分层 一、丛林法则 丛林法则 是 在 资源有限 的环境中 , 竞争 是生存的基础 , 弱肉强食 , 适者生存 , 且过程 不受道德约束 ; 丛林法则 在 自…

【含文档+PPT+源码】基于小程序的智能停车管理系统设计与开发

项目视频介绍&#xff1a; 毕业作品基于小程序的智能停车管理系统设计与开发 课程简介&#xff1a; 本课程演示的是一款基于小程序的智能停车管理系统设计与开发&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;…

Navicat连接远程PostGreSQL失败

问题描述 使用本地Navicat连接Windows远程服务器上部署的PostGreSQL数据库时,出现以下错误: 解决方案 出现以上报错信息,是因为PostGreSQL数据库服务尚未设置允许客户端建立远程连接。可做如下配置, 1. 找到PostGreSQL数据库安装目录下的data子文件夹,重点关注:postgres…

【Linux】jumpserver开源堡垒机部署

JumpServer 安装部署指南 本文档详细记录了 JumpServer 安装部署的过程、核心脚本功能说明以及后续管理使用提示&#xff0c;方便运维人员快速查阅和二次安装。 1. 前提条件 操作系统要求&#xff1a; 仅支持 Linux 系统&#xff0c;不支持 Darwin&#xff08;macOS&#xff0…

餐饮厨房开源监控安全系统的智能革命

面对日益严格的合规要求和消费者对卫生的信任危机&#xff0c;传统人工监督已力不从心&#xff1a;卫生死角难发现、违规操作难追溯、安全隐患防不胜防。如何让后厨更透明、更安全、更可信&#xff1f;餐饮厨房视频安全系统横空出世&#xff01;这套系统融合实时监控与AI技术&a…

HashMap为什么扩容为原来2倍呢?

1、减少哈希碰撞 核心原因&#xff1a;HashMap的所有设计都依赖于数组长度为2的幂次方这一前提。 索引计算使用 &#xff08;n-1)&hash &#xff0c;其中 n 是数组长度当 n 是 2 的幂次方时&#xff0c;n-1 的二进制形式是全 1&#xff08;例如&#xff0c;15——>111…

debian系统中文输入法失效解决

在 Debian 9.6 上无法切换中文输入法的问题通常与输入法框架&#xff08;如 Fcitx 或 IBus&#xff09;的配置或依赖缺失有关。以下是详细的解决步骤&#xff1a; 1. 安装中文语言包 确保系统已安装中文语言支持&#xff1a; sudo apt update sudo apt install locales sudo…

3DGS之光栅化

光栅化&#xff08;Rasterization&#xff09;是计算机图形学中将连续的几何图形&#xff08;如三角形、直线等&#xff09;转换为离散像素的过程&#xff0c;最终在屏幕上形成图像。 一、光栅化的核心比喻 像画家在画布上作画 假设你是一个画家&#xff0c;要把一个3D立方体画…

学习51单片机Day02---实验:点亮一个LED灯

目录 1.先看原理图 2.思考一下&#xff08;sbit的使用&#xff09;&#xff1a; 3.给0是要让这个LED亮&#xff08;LED端口设置为低电平&#xff09; 4.完成的代码 1.先看原理图 比如我们要让LED3亮起来&#xff0c;对应的是P2^2。 2.思考一下&#xff08;sbit的使用&…

Redis与Lua原子操作深度解析及案例分析

一、Redis原子操作概述 Redis作为高性能的键值存储系统&#xff0c;其原子性操作是保证数据一致性的核心机制。在Redis中&#xff0c;原子性指的是一个操作要么完全执行&#xff0c;要么完全不执行&#xff0c;不会出现部分执行的情况。 Redis原子性的实现原理 单线程模型&a…

深入理解 GLOG_minloglevel 与 GLOG_v:原理与使用示例

文章目录 深入理解 GLOG_minloglevel 与 GLOG_v&#xff1a;原理与使用示例1. GLOG_minloglevel&#xff1a;最低日志等级控制2. GLOG_v&#xff1a;控制 VLOG() 的详细输出等级3. GLOG_minloglevel 与 GLOG_v 的优先级关系4. 使用示例4.1 基础示例&#xff1a;不同日志等级4.2…

Cline Memory Bank 结构化文档持久化 AI 上下文详解

&#x1f3ae; 什么是 Cline Memory Bank&#xff1f; Memory Bank 是一个结构化文档系统&#xff0c;允许 Cline 在会话之间保持上下文。它能让 Cline 从无状态的助手转变为持久记忆的开发伙伴&#xff0c;随着时间推移有效地“记住”项目细节。 &#x1f5e1;️ 关键优势 上…

【JavaScript】面向对象与设计模式

个人主页&#xff1a;Guiat 归属专栏&#xff1a;HTML CSS JavaScript 文章目录 1. JavaScript 中的面向对象编程1.1 对象基础1.2 构造函数1.3 原型和原型链1.4 ES6 类1.5 继承1.6 封装 2. 创建型设计模式2.1 工厂模式2.2 单例模式2.3 建造者模式2.4 原型模式 3. 结构型设计模式…

网络安全防护技术

边界安全防护——防火墙 控制&#xff1a;在网络连接点上建立一个安全控制点&#xff0c;对进出数据进行限制隔离&#xff1a;将需要保护的网络与不可信任网络进行隔离&#xff0c;隐藏信息并进行安全防护记录&#xff1a;对进出数据进行检查&#xff0c;记录相关信息 防火墙…

Spring MVC 视图解析器(JSP、Thymeleaf、Freemarker、 JSON/HTML、Bean)详解

Spring MVC 视图解析器详解 1. 视图解析器概述 视图解析器&#xff08;ViewResolver&#xff09;是 Spring MVC 的核心组件&#xff0c;负责将控制器返回的视图名称&#xff08;如 success&#xff09;转换为具体的 View 对象&#xff08;如 Thymeleaf 模板或 JSP 文件&#x…

# 爬虫技术的实现

手把手教你网络爬虫&#xff1a;从入门到实践 一、网络爬虫简介 网络爬虫&#xff08;Web Crawler&#xff09;是一种自动化获取互联网数据的程序&#xff0c;广泛应用于搜索引擎、数据分析、市场调研等领域。通过模拟浏览器行为&#xff0c;爬虫可以高效地从网页中提取结构化…