[杂货铺系列]ProxyServlet权限托管
猿Why上个月接到一个需求,大致要求:在一个运维管理系统(System S)中集成另一个已经成熟的日志管理系统(System T)(没有权限管理)。 拿到需求,首先想到:通过代理方式做资源(API、静态资源)访问、权限控制。在一个微服务(分布式)的环境下,网关和外层的Nginx(反向代理)其实都可以实现这个需求。 现实情况,我们没有网关服务,Nginx做用户信息粘合不好(团队中没有人擅长)。所以,经过我的考虑,直接在S系统中添加个代理,所有T系统的访问,都经S一手,这样在S系统中就可以做粒度很细的权限控制。 查询一番资料后就会发现,自己能想到的,前辈们早就干过了(感叹Java生态环境之成熟)。于是,就要介绍这个方案的核心了:ProxyServlet
ProxyServlet API
来看看ProxyServlet的说明。嗯!很符合需求,而且它就是一个Servlet,很适合作轻量级的代理。
ProxyServlet使用
核心依赖
定义配置数据结构
public class ProxyInfo { // 服务/功能名称 String name; // 暴露给外部的请求 String servlet_url; // 实际目标服务 String target_url; }
外部化配置
启动加载代理配置类
package com.young.proxytool.config; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.young.proxytool.proxy.ProxyInfo; import com.young.proxytool.proxy.ProxyServletExtend; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.FileUrlResource; import org.springframework.core.io.UrlResource; import javax.servlet.ServletContext; import javax.servlet.ServletRegistration; import java.io.IOException; import java.io.InputStream; import java.util.LinkedHashMap; import java.util.Map; /** * Proxy注册 * * @author :<a href="mailto:youngkun2016@163.com">young</a> * @date :Created in 2020/12/19 */ @Configuration public class RegistryProxyServletConfig implements ServletContextInitializer { /** * 外部配置文件路径 */ @Value("${proxy.data}") private String proxyFilePath; private ServletContext sc; private UrlResource urlResource; @Override public void onStartup(ServletContext servletContext) { try { this.sc = servletContext; this.urlResourceInit(); InputStream config = this.urlResource.getInputStream(); JSONArray jsonArray = JSON.parseObject(config, JSONArray.class); for (Object object : jsonArray) { ProxyInfo proxyInfo = JSON.toJavaObject(JSONObject.class.cast(object), ProxyInfo.class); this.registerProxy(proxyInfo); } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } private void urlResourceInit() throws Exception { FileUrlResource fileUrlResource = new FileUrlResource(proxyFilePath); if (fileUrlResource.isReadable()) { this.urlResource = fileUrlResource; } else { throw new Exception("无法读取代理配置文件:" + proxyFilePath); } } protected void registerProxy(ProxyInfo proxyInfo) { ServletRegistration registrationBean = this.sc.addServlet(proxyInfo.getName(), ProxyServletExtend.class); Map<String, String> initParameters = new LinkedHashMap<>(); initParameters.put(ProxyServletExtend.P_LOG, "true"); initParameters.put(ProxyServletExtend.P_TARGET_URI, proxyInfo.getTarget_url()); registrationBean.setInitParameters(initParameters); registrationBean.addMapping(proxyInfo.getServlet_url()); } }
权限托管
-
限制直接访问T的请求,不对外暴露 S通过ProxyServlet做S系统与T系统的映射关系 S系统Filter做权限控制