【请求代理】springboot单机服务基于过滤器Filter实现第三方服务器接口请求代理功能

springboot单机服务基于过滤器Filter实现第三方服务器接口请求代理功能

**注:本文源码获取或者更多资料,关注公众号:技术闲人**

一、前言

在项目开发时会遇到web端/接口请求第三方服务接口的场景,对web请求来说最后请求的服务地址是一个,避免跨域问题,在微服务场景经常使用gateway等网关服务实现,或者使用nginx代理组件实现,但是不同三方服务有不同的鉴权要求,但是后端服务最好有相同的鉴权;
在这里插入图片描述

二、解决思路

在非微服务架构和三方不同鉴权接口的服务场景下,通过过滤器Filter实现请求转发,并使用适配器设计模式,兼容不同的三方服务请求(鉴权),减少重复代码开发,也能监控所有的服务请求,并对所有请求做限流、统计等操作;

三、基于gateway实现

在没有spring-boot-starter-web依赖的场景下可以使用gateway,Spring MVC(基于Servlet的Web应用程序)和Spring Cloud Gateway(基于反应式编程的API网关),但是这两个组件是不兼容的。Spring Cloud Gateway是专为反应式编程设计的,使用Spring WebFlux作为底层框架,而Spring MVC则基于Servlet API。

gateway实现代码:

package com.sk.proxytest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class ProxyTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProxyTestApplication.class, args);
    }

    @Bean
    public RouteLocator myRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(p -> p
                        .path("/test/**")
                        .uri("http://127.0.0.1:8089/api")
                )
                .build();
    }

}

四、基于过滤器Filter实现

本文主要使用过滤器Filter实现,既能控制代理请求,又能最少开发量;

GET请求结果
在这里插入图片描述
POST请求结果
在这里插入图片描述
实现源码:

ProxyFilter.java

package com.sk.proxytest.proxy;

import com.sk.proxytest.proxy.bean.ProxyParam;
import com.sk.proxytest.proxy.bean.ProxyResult;
import com.sk.proxytest.proxy.strategy.ProxyHandleService;
import com.sk.proxytest.proxy.strategy.ProxyHandleStrategyFactory;
import com.sk.proxytest.proxy.strategy.ProxyStrategyContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

@Slf4j
@Configuration
@WebFilter(filterName = "ProxyFilter", urlPatterns = "/proxy/*")
public class ProxyFilter implements Filter {

    @Resource
    private RestTemplate restTemplate;

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

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String proxyType = request.getHeader("proxy-type");

        ProxyStrategyContext proxyStrategyContext = new ProxyStrategyContext();
        ProxyHandleService proxyHandleService = ProxyHandleStrategyFactory.getProxyHandleStrategy(proxyType);
        proxyStrategyContext.setProxyHandleStrategy(proxyHandleService);
        ProxyResult proxyResult = proxyStrategyContext.handleProxy(new ProxyParam());
        boolean flag = true;
        if (null != proxyResult) {
            PrintWriter writer = null;
            try {
                String body = IOUtils.toString(request.getInputStream());
                HttpEntity<?> entity = new HttpEntity<>(body, proxyResult.getHeaders());
                String url = proxyResult.getProxyUrl() + getNewUrl(request);
                log.info("-----------new-url:{}", url);
                ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.resolve(request.getMethod()), entity, String.class);
                response.setStatus(responseEntity.getStatusCodeValue());
                writer = response.getWriter();
                writer.write(responseEntity.getBody());
                writer.flush();
                flag = false;
            } catch (Exception e) {
                log.error("------error:{}", e);
            } finally {
                if (writer != null) {
                    writer.close();
                }
            }
        }
        if (flag) {
            chain.doFilter(request, response);
        }
    }

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

    //获取被代理的url和参数
    private String getNewUrl(HttpServletRequest request) {
        String proxyUrl = request.getRequestURI().replace("/proxy", "");
        Map<String, String[]> parameterMap = request.getParameterMap();
        int i = 0;
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue()[0];
            if (i == 0) {
                proxyUrl = proxyUrl + "?" + key + "=" + value;
            } else {
                proxyUrl = proxyUrl + "&" + key + "=" + value;
            }
        }
        return proxyUrl;
    }
}

ProxyHandleService.java

package com.sk.proxytest.proxy.strategy;

import com.sk.proxytest.proxy.bean.ProxyParam;
import com.sk.proxytest.proxy.bean.ProxyResult;

public interface ProxyHandleService {

    ProxyResult proxyHandle(ProxyParam proxyParam);

}

AlibabaProxyHandleStrategy.java

package com.sk.proxytest.proxy.strategy;

import com.sk.proxytest.proxy.bean.ProxyParam;
import com.sk.proxytest.proxy.bean.ProxyResult;
import org.springframework.http.HttpHeaders;

public class AlibabaProxyHandleStrategy implements ProxyHandleService {

    @Override
    public ProxyResult proxyHandle(ProxyParam proxyParam) {
        HttpHeaders headers = new HttpHeaders();
        //TODO 根据三方服务登录接口获取鉴权信息
        String token = "token--------";
        headers.add("token", token);
        headers.add("Content-Type","application/json");

        //三方服务ip和port
        String ip = "127.0.0.1";
        String port = "8080";
        String proxyUrl = "http://" + ip + ":" + port;
        return new ProxyResult(headers, proxyUrl);
    }
}

ProxyHandleStrategyFactory.java

package com.sk.proxytest.proxy.strategy;


import java.util.HashMap;
import java.util.Map;

public class ProxyHandleStrategyFactory {

    private static Map<String, ProxyHandleService> proxyHandleServiceMap;

    static {
        proxyHandleServiceMap = new HashMap<>();
        proxyHandleServiceMap.put("alibaba", new AlibabaProxyHandleStrategy());
    }

    public static ProxyHandleService getProxyHandleStrategy(String proxyType){
        return proxyHandleServiceMap.get(proxyType);
    }

}

ProxyStrategyContext.java

package com.sk.proxytest.proxy.strategy;

import com.sk.proxytest.proxy.bean.ProxyParam;
import com.sk.proxytest.proxy.bean.ProxyResult;

public class ProxyStrategyContext {

    private ProxyHandleService proxyHandleService;

    public void setProxyHandleStrategy(ProxyHandleService proxyHandleService){
        this.proxyHandleService = proxyHandleService;
    }

    public ProxyResult handleProxy(ProxyParam proxyParam){
        if(proxyHandleService != null){
            return proxyHandleService.proxyHandle(proxyParam);
        }
        return null;
    }

}

五、问题总结

在单机服务中,gateway过于重,并且与springMVC有冲突,nginx代理服务不能同一鉴权,或者同一鉴权太过于麻烦,过滤器Filter+适配器模式正好满足我们业务场景需求;
功能实现的方式选择还是要考虑业务场景。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dylan~~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值