spring boot实现超轻量级网关(反向代理转发)

Posted shenjun980326

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring boot实现超轻量级网关(反向代理转发)相关的知识,希望对你有一定的参考价值。

spring boot实现超轻量级网关(反向代理、转发)

原文连接 https://www.cnblogs.com/xiaoqi/p/spring-boot-route.html

在我们的rest服务中,需要暴露一个中间件的接口给用户,但是需要经过rest服务的认证,这是典型的网关使用场景。可以引入网关组件来搞定,但是引入zuul等中间件会增加系统复杂性,这里实现一个超轻量级的网关,只实现请求转发,认证等由rest服务的spring security来搞定。

如何进行请求转发呢? 熟悉网络请求的同学应该很清楚,请求无非就是请求方式、HTTP header,以及请求body,我们将这些信息取出来,透传给转发的url即可。

举例:

/router/** 转发到 businessServer/**

获取转发目的地址:

    private String createRedirectUrl(HttpServletRequest request, String routeUrl, String prefix) 
        String queryString = request.getQueryString();
        return routeUrl + request.getRequestURI().replace(prefix, "") +
                (queryString != null ? "?" + queryString : "");
    

解析请求头和内容

然后从request中提取出header、body等内容,构造一个RequestEntity,后续可以用RestTemplate来请求。

private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException 
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(method);
        MultiValueMap<String, String> headers = parseRequestHeader(request);
        byte[] body = parseRequestBody(request);
        return new RequestEntity<>(body, headers, httpMethod, new URI(url));
    


    private byte[] parseRequestBody(HttpServletRequest request) throws IOException 
        InputStream inputStream = request.getInputStream();
        return StreamUtils.copyToByteArray(inputStream);
    

    private MultiValueMap<String, String> parseRequestHeader(HttpServletRequest request) 
        HttpHeaders headers = new HttpHeaders();
        List<String> headerNames = Collections.list(request.getHeaderNames());
        for (String headerName : headerNames) 
            List<String> headerValues = Collections.list(request.getHeaders(headerName));
            for (String headerValue : headerValues) 
                headers.add(headerName, headerValue);
            
        
        return headers;
    

透明转发

最后用RestTemplate来实现请求:

 private ResponseEntity<String> route(RequestEntity requestEntity) 
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.exchange(requestEntity, String.class);
    

全部代码

以下是轻量级转发全部代码:

import com.jyw.router.miniapp.service.IRouterService;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
/**
 * @author 黑子桑
 * @ClassName IRouterServiceImpl
 * @Description TODO
 * @Date 2023/4/20 13:32
 **/
@Service
public class RouterServiceImpl implements IRouterService 

    @Override
    public ResponseEntity<String> redirect(HttpServletRequest request, HttpServletResponse response,String routeUrl, String prefix) 
        try 
            // build up the redirect URL
            String redirectUrl = createRedirectUrl(request,routeUrl, prefix);
            RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
            return route(requestEntity);
         catch (Exception e) 
            return new ResponseEntity("REDIRECT ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
        
    

    private String createRedirectUrl(HttpServletRequest request, String routeUrl, String prefix) 
        String queryString = request.getQueryString();
        return routeUrl + request.getRequestURI().replace(prefix, "") +
                (queryString != null ? "?" + queryString : "");
    


    private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException 
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(method);
        MultiValueMap<String, String> headers = parseRequestHeader(request);
        byte[] body = parseRequestBody(request);
        return new RequestEntity<>(body, headers, httpMethod, new URI(url));
    

    private ResponseEntity<String> route(RequestEntity requestEntity) 
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.exchange(requestEntity, String.class);
    


    private byte[] parseRequestBody(HttpServletRequest request) throws IOException 
        InputStream inputStream = request.getInputStream();
        return StreamUtils.copyToByteArray(inputStream);
    

    private MultiValueMap<String, String> parseRequestHeader(HttpServletRequest request) 
        HttpHeaders headers = new HttpHeaders();
        List<String> headerNames = Collections.list(request.getHeaderNames());
        for (String headerName : headerNames) 
            List<String> headerValues = Collections.list(request.getHeaders(headerName));
            for (String headerValue : headerValues) 
                headers.add(headerName, headerValue);
            
        
        return headers;
    

在反向代理后面使用 OAuth2 的 Spring Boot

【中文标题】在反向代理后面使用 OAuth2 的 Spring Boot【英文标题】:Spring Boot with OAuth2 behind reverse proxy 【发布时间】:2019-05-01 09:23:38 【问题描述】:

我是 Spring Security 的新手,并尝试使用在主机名:8080 下运行的 OAuth2 开发带有 Google 登录的 Spring Boot 应用程序。此应用位于 Apache 反向代理服务器 https://url.com 后面。

Spring Boot 2.1.0 版

Spring Security 5.1.1 版

build.gradle:

dependencies 
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.security:spring-security-oauth2-client")
    implementation("org.springframework.security:spring-security-oauth2-jose")

application.yml:

oauth2:
  client:
    registration:
      google:
        clientId: <clientId>
        clientSecret: <clientSecret> 
        scope: profile, email, openid

server:
  use-forward-headers: true
  servlet:
    session:
      cookie:
        http-only: false

Spring 安全配置:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
            .csrf()
                .disable()
            .authorizeRequests()
                .anyRequest()
                    .authenticated()
            .and()
            .oauth2Login();
    
  
    我请求https://url.com 被重定向到https://accounts.google.com/signin/oauth/ 经过身份验证后被重定向回 https://url.com/login/oauth2/code/google?state=state&code=code&scope=openid+email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.me&authuser=0&session_state=session_state&prompt=none 超时错误:

[invalid_token_response] 尝试 检索 OAuth 2.0 访问令牌响应:POST 上的 I/O 错误 请求“https://www.googleapis.com/oauth2/v4/token”:连接 超时(连接超时);嵌套异常是 java.net.ConnectException:连接超时(连接超时)

此错误是由代理服务器设置或启动应用程序引起的吗?感谢您的帮助。

【问题讨论】:

显示你的反向代理配置。 【参考方案1】:

解决了。我必须设置 JVM 参数:

https.proxyHost=[host]
https.proxyPort=[port] 

http.proxyHost=[host]
http.proxyPort=[port]

【讨论】:

您的应用程序是否在主机上运行,​​主机有一些防火墙规则来禁止直接访问 Internet?为什么设置 JVM 代理参数有帮助?

以上是关于spring boot实现超轻量级网关(反向代理转发)的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot - Apache 反向代理背后的 Spring 安全性

NGINX 反向代理背后的 Spring Boot API REST

在反向代理后面使用 OAuth2 的 Spring Boot

非默认端口上的反向代理背后的 Spring Boot Cors

微软的反向代理库YARP

微软的反向代理库YARP