基于Servlet体系的HTTP请求代理转发Spring Boot组件

两个项目组原本都是各自负责两个产品线(产品A产品B),由于公司业务的发展,目前需要将两个产品合并成一个大产品(功能整合,部分做取舍,最终产出产品C),前后端代码必然也需要整合,包括两个产品线的用户体系等。并且给出的时间节点很紧张

目前两个产品线的区别点:

产品A

前端模块载体是微信小程序,没有H5、APP等需求,因此所采用的技术栈是原生写法,没有用到技术框架

服务端技术架构是单体架构,Spring Boot框架,管理后台框架采用的是Apache Shiro

前后端接口调用采用的是服务端token鉴权的方式交互

用户体系简单,小程序端没有会员等业务,仅涉及到微信openid,管理后台涉及权限菜单.

后端管理系统前端开发技术框架是React

产品B

前端模块载体多样,包括微信小程序、H5、APP等,因此采用的是多端统一框架,例如:union-app

服务端技术架构单体架构,Spring Boot框架

前后端接口调用采用的是服务端token鉴权的方式交互

用户体系复杂,有会员、优惠券等业务,管理后台涉及权限菜单

后端管理系统前端开发技术框架是Vue

产品C

载体是微信小程序,没有H5、APP等需求

产品A中的功能居多,产品B中的功能占用少部分

鉴于上面的背景,我们讨论接下来产品线合并的可能性

前端代码重写,虽说是产品线合并,但是原来两个产品线的功能点只是做整合,并没有太多新增的功能,因此原来的部分功能模块可以复用,采用原生写法,不用多端框架

后端用户体系复用产品B中的体系,基本控制菜单权限即可

考虑到时间紧迫,因此原本产品A\B两个产品线的已有的功能基本不动,只对新增模块的功能进行开发。

产品B的后端系统功能菜单、权限系统较A完善,因此作为产品C的管理后端进行复用,将产品A的后端功能全部移动到产品C中,由于两个产品线管理后台开发的技术栈不一样,因此产品C中的部分功能需要重写,将产品A的功能使用Vue的技术栈移到产品C中

游客端(小程序端)

针对产品C的小程序端,由于需要包含产品A中的某一核心功能,因此不太可能使用多端框架进行重写(PS:主要是领导给的时间不够),因此采用的做法是直接在产品A的基础上衍生一个版本,最终将产品B中的部分功能,通过原生框架,最终在产品C中进行呈现。

因为小程序的接口调用方式是直连,通过发起HTTPS的接口请求即可,因此服务端接口逻辑不动,前端开发人员只需要和产品B的人员进行接口对接即可,最终接口调用流程示意图如下:

基于Servlet体系的HTTP请求代理转发Spring Boot组件

管理端(PC端)

管理端则不同,由于是使用的产品B中的后台,因此产品A中的权限控制需要去除(例如登录后才能调用接口等限制),而产品A中的接口权限控制需要交给B来管,发送请求时需要校验当前请求的权限,校验通过后再转发给A,调用时序图如下:

基于Servlet体系的HTTP请求代理转发Spring Boot组件

上面这张图也是这个组件雏形,寄希望与通过该转发组件,通过提供不同的转发方式,封装转发HTTP请求的能力,达到直连服务的目的

如果单纯从一个新产品C的角度出发,ServiceA中的服务接口代码应该合并到ServiceB,最终形成一个新的ServiceC,但是考虑到时间紧迫,所以代码层面的合并并没有形成,因此考虑直接将请求HTTP转发的方式,最终将任务完成。

程序设计

从需求背景出发,在程序设计上需要考虑的几个点:

上游服务接收到的固定请求头,或者请求参数,比如多租户系统需要接收一个租户的请求header,因此转发组件需要有配置固定header的能力,以便在实际转发过程中发送到下游服务,方便系统扩展

需要提供权限验证的接口,不同的权限框架可能验证方式不同,有些系统是Shiro,或者Spring Security,或者自研,因此在最终权限校验时,考虑到和系统的兼容性,对于下游的转发服务接口,需要提供和系统兼容的验证接口,不可打破原系统的稳定性

转发的方式支持类别,考虑到系统的健壮性,需要提供不同的转发类别支撑

由于是基于Servlet体系,因此对于接口的请求,需要做一层拦截判断,以验证当前的请求是否是需要转发到下游服务,核心过滤器如下:

public class ServletGatewayRouteProxyFilter implements Filter { //执行器对象 private final RouteDispatcher routeDispatcher; //权限对象 private final ServletGatewayAuthentication servletGatewayAuthentication; Logger logger= LoggerFactory.getLogger(ServletGatewayRouteProxyFilter.class); /** * 狗仔ProxyHttpFilter 对象实例 * @param routeDispatcher 执行器对象 * @param servletGatewayAuthentication 权限校验对象 */ public ServletGatewayRouteProxyFilter(RouteDispatcher routeDispatcher, ServletGatewayAuthentication servletGatewayAuthentication) { this.routeDispatcher = routeDispatcher; this.servletGatewayAuthentication = servletGatewayAuthentication; } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request= (HttpServletRequest) servletRequest; HttpServletResponse response=(HttpServletResponse) servletResponse; //根据程序配置方式,截取当前请求是否符合转发请求 Optional<ServiceRoute> serviceRouteOptional=routeDispatcher.assertServletRequest(request); if (serviceRouteOptional.isPresent()){ logger.info("转发目标服务,地址:{}",request.getRequestURI()); if (servletGatewayAuthentication.required()){ if (servletGatewayAuthentication.auth(servletRequest,servletResponse)){ routeDispatcher.execute(request,response,serviceRouteOptional.get()); }else{ servletGatewayAuthentication.failedHandle(servletRequest,servletResponse); } }else{ routeDispatcher.execute(request,response,serviceRouteOptional.get()); } }else{ //不符合,继续执行 filterChain.doFilter(servletRequest,servletResponse); } } //other code... }

对于当前的HttpServletRequest信息做判断,获取当前请求的ServiceRoute对象,以此来判断请求是否需要转发

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxyps.html