文章目录

  • 一、背景
  • 二、详细设计
    • 1、UML设计
    • 2、程序设计
      • 2.1、AuthenticateActionEnum
      • 2.2、AuthenticateDispatcher
      • 2.3、BaseAuthenticateContext`<Request>`
        • 2.3.1、ActivityStatusChangeAuthenticateContext
        • 2.3.2、VodEventNotifyAuthenticateContext
      • 2.4、AbstractAuthenticateHandler
        • 2.4.1、ActivityStatusChangeAuthenticateHandler
        • 2.4.2、VodEventNotifyAuthenticateHandler
      • 2.5、AuthenticateConfig
      • 2.6、VolcAuthenticateApolloConfig
      • 2.7、yml配置
      • 2.8、业务接入

通过系统应用服务总会与三方服务商进行对接,既然有对接,就会有回调。但是此应用服务由于部署在公网访问,为了考虑系统安全系以及防止报文被篡改,这就意味着我们需要跟三方服务商进行鉴权技术方案设计。此文章,就是一个具体典型的案例,由于此应用服务有两个不同的场景,但是鉴权设计上又有不同差异之处,所以在总体程序设计上巧妙的满足场景的需求前提下,又能尽可能做到更好的扩展维护。

一、背景

此次涉及到对接三方的两个不同场景,暂且定位场景1和场景2。场景1的鉴权方案就是通过http接口回调,在请求头+请求报文上做鉴权处理,具体鉴权机制:请求头中的签名=md5(base64(报文)+回调url+私钥+时间戳)。
而场景2的鉴权就是在请求报文中增加鉴权字段,该鉴权字段=md5(秘钥+字段1+字段2+字段3+…)。
总而言之,都是通过md5加密,只不过加密的数据步骤有些区别。
为了考虑减少代码的耦合度,同时尽可能提高后续的扩展性,在程序设计上引入了设计模式。

二、详细设计

1、UML设计


从上图可以看出,依然采用定义一个上下文对象BaseAuthenticateContext<Request>,该类定义一个泛型,意味着需要子类来继承,并指定请求参数类。通过AbstractAuthenticateHandler它来封装鉴权的共性逻辑,比如鉴权流程,以及相关复用的代码。相关子类来继承它,实现相关抽象方法即可。AuthenticateDispatcher这个类来对外暴露,外部调用无需晓得具体使用哪个Handler来处理,还需要委托给它即可。

2、程序设计

2.1、AuthenticateActionEnum

定义一个枚举,来维护所有的鉴权场景类型,这里把场景抽象成Action

/*** 鉴权活动枚举类型** @author : Sieg Heil* @since 2022/11/25 10:30 AM*/
@Getter
@ToString
public enum AuthenticateActionEnum {/*** 企业直播活动变更*/ACTIVITY_STATUS_CHANGE("企业直播活动变更"),/*** 视频点播事件通知*/VOD_EVENT_NOTIFY("视频点播事件通知");/*** 构造函数** @param desc 描述*/AuthenticateActionEnum(String desc) {this.desc = desc;}/*** 描述*/private final String desc;
}

2.2、AuthenticateDispatcher

通过@Autowired这个注解,把AbstractAuthenticateHandler的子类集合自动装配,作为该类的一个成员。同时,提供一个分发的方法。

/*** 鉴权处理分发器** @author : Sieg Heil* @since 2022/11/25 10:28 AM*/
@Component
public class AuthenticateDispatcher {@Autowiredprivate List<AbstractAuthenticateHandler> handlerList;/*** 执行处理** @param context 上下文对象*/public void execute(BaseAuthenticateContext context) {handlerList.stream().filter(handler -> handler.getAction() == context.getAction()).forEach(handler -> handler.execute(context));}
}

2.3、BaseAuthenticateContext<Request>

定义一个上下文类。该类,包含一个内部静态类Response,并作为它的成员属性,来封装鉴权执行结果。

/*** 回调鉴权上下文对象** @author : Sieg Heil* @since 2022/11/25 10:08 AM*/
@ToString
@Getter
@Setter
public abstract class BaseAuthenticateContext<Request> {/*** 活动类型*/private AuthenticateActionEnum action;/*** 请求参数*/private Request request;/*** 响应结果*/private Response response;@ToString@Getter@Setterpublic static class Response {/*** 静态变量*/public static String SUCCESS = "鉴权成功";/*** 鉴权是否成功*/private boolean success;/*** 鉴权结果*/private String result;/*** 静态方法** @param result 鉴权结果* @return 响应对象*/public static Response buildSuccess(String result) {Response response = new Response();response.setResult(result);response.setSuccess(Boolean.TRUE);return response;}/*** 静态方法** @param result 鉴权结果* @return 响应对象*/public static Response buildFailure(String result) {Response response = new Response();response.setResult(result);response.setSuccess(Boolean.FALSE);return response;}}
}

2.3.1、ActivityStatusChangeAuthenticateContext

具体的一个场景子类

/*** [企业直播活动变更]回调鉴权上下文对象** @author : Sieg Heil* @since 2022/11/25 10:08 AM*/
@ToString(callSuper = true)
@Getter
@Setter
public class ActivityStatusChangeAuthenticateContext extends BaseAuthenticateContext<SubscribeLiveActivityStatusChangeRequest> {}

2.3.2、VodEventNotifyAuthenticateContext

具体的一个场景子类

/*** [视频点播事件通知]回调鉴权上下文对象** @author : Sieg Heil* @since 2022/11/25 10:08 AM*/
@ToString(callSuper = true)
@Getter
@Setter
public class VodEventNotifyAuthenticateContext extends BaseAuthenticateContext<VolcVodRequestContext> {}

2.4、AbstractAuthenticateHandler

鉴权处理类的基类,外部暴露的公共方法为public void execute(Context context)。该方法内部封装了具体鉴权的相关步骤,相关子类只需要实现相关抽象方法即可。

三个重要抽象方法:

  • abstract String getTraceId(Context context) :用于获取请求的traceId,便于日志打印,后续方便追踪问题。
  • abstract void doExecute(Context context):用于做具体的鉴权执行逻辑
  • abstract AuthenticateConfig getConfig():获取处理类场景的鉴权配置,该配置可以通过yml配置文件或者apollo实现,管理维护相关鉴权配置参数。
/*** 抽象鉴权处理器** @author : Sieg Heil* @since 2022/11/25 10:14 AM*/
@Slf4j
public abstract class AbstractAuthenticateHandler<Context extends BaseAuthenticateContext> implements LoggerService {@Autowiredprotected VolcAuthenticateApolloConfig volcAuthenticateApolloConfig;@PostConstructvoid init() {getLog().info("AuthenticateApolloConfig={}", JsonUtils.toJson(volcAuthenticateApolloConfig));}/*** 鉴权活动类型*/protected AuthenticateActionEnum action;/*** 活动名称*/protected String actionName;/*** 构造函数** @param action 活动类型*/public AbstractAuthenticateHandler(AuthenticateActionEnum action) {this.action = action;if (Objects.nonNull(action)) {this.actionName = action.name();}}/*** 对外部方法** @param context*/public void execute(Context context) {String traceId = getTraceId(context);if (logDebug()) {getLog().info("[{}|{}],context={}", traceId, actionName, JsonUtils.toJson(context));}AuthenticateConfig config = getConfig();if (null == config) {context.setResponse(BaseAuthenticateContext.Response.buildSuccess(SUCCESS));return;}boolean enableSwitch = Optional.ofNullable(config.getEnableSwitch()).orElse(Boolean.FALSE);//如果没有开启鉴权,则不执行鉴权if (!enableSwitch) {context.setResponse(BaseAuthenticateContext.Response.buildSuccess(SUCCESS));return;}doExecute(context);BaseAuthenticateContext.Response response = context.getResponse();getLog().info("[{}|{}]{}", traceId, actionName, JsonUtils.toJson(response));if (!response.isSuccess()) {throw new ForbiddenException("鉴权失败[" + response.getResult() + "]", response.getResult());}}@Overridepublic boolean logDebug() {Boolean enableLogDebug = volcAuthenticateApolloConfig.getEnableLogDebug();Boolean enable = Optional.ofNullable(enableLogDebug).orElse(Boolean.TRUE);return enable.booleanValue();}/*** 获取一个traceId,用于问题排查使用** @param context 上下文对象* @return traceId*/protected abstract String getTraceId(Context context);/*** 执行鉴权* 需要子类实现此方法,完成具体的健全处理** @param context 上下文对象*/protected abstract void doExecute(Context context);/*** 获取鉴权配置** @return 鉴权配置*/protected abstract AuthenticateConfig getConfig();public AuthenticateActionEnum getAction() {return action;}
}

2.4.1、ActivityStatusChangeAuthenticateHandler

鉴权场景1的具体鉴权逻辑。

/*** [企业直播活动变更]回调鉴权处理器** @author : Sieg Heil* @since 2022/11/25 10:20 AM*/
@Component
@Slf4j
public class ActivityStatusChangeAuthenticateHandler extends AbstractAuthenticateHandler<ActivityStatusChangeAuthenticateContext> {/*** 构造函数*/public ActivityStatusChangeAuthenticateHandler() {super(AuthenticateActionEnum.ACTIVITY_STATUS_CHANGE);}@Overridepublic Logger getLog() {return log;}@Overrideprotected String getTraceId(ActivityStatusChangeAuthenticateContext context) {return context.getRequest().getActivityID();}@Overrideprotected AuthenticateConfig getConfig() {return volcAuthenticateApolloConfig.getActivityStatusChange();}@Overrideprotected void doExecute(ActivityStatusChangeAuthenticateContext context) {SubscribeLiveActivityStatusChangeRequest request = context.getRequest();String sign = request.getSign();String signature = getSignature(context);if (Objects.equals(sign, signature)) {context.setResponse(BaseAuthenticateContext.Response.buildSuccess(SUCCESS));} else {String traceId = getTraceId(context);if (logDebug()) {getLog().info("[{}|{}],ts={},encrypted={}", traceId, actionName, request.getTimestamp(), signature);}String debug = MessageFormat.format("activityId={0},signature={1},md5={2}", traceId, sign, signature);context.setResponse(BaseAuthenticateContext.Response.buildFailure(debug));}}/*** 获取报文加密后的密文** @param context 上下文对象* @return 密文*/private String getSignature(ActivityStatusChangeAuthenticateContext context) {SubscribeLiveActivityStatusChangeRequest request = context.getRequest();String privateKey = volcAuthenticateApolloConfig.getActivityStatusChange().getPrivateKey();StringBuilder content = new StringBuilder(privateKey);content.append(request.getActivityID()).append(request.getEventType()).append(request.getStatus()).append(request.getTimestamp());String original = content.toString();String encrypted = Md5Util.encrypt(original);return encrypted;}
}

2.4.2、VodEventNotifyAuthenticateHandler

鉴权场景2的具体鉴权逻辑。

/*** [视频点播事件通知]回调鉴权处理器** @author : Sieg Heil* @since 2022/11/25 10:20 AM*/
@Component
@Slf4j
public class VodEventNotifyAuthenticateHandler extends AbstractAuthenticateHandler<VodEventNotifyAuthenticateContext> {/*** 构造函数*/public VodEventNotifyAuthenticateHandler() {super(AuthenticateActionEnum.VOD_EVENT_NOTIFY);}@Overridepublic Logger getLog() {return log;}@Overrideprotected String getTraceId(VodEventNotifyAuthenticateContext context) {return context.getRequest().getRequest().getRequestId();}@Overrideprotected AuthenticateConfig getConfig() {return volcAuthenticateApolloConfig.getVodEventNotify();}@Overrideprotected void doExecute(VodEventNotifyAuthenticateContext context) {VolcVodRequestContext requestContext = context.getRequest();String sign = requestContext.getSignature();String original = getMd5Content(context);String signature = Md5Util.encrypt(original);if (Objects.equals(sign, signature)) {context.setResponse(BaseAuthenticateContext.Response.buildSuccess(SUCCESS));} else {String traceId = getTraceId(context);if (logDebug()) {getLog().info("[{}|{}],encrypted={}", traceId, actionName, signature);}String debug = MessageFormat.format("requestId={0},signature={1},md5={2}", traceId, sign, signature);context.setResponse(BaseAuthenticateContext.Response.buildFailure(debug));}}private String getMd5Content(VodEventNotifyAuthenticateContext context){VolcVodRequestContext requestContext = context.getRequest();String requestBody = requestContext.getRequestBody();String privateKey = volcAuthenticateApolloConfig.getVodEventNotify().getPrivateKey();String callbackUrl = volcAuthenticateApolloConfig.getVodEventNotify().getCallbackUrl();String callbackContent = encode(requestBody);StringBuilder original = new StringBuilder(callbackUrl).append("|").append(requestContext.getTimestamp()).append("|").append(privateKey).append("|").append(callbackContent);return original.toString();}private String encode(String value) {Base64.Encoder encoder = Base64.getEncoder();return encoder.encodeToString(value.getBytes(StandardCharsets.UTF_8));}
}

2.5、AuthenticateConfig

鉴权配置类

/*** 鉴权配置类** @author : Sieg Heil* @since 2022/11/25 11:47 AM*/
@ToString(callSuper = true)
@Getter
@Setter
public class AuthenticateConfig {/*** 鉴权开关*/private Boolean enableSwitch;/*** 鉴权私钥*/private String privateKey;/*** 回调url*/private String callbackUrl;/*** 鉴权策略*/private StrategyEnum strategy;/*** 鉴权策略类型*/public enum StrategyEnum {/*** 对报文进行MD5加密,防止报文被篡改*/MD5}
}

2.6、VolcAuthenticateApolloConfig

所有回调场景鉴权配置类

/*** 回调鉴权配置** @author : Sieg Heil* @since 2022/11/25 11:55 AM*/
@Component
@RefreshScope
@ConfigurationProperties(prefix = "xxx.xxx.authenticate.volc")
@ToString
@Getter
@Setter
public class VolcAuthenticateApolloConfig {/*** 是否启用日志输出,便于追踪问题*/private Boolean enableLogDebug;/*** 企业直播活动变更*/private AuthenticateConfig activityStatusChange;/*** 视频点播事件通知*/private AuthenticateConfig vodEventNotify;
}

2.7、yml配置

yml配置文件,可以通过diamond或者apollo,当前应用服务对接了apollo。

xxx:xxx:# 回调配置 true|falsecallback:# 订阅企业直播活动状态变更subscribeVolcActivityStatusChange:# 启用日志输出logDebug: true# 启用日志输出enableHandle: false# 订阅视频点播事件通知subscribeVolcVodEventNotify:# 启用日志输出logDebug: true# 启用日志输出enableHandle: false   # 鉴权配置authenticate:# 鉴权配置volc:# 是否启用日志输出,便于追踪问题 true|falseenableLogDebug: true# 企业直播活动变更activityStatusChange:# 鉴权开关enableSwitch: true# 鉴权私钥privateKey: xxxxx# 回调urlcallbackUrl: xxxx# 鉴权策略strategy: MD5# 视频点播事件通知vodEventNotify:# 鉴权开关enableSwitch: true# 鉴权私钥privateKey: xxxx# 回调urlcallbackUrl: xxxx         # 鉴权策略strategy: MD5          

2.8、业务接入

/*** Created at 2022/5/24 11:01 AM** @author : Sieg Heil*/
@ThriftService(service = "volcEngineCallback")
@Validated
@Slf4j
public class VolcEngineCallbackServiceImpl implements VolcEngineCallbackService{@Autowiredprivate VolcEngineCallbackConverter volcEngineCallbackConverter;@Autowiredprivate CallbackEnableSwitch callbackEnableSwitch;@Autowiredprivate SubscribeLiveStatusEventDispatcher subscribeLiveStatusEventDispatcher;@Autowiredprivate SubscribeVodEventDispatcher subscribeVodEventDispatcher;@Autowiredprivate AuthenticateDispatcher authenticateDispatcher;@Overridepublic void subscribeLiveActivityStatusChange(SubscribeLiveActivityStatusChangeRequest request) {ActivityStatusChangeAuthenticateContext authenticateContext = volcEngineCallbackConverter.convertToActivityStatusChangeAuthenticateContext(request);authenticateDispatcher.execute(authenticateContext);if (!callbackEnableSwitch.subscribeVolcActivityStatusChange()) {String traceId = request.getActivityID();log.info("[SubscribeLiveActivityStatusChange|{}]业务处理开关关闭|{}", traceId, callbackEnableSwitch.subscribeVolcActivityStatusChange());return;}SubscribeLiveStatusEventContext context = volcEngineCallbackConverter.convertToSubscribeLiveStatusEventContext(request);subscribeLiveStatusEventDispatcher.execute(context);}
}

原创|对接三方服务商回调鉴权的程序代码设计相关推荐

  1. java七牛获取访问路径_七牛回调及回调鉴权

    概述 客户上传文件到七牛后,七牛服务器会响应 200 状态码,响应内容包括 hash 和 key .但是如果客户需要自定义响应内容,则可以通过设置回调来实现. 回调实现 客户端需要在上传 token ...

  2. 快捷支付各种绑卡鉴权方式

    1.背景 互联网金融平台账户进行开户或者支付业务时,绑卡鉴权环节是必经之路. 那么什么是绑卡鉴权?绑卡是将用户银行卡信息提供给金融平台,以后金融平台就用这个信息去银行完成支付.绑卡实际上是一个授权,让 ...

  3. 互联网金融平台常见绑卡鉴权方式分析对比

    1.背景 互联网金融平台账户进行开户或者支付业务时,绑卡鉴权环节是必经之路. 那么什么是绑卡鉴权?绑卡是将用户银行卡信息提供给金融平台,以后金融平台就用这个信息去银行完成支付.绑卡实际上是一个授权,让 ...

  4. 云调用,小程序鉴权正确姿势

    目录: 一.无处不在的鉴权 1. 现实生活中的身份鉴权方法 2. 简单的密码鉴权体系 二.鉴权优化 1. 频繁的鉴权场景下的优化方案 2. 第三方鉴权体现下的设计--oAuth 2.0鉴权体系 三.说 ...

  5. 技术分享|明源云天际集成开放平台接口中心基于IdentityServer4的鉴权机制

    源宝导读:企业数字化生态建设中为解决集成多样性和资源统一管理的痛点引入企业级网关,网关作为资源访问的大门,身份认证鉴权是其业务的重中之重,本文将介绍企业级网关-天际集成开放平台是如何通过Identit ...

  6. 详解比springSecurity和shiro更简单优雅的轻量级Sa-Token框架,比如登录认证,权限认证,单点登录,OAuth2.0,分布式Session会话,微服务网关鉴权

    文章目录 1. 技术选型 2. Sa-Token概述 2.1 简单介绍 2.2 登录认证 2.3 权限认证 3. 功能一览 4. Sa-Token使用 4.1 引入Sa-Token依赖 4.2 Sa- ...

  7. API 鉴权插件上线!支持用户自定义鉴权插件

    0.4.0 版本更新主要围绕这几个方面: 分组独立的 UI,支持分组 API 鉴权 API 测试支持继承 API 鉴权 支持用户自定义鉴权插件,仅需部分配置即可发布鉴权插件 开始介绍功能之前,我想先和 ...

  8. 【Spring Cloud Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间这里只贴出关键部分代码的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证 ...

  9. 【Gorho】springboot整合Shiro+jwt 前后端分离 超级详细的shiro+jwt鉴权过程

    shiro+jwt+springboot 说在前面 简介 项目环境(pom.xml) 项目结构(各种包和类) 鉴权流程 具体代码 配置Shiro 配置JWTUtils 定义JwtFilter 定义Jw ...

最新文章

  1. Angular2入门教程-1
  2. mysql索引执行计划_第六章· MySQL索引管理及执行计划
  3. serial driver 2
  4. Python之异常处理-Exception
  5. 如何在Windows上解决蓝牙问题
  6. Katy Perry - E.T.
  7. file对象怎样获取文件的长度?_使用FSO对象获取整个文件夹的信息
  8. Mysql源代码分析系列(1): 编译和调试--转载
  9. 网络安全 Python 编程指南
  10. 【20180905】【计算机技术】为什么每次打印机都要手动设置双面打印?已解决~
  11. DirectX修复工具出现0xc000007b错误——分析与解决
  12. 087 定积分求面积应用习题
  13. 德马克机械波中shift over to right的意思
  14. 利用指针访问opencv Mat类型的矩阵,以及求椭圆方程的函数
  15. 如何用Matlab进行曲线拟合
  16. 易优CMS网站建设类企业网站模板 互联网营销网站模板
  17. QByteArrary、QString、QSL使用注意事项
  18. CMU15-213学习笔记(六)Exceptional Control Flow
  19. 我们用程序整理出了一份Python英语高频词汇表,拿走不谢!
  20. 如何实现同一IP的不同端口访问不同的网站

热门文章

  1. 由于使用计算机编制会计报表,《计算机会计学》1
  2. matlab如何表示冲激信号,matlab里如何表示冲激信号
  3. Windows原版安装程序中diskpart使用
  4. 金蝶K3开发-工业单据显示物料图片
  5. 小米android手机同步数据,怎样将旧手机里面的数据,丝毫不差的转移到新手机?一键教你搞定...
  6. 关于angular模态框遇到的坑 Error: [$injector:unpr] Unknown provider
  7. MATHTYPE安装出现问题:无法打开要写入的文件;MathType打开word时“安全警告,宏已被禁用”;mathtype与AXmath不能同时使用
  8. 2021-2-26编程语言知识点整理
  9. python类的魔法方法和装饰器
  10. 【JavaWeb】Servlet系列——响应HTML代码、Servlet连接数据库、IDEA开发Servlet程序、Servlet对象的生命周期、GenericServelet适配器模式