smiley-http-proxy-servlet 实现springboot 反向代理,项目鉴权,安全的引入第三方项目服务

背景:

项目初期 和硬件集成,实现了些功能服务,由于是局域网环境,安全问题当时都可以最小化无视。随着对接的服务越来越多,部分功能上云,此时就需要有一种手段可以控制到其他项目/接口的访问权限。 无疑 反向代理是最轻快的解决办法。

反向代理 集成第三方的服务接口或web监控界面,并与项目实现的鉴权方法

依赖 smiley-http-proxy-servlet GitHub链接

java 复制代码
  2.0 版开始,代理切换到jakarta servlet-api
  <!--HTTP 代理 Servlet-->
   <dependency>
       <groupId>org.mitre.dsmiley.httpproxy</groupId>
       <artifactId>smiley-http-proxy-servlet</artifactId>
       <version>2.0</version>
	</dependency>

javax servlet-api 请选择

java 复制代码
<dependency>
    <groupId>org.mitre.dsmiley.httpproxy</groupId>
    <artifactId>smiley-http-proxy-servlet</artifactId>
    <version>${smiley-http-proxy-servlet.version}</version>
    <classifier>javax</classifier>
</dependency>

仅仅是代理接口 默认官网示例使用即可,参考第二个接口代理。

以Nginx 代理Grafana监控平台为例,解决静态资源加载失败、访问鉴权、及websocket连接问题

java 复制代码
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;

/**
 * 本地代理服务
 *
 * @author Smile
 */
@Configuration
public class ProxyServletConfig {


	/**
	* 代理Grafana 监控平台
	*/	
    @Bean
    public ServletRegistrationBean<ProxyServlet> servletRegistrationBean() {
        ServletRegistrationBean<ProxyServlet> servletRegistrationBean = new ServletRegistrationBean<>(new ProxyServlet(), "/grafana/*");
        servletRegistrationBean.addInitParameter(ProxyServlet.P_TARGET_URI, "http://127.0.0.1:9999");
        servletRegistrationBean.addInitParameter(ProxyServlet.P_LOG, "true");
//       自动处理重定向
        servletRegistrationBean.addInitParameter(ProxyServlet.P_HANDLEREDIRECTS, "false");
//      保持 COOKIES 不变
        servletRegistrationBean.addInitParameter(ProxyServlet.P_PRESERVECOOKIES, "true");
//       Set-Cookie 服务器响应标头中保持 cookie 路径不变
        servletRegistrationBean.addInitParameter(ProxyServlet.P_PRESERVECOOKIEPATH, "true");
//        保持 HOST 参数不变
        servletRegistrationBean.addInitParameter(ProxyServlet.P_PRESERVEHOST, "true");
        return servletRegistrationBean;
    }

	/**
	*接口代理
	*/
    @Bean
    public ServletRegistrationBean<ProxyServlet> servletRegistration() {
        ServletRegistrationBean<ProxyServlet> servletRegistrationBean = new ServletRegistrationBean<>(new ProxyServlet(), "/one/*","/two/*","three/*");
        servletRegistrationBean.addInitParameter(ProxyServlet.P_TARGET_URI, "http://localhost:8001/api");
        servletRegistrationBean.addInitParameter(ProxyServlet.P_LOG, "true");
        return servletRegistrationBean;
    }


    /**
     * 禁用springboot 自带的 HiddenHttpMethodFilter 防止post提交的form数据流被提前消费
     * <p>
     * fix springboot中使用proxyservlet的 bug.
     * <a href="https://github.com/mitre/HTTP-Proxy-Servlet/issues/83">bugs</a>
     * <a href="https://stackoverflow.com/questions/8522568/why-is-httpservletrequest-inputstream-empty">bugs</a>
     *
     * @return 
     */
    @Bean
    public FilterRegistrationBean<HiddenHttpMethodFilter> disableHiddenHttpMethodFilter() {
        FilterRegistrationBean<HiddenHttpMethodFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new HiddenHttpMethodFilter());
        registrationBean.setEnabled(false); // 禁用过滤器
        return registrationBean;
    }
}

直接访问grafana代理 springboot项目端口8088报错,静态资源 路径不正确 加载失败,grafana live/ws链接也会失败,参考附录。

解决办法:

1.修改对应前端项目,使其 都通过代理路径。

2.可以新建项目,ProxyServlet使用 /*路径匹配规则。

3.如果这两种都不适合,那么变动最少的方案,使用nginx反向代理到ProxyServlet的代理路径上。这样做会增加一些性能损耗,但是换来了,前端项目无需更改,后端可增加接口鉴权,某些时候是值得的。

nginx配置参考:

javascript 复制代码
 server {
            listen       8889;
            server_name  localhost;
			
			# grafana websocket地址代理
            location /api/live/ws {
                 proxy_http_version 1.1;
                 proxy_set_header Upgrade $http_upgrade;
                 proxy_set_header Connection "Upgrade";
                 proxy_set_header X-real-ip $remote_addr;
                 proxy_set_header X-Forwarded-For $remote_addr;
                 proxy_pass http://127.0.0.1:9999;
            }

            location / {
                #add_header Access-Control-Allow-Origin *;
                add_header Access-Control-Max-Age 1728000;
                add_header Access-Control-Allow-Methods 'POST,GET,OPTIONS,DELETE,PUT,HEAD,PATCH';
                add_header Access-Control-Allow-Headers 'satoken,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
                add_header Access-Control-Allow-Origin $http_origin;
                client_max_body_size 10m;

                if ($request_method = 'OPTIONS') {
                    return 204;
                }
			  	# grafana支持配置apikey 免登录访问
                set $auth 'Bearer eyJrIjoiN1pKYlk5akFDZWNoMlVSUEN1YllXdm0yd2VYN2RzZFIiLCJuIjoiYWRtaW5rZXkiLCJpZCI6MX0=';
               # apiKey设置到header grafana免密访问
                proxy_set_header Authorization $auth;

                proxy_pass http://127.0.0.1:8088/grafana//;

                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_connect_timeout 600;
                proxy_read_timeout 600;
            }


      }

划重点

springboot 代理的/grafana/* 到 http://127.0.0.1:9999

静态资源的访问失败 404或 错误的返回html首页,是因为路径不符合此规则导致代理是失败

proxy_pass http://127.0.0.1:8088/grafana//;

由nginx代理到 // 则问题解决 ,使 /grafana/* 代理规则生效

其他访问的服务调用 nginx的这个代理

ok.

再看后端 日志 代理已正常

更安全的访问

只需要限制 原服务端口的放行规则,如仅本机可访问,然后项目增加过滤器自行判断权限。

启动类添加@ServletComponentScan扫描WebFilter,增加 Filter

java 复制代码
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;

import java.io.IOException;
/**
 * 过滤器
 *
 * @author Smile
 */
@Order(1)
@WebFilter(filterName = "piceaFilter", urlPatterns = "/grafana/*")
public class ProxyServletFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    	//自行 实现条件判断即可
        if (StpUtil.isLogin()) {
            // 用户已登录,继续执行过滤器链
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            // 用户未登录,可以返回错误信息或重定向到登录页面
            // 例如,返回 HTTP 401 未授权状态
            HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        }
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

未登录则:

``收工~~

附:

grafana websocket 可能需要修改custom.ini配置

java 复制代码
# allowed_origins is a comma-separated list of origins that can establish connection with Grafana Live.
# If not set then origin will be matched over root_url. Supports wildcard symbol "*".

allowed_origins=*
或
allowed_origins = http://127.0.0.1:8889
相关推荐
十二7401 天前
前端缓存踩坑实录:从版本号管理到自动化构建
前端·javascript·nginx
用户908324602731 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
可观测性用观测云2 天前
云原生网关 Ingress-Nginx 链路追踪实战:OpenTelemetry 采集与观测云集成方案
nginx·kubernetes
用户8307196840822 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解2 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解2 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记2 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者3 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840823 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解3 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端